如何使用 OPENSSL 签署电子邮件并使用 CURL 发送它?

问题描述

我想知道如何使用电子邮件数字证书对其进行签名,然后使用 CURL 发送此签名电子邮件

以下是我想签名的简单文本/纯文本电子邮件的示例。

Content-Type: text/plain; charset="utf-8"
Content-transfer-encoding: 7bit
MIME-Version: 1.0 (Created with SublimeText 3)
Date: Fri,01 Jan 2021 23:59:59 -0700
Message-ID: <[email protected]>
Subject: TPS Report #1001
From: "Michael @ Initech" <[email protected]>
To: "Peter @ Initech" <[email protected]>
User-Agent: CURL/7.75.0

Good morning.

This is a message in plain text format.

It contains no inline images or attachments.

Each line is no more than 76 characters in length.

Please reach out if you have any questions.


Thank you,Michael Bolton
SENIOR DEVELOPER
[email protected]
P | 801.555.1234

我将整个消息放在一个名为 singlepart--text-plain.eml文件中,然后我使用 CURL 发送它。

curl --verbose -ssl smtps://secure.email.com:465 --login-options AUTH=PLAIN --user [email protected]:Letmein --mail-from [email protected] --mail-rcpt [email protected] --mail-rcpt-allowfails --upload-file singlepart--text-plain.eml

呸。这就是全部。

现在以下链接描述了如何使用 OPENSSL 对 mime 消息进行签名。

https://www.misterpki.com/openssl-smime/

openssl smime -sign -in singlepart--text-plain.eml -text -out signed-singlepart--text-plain.eml -signer michael-digital-signature.pem

RFC 8551 第 3.5、3.5.1 和 3.5.2 节展示了有关如何构建消息的有限示例,我的问题是如何正确执行此操作。

https://tools.ietf.org/html/rfc8551#section-3.5

我不知道的是,我在签署什么? 全部内容? 只是消息的文字正文? 这应该是包含未更改的文本正文和作为 base64 附件的数字签名的多部分消息吗?

或者它是否应该像 text/plain 一样在单个部分中包含修改/签名的文本?

如您所知,RFC 文档不适合胆小的人。

解决方法

仅对消息正文进行签名。这是标题之后的空行之后的所有内容。

不能包含邮件头,因为邮件服务器和邮件客户端可以在发送给收件人的途中更改(修改现有邮件或添加新邮件)。

但是,有一些方法可以通过将一些重要的标头(如 ToSubject)复制到正文中来包含它们,但它们还没有得到广泛支持(还没有?)。

openssl smime 知道如何正确处理 S/MIME 消息,因此它的输入是完整的消息(在您的示例中为 singlepart--text-plain.eml)。


这是我从页面 http://openssl.cs.utah.edu/docs/apps/smime.html 复制的大量有用的 openssl smime 示例,令我遗憾的是,这些示例似乎已经消失了:

创建明文签名消息:

openssl smime -sign -in message.txt -text -out mail.msg \
    -signer mycert.pem

创建一个不透明的签名消息:

openssl smime -sign -in message.txt -text -out mail.msg -nodetach \
    -signer mycert.pem

创建一个签名消息,包含一些额外的证书并从另一个文件中读取私钥:

openssl smime -sign -in in.txt -text -out mail.msg \
    -signer mycert.pem -inkey mykey.pem -certfile mycerts.pem

创建具有两个签名者的签名消息:

openssl smime -sign -in message.txt -text -out mail.msg \
    -signer mycert.pem -signer othercert.pem

在Unix下直接向sendmail发送签名邮件,包括标题:

openssl smime -sign -in in.txt -text -signer mycert.pem \
    -from [email protected] -to someone@somewhere \
    -subject "Signed message" | sendmail someone@somewhere

验证消息并在成功时提取签名者的证书:

openssl smime -verify -in mail.msg -signer user.pem -out signedtext.txt

使用三重 DES 发送加密邮件:

openssl smime -encrypt -in in.txt -from [email protected] \
    -to someone@somewhere -subject "Encrypted message" \
    -des3 user.pem -out mail.msg

对邮件进行签名和加密:

openssl smime -sign -in ml.txt -signer my.pem -text \
    | openssl smime -encrypt -out mail.msg \
    -from [email protected] -to someone@somewhere \
    -subject "Signed and Encrypted message" -des3 user.pem

注意:加密命令不包括 -text 选项,因为被加密的邮件已经有 MIME 标头。

解密邮件:

openssl smime -decrypt -in mail.msg -recip mycert.pem -inkey key.pem

Netscape 表单签名的输出是具有分离签名格式的 PKCS#7 结构。您可以使用此程序通过对 base64 编码结构进行换行并将其包围来验证签名:

-----BEGIN PKCS7-----
-----END PKCS7-----

并使用命令:

 openssl smime -verify -inform PEM -in signature.pem -content content.txt

或者,您可以对签名进行 base64 解码并使用:

openssl smime -verify -inform DER -in signature.der -content content.txt

使用 128 位 Camellia 创建加密消息:

openssl smime -encrypt -in plain.txt -camellia128 -out mail.msg cert.pem

向现有消息添加签名者:

 openssl smime -resign -in mail.msg -signer newsign.pem -out mail2.msg
,

这是从问题的主要目的开始的回顾。

RFC-2311 - Section 3.4.3.3 Sample multipart/signed Message - 我根本无法让这个实现与 openssl 一起工作。出于某种原因,它破坏了消息。

Content-Type: multipart/signed;
  protocol="application/pkcs7-signature";
  micalg=sha1; boundary=boundary42

--boundary42
Content-Type: text/plain

This is a clear-signed message.

--boundary42
Content-Type: application/pkcs7-signature; name=smime.p7s
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=smime.p7s

ghyHhHUujhJhjH77n8HHGTrfvbnj756tbB9HG4VQpfyF467GhIGfHfYT6
4VQpfyF467GhIGfHfYT6jH77n8HHGghyHhHUujhJh756tbB9HGTrfvbnj
n8HHGTrfvhJhjH776tbB9HG4VQbnj7567GhIGfHfYT6ghyHhHUujpfyF4
7GhIGfHfYT64VQbnj756

--boundary42--

RFC-2311 - Section 3.4.2 Signing Using application/pkcs7-mime and SignedData - 此实现在我测试过的 Outlook365 中确实有效。

我认为 signed-data 效果最好的原因是,如果整个内容都编码为 base64,则在电子邮件传输过程中不太可能弄乱。

它接受您的纯文本消息并将您的数字签名嵌入其中,创建一个包含两者的 smime.p7m 附件。这是包含所有 base64 格式的文件。

S/MIME 电子邮件客户端将自动验证、解码并再次以纯文本形式显示给您。效果很好!

Content-Type: application/pkcs7-mime; smime-type=signed-data;
    name=smime.p7m
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=smime.p7m

567GhIGfHfYT6ghyHhHUujpfyF4f8HHGTrfvhJhjH776tbB9HG4VQbnj7
77n8HHGT9HG4VQpfyF467GhIGfHfYT6rfvbnj756tbBghyHhHUujhJhjH
HUujhJh4VQpfyF467GhIGfHfYGTrfvbnjT6jH7756tbB9H7n8HHGghyHh
6YT64V0GhIGfHfQbnj75

步骤 1. 使用 openssl 为您想要的电子邮件地址生成证书签名请求私钥。将私钥复制到openssl\bin目录。

第 2 步。使用证书签名请求https://www.sslstore.com

获取 DigiCert S/MIME 1 级证书 (DV)

步骤 3. 将证书文件下载到磁盘并查找 My_CA_Bundle.ca-bundle 文件。将此文件复制到 openssl\bin 目录。

可选步骤。如果您想将此证书导入 Windows 证书管理器,请从 3 个文件 My_Private.key My_Certificate.crt My_CA_Bundle.ca-bundle 创建 PKCS #12。

openssl pkcs12 -export -out My_Digital_Signature_Bundle.p12 -inkey My_Private.key -in My_Certificate.crt -certfile My_CA_Bundle.ca-bundle

签署纯文本消息

步骤 1. 创建一个名为 text-message.eml 的文本文件,添加字符串 This is a opaque-signed message. 并将其复制到 openssl\bin 目录。此文件是您要签名的文字纯文本消息。没有标题。只是你的消息。

现在我们将使用 3 个文件 My_Private.key My_Certificate.crt My_CA_Bundle.ca-bundle 签署一条纯文本消息,然后将其输出到 signed-message.eml 文件。

openssl smime -sign -in text-message.eml -inkey My_Private.key -signer My_Certificate.crt -certfile My_CA_Bundle.ca-bundle -nodetach -text -out signed-message.eml -to "<[email protected]>" -from "<[email protected]>" -subject "A Digitally Signed Message"

在上面的 openssl 命令中,您会看到 -nodetach 将消息和签名的 .p7m base64 融合到一个 blob 中。

signed-message.eml

过去只是 This is an opaque-signed message. 现在已经变成:

To: <[email protected]>
From: <[email protected]>
Subject: A Digitally Signed Message
MIME-Version: 1.0
Content-Disposition: attachment; filename="smime.p7m"
Content-Type: application/x-pkcs7-mime; smime-type=signed-data; name="smime.p7m"
Content-Transfer-Encoding: base64

MIISuwYJKoZIhvcNAQcCoIISrDCCEqgCAQExDzANBglghkgBZQMEAgEFADBKBgkq
hkiG9w0BBwGgPQQ7Q29udGVudC1UeXBlOiB0ZXh0L3BsYWluDQoNClRoaXMgaXMg
YSBjbGVhci1zaWduZWQgbWVzc2FnZS6ggg+1MIIGTjCCBTagAwIBAgIQBK55YGZm
kBq5xX+mbFvczTANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UE
ChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD
VQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMTMxMTA1MTIwMDAw
WhcNMjg...

从现在开始,纯文本消息与公共证书一起嵌入到 base64 内容中。 即使对此 base64 消息进行最轻微的更改也会使整个消息无效并且无法通过验证。那没什么意思;没有多余的单词,没有空格,没有点。在所有。期间。

使用 openssl 验证数字签名的消息

最后一步是验证最近数字签名的消息。

openssl smime -verify -in signed-message.eml -CAfile My_CA_Bundle.ca-bundle -binary -signer signer-certificate-extracted.crt -out signed-content-extracted.txt

openssl 只需要 public My_CA_Bundle.ca-bundle 文件来执行此验证。如果成功,openssl 将输出 signer-certificate-extracted.crt,最后是数字签名的 signed-content-extracted.txt;消息完好无损,如果你愿意的话。

下面的屏幕截图说明了成功的数字签名验证。

successful_verification

使用 CURL 发送数字签名的电子邮件

curl --verbose -ssl smtps://secure.email.com:465 --login-options AUTH=PLAIN --user [email protected]:Letmein --mail-from [email protected] --mail-rcpt [email protected] --mail-rcpt-allowfails --upload-file signed-message.eml