如何使用 OPENSSL 对电子邮件消息进行签名并使用 CURL 进行发送?

How to use OPENSSL to sign an email message and CURL to send it?

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

下面是我要签名的简单 TEXT/PLAIN 电子邮件示例。

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: <aade2ef2-960f-4e0d-94e4-d100f5270d28@initech.com>
Subject: TPS Report #1001
From: "Michael @ Initech" <michael@initech.com>
To: "Peter @ Initech" <peter@initech.com>
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
michael@initech.com
P | 801.555.1234

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

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

砰。仅此而已。

下面link介绍如何使用 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://www.rfc-editor.org/rfc/rfc8551#section-3.5

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

或者它是否应该像 text/plain 一样在单个部分中包含 modified/signed 文本?

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

只有消息 body 被签名。那就是 headers.

之后的空行之后的所有内容

Headers 无法包含在内,因为它们在发送给收件人的途中可能会被邮件服务器和邮件客户端更改(修改现有的,或添加新的)。

然而,有一些方法可以将一些重要的 headers(如 ToSubject)复制到 body,但它们并不广泛支持(还没有?)。

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,包括headers:

openssl smime -sign -in in.txt -text -signer mycert.pem \
    -from steve@openssl.org -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 steve@openssl.org \
    -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 steve@openssl.org -to someone@somewhere \
    -subject "Signed and Encrypted message" -des3 user.pem

注意:加密命令不包含-text选项,因为被加密的邮件已经有MIME headers.

解密邮件:

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[=40 获取 DigiCert S/MIME Class 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 an opaque-signed message. 并将其复制到 openssl\bin 目录。此文件是您要签名的文字纯文本消息。没有headers。只是你的留言。

现在我们将使用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 "<admin@example.com>" -from "<client@example.com>" -subject "A Digitally Signed Message"

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

signed-message.eml

原来只是This is an opaque-signed message.现在变成了:

To: <client@example.com>
From: <admin@example.com>
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...

从现在开始,纯文本消息与 public 证书一起嵌入到 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;消息完好无损,如果你愿意的话。

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

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

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