在关于对电子邮件本身进行签名或加密的众多讨论中,没有真正讨论过对电子邮件进行签名和加密的痛苦。
*** 您首先做什么?先签名后加密?还是先加密后签名?
根据 RFC 2311,您可以先加密后签名,也可以先签名后加密。但是,这取决于您为其编程的客户端。根据我的经验,在 Outlook 2000 中,它更喜欢先加密后签名。而在 Outlook 2003 中,则是先签名后加密。通常,您想要先签名后加密,因为从普通邮件的角度来看,这似乎更合乎逻辑。您先签署一封信,然后将其放入信封中。某些客户端如果您以它不喜欢的顺序进行操作,则会抱怨,因此您可能需要对此进行尝试。
*** 对签名和加密进行操作的示例。
当您执行第一个函数时,请不要在 headers 数组参数中放入任何标头,您希望将其放入要执行的第二个函数中。如果您在第一个函数中放入了标头,则第二个函数将将其从邮件服务器中隐藏。您不希望这样。在这里,我将先签名后加密。
<?
// 设置邮件标头。
$headers = array("To" => "[email protected]",
"From" => "[email protected]",
"Subject" => "一条已签名和加密的消息。");
// 首先签名消息
openssl_pkcs7_sign("msg.txt","signed.txt",
"signing_cert.pem",array("private_key.pem",
"password"),array());
// 获取公钥证书。
$pubkey = file_get_contents("cert.pem");
// 加密消息,现在放入标头。
openssl_pkcs7_encrypt("signed.txt", "enc.txt",
$pubkey,$headers,0,1);
$data = file_get_contents("enc.txt");
// 将标头和正文分开,以便与邮件函数一起使用
// 不幸的是,但这是必需的,否则我们将有两组标头
// 并且电子邮件客户端无法解码附件
$parts = explode("\n\n", $data, 2);
// 发送邮件(Headers 参数中的标头将覆盖
// 为 To 和 Subject 参数生成的标头)
mail($mail, $subject, $parts[1], $parts[0]);
?>
请注意,如果您使用一个函数从磁盘中获取数据以便在您程序中的另一个函数中使用,请记住,您可能使用了 explode("\n\n",$data,2) 函数,该函数可能删除了标头和消息内容之间的空格。
当您将签名后的消息输入加密部分时,您必须记住,行距也必须作为消息正文的一部分输入!如果您计划先签名再加密,请不要将签名输出的标头作为标头数组参数的一部分输入加密程序!签名的输出应作为要加密的消息正文的一部分保留。(如果您正在执行加密再签名的反向操作,也是如此。)签署和加密功能的示例被整合到一个可重复使用的例程中,然后被调用以签名和加密消息。
*** 签名和加密的示例,通过例程函数执行,以实现代码的可重复使用性。
这是错误的!
<?
// 数组的 [0] 包含消息标头。数组的 [1] 包含消息的签名正文。
$signedOutputArray = signMessage($inputMessage,$headers);
// 数组的 [0] 包含消息的标头和签名。
// 数组的 [1] 包含加密的消息正文,不包含签名标头。
$signedAndEncryptedArray = encryptMessage($signedOutputArray[1],
$signedOutputArray[0]);
mail($emailAddr,$subject,$signedAndEncryptedArray[1],
$signedAndEncryptedArray[0]);
?>
这是正确的!
<?
// 数组的 [0] 包含签名的标头。
// 数组的 [1] 包含消息的签名正文。
$signedOutputArray = signMessage($inputMessage,array());
// 数组的 [0] 包含消息的标头。
// 数组的 [1] 包含已签名消息及其签名标头的加密内容。
$signedAndEncryptedArray =
encryptMessage($signedOutputArray[0] . "\n\n" . $signedOutputArray[1],$headers);
mail($emailAddr,$subject,$signedAndEncryptedArray[1],
$signedAndEncryptedArray[0]);
?>