使用 OpenSSL 的 SMIME 电子邮件解密密钥

信息安全 openssl 微笑
2021-08-26 04:22:03

我想知道是否可以使用 openssl 命令检索和解密用于加密/解密电子邮件内容的密钥。我知道我可以通过这样的方式解密整个加密邮件

openssl smime -decrypt -in enc_mail.eml -inkey recip_priv.pem > dec_mail.eml

但是,我想看看中间的步骤。如果我正确理解该程序,则电子邮件的实际内容不是由收件人的公钥加密,而是由发件人侧随机生成的密钥加密。然后使用收件人的 pubkey 对该密钥进行加密,并将其附加到加密的消息中。我对吗?是否可以使用 openssl 向我显示附加的加密密钥并单独解密?

谢谢。

2个回答

是的你可以。此示例使用openssl smime具有 40 位密钥的默认 RC2 CBC。较新的cms子命令的行为略有不同,默认使用 3-DES。您可能不应该使用其中任何一种算法来加密重要数据;-)

有两个小警告:首先,我还将使用其他一些工具(尽管所有繁重的工作都使用OpenSSL),其次,我将对电子邮件的加密方式做出一些假设。

加密/解密与大多数使用 RSA 的方法一致:使用(缓慢、昂贵的)RSA 加密/解密对称密钥,使用快速对称密钥加密/解密真实数据。(有关更多背景信息,请参阅此问题问题)。

获取您的电子邮件,提取 P7M 部分,然后对其进行解码。如果你有一个.p7mbase64 编码的部分,你可以很容易地做到这一点metamail

$ metamail -wy enc_mail.eml

保存 P7M 文件。这是一个ASN.1 DER 编码的CMS (PKCS#7) 文件,所以我们可以窥视一下:

  $ dumpasn1 -tilda  smime.p7m
   0 1946: SEQUENCE {
   4    9: . OBJECT IDENTIFIER envelopedData (1 2 840 113549 1 7 3)
         : . . (PKCS #7)
  15 1931: . [0] {
                  [ .. certificate details and whatnot omitted ...]
 188   13: . . . . . SEQUENCE {
 190    9: . . . . . . OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
         : . . . . . . . (PKCS #1)
 201    0: . . . . . . NULL
         : . . . . . . }
 203  256: . . . . . OCTET STRING    
         : . . . . . . A0 DA EA FB EA 1A 0F 81    ........
         : . . . . . . F4 30 9F 78 5C 9B A7 27    .0.x\..'
                       [ ... blob snipped ...]
 463 1483: . . . SEQUENCE {
 467    9: . . . . OBJECT IDENTIFIER data (1 2 840 113549 1 7 1)
         : . . . . . (PKCS #7)
 478   26: . . . . SEQUENCE {
 480    8: . . . . . OBJECT IDENTIFIER rc2CBC (1 2 840 113549 3 2)
         : . . . . . . (RSADSI encryptionAlgorithm)
 490   14: . . . . . SEQUENCE {
 492    2: . . . . . . INTEGER 160
 496    8: . . . . . . OCTET STRING 3E EA 0E 12 37 A8 56 70                 
         : . . . . . . }
         : . . . . . }
 506 1440: . . . . [0]    
         : . . . . . A5 FF A1 70 2C AD 82 6A    ...p,..j
         : . . . . . C7 F0 84 E8 9E 93 8F 53    .......S
                     [... blob snipped ...]

我使用它是dumpasn1因为openssl asn1parse不愿意显示或转储我们感兴趣的各种 blob。当然,您的电子邮件结构会与上述不同。第 1 列和第 2 列是每个(可能是嵌套的)子结构的偏移量和大小。

有趣的部分在偏移处:

  • 188 RSA 加密细节,在偏移量203之后是加密数据
  • 463 S/MIME 加密细节和参数 (RC2 CBC)
  • 506加密的 blob

在偏移量 188 处,我们可以看到使用了 RSA,然后是 256 字节的数据,因此提取该数据块(偏移量 203)并将其转换为二进制:

$ dumpasn1 -a -203 smime.p7m | tail -qn +2 | xxd -r -p > rsa.bin

(注意起始偏移-203和使用tail跳过输出的第一行。这有点令人费解,但不幸的是,当您尝试对对象进行切片和切块时,两者都dumpasn1保持openssl asn1parse类型长度前缀不变。)

使用 RSA 解密此数据:

$ openssl rsautl -inkey recip_priv.pem -in rsa.bin -decrypt -out rc2key.bin
$ xxd -u -p  rc2key.bin
92F6EB53B1

在这种情况下,我们得到 5 个字节(40 位)的输出,即我们需要的对称密钥。由于各种原因,输入是PKCS#1 v1.5填充(参见第 8.1 节),因此存在大小差异。

主要有效负载(电子邮件)位于偏移量 506 的 blob 中,将其提取到文件中:

$ dumpasn1 -a -506 smime.p7m | tail -qn +2 | xxd -r -p > email.bin

现在这里有点棘手,对于 RC2,请参阅RFC 2268的第 6 节:

rc2CBC OBJECT IDENTIFIER
 ::= {iso(1) member-body(2) US(840) rsadsi(113549)
      encryptionAlgorithm(3) 2}

RC2-CBCParameter ::= CHOICE {
  iv IV,
  params SEQUENCE {
    version RC2Version,
    iv IV
  }
}

RC2Version ::= INTEGER -- 1-1024
IV ::= OCTET STRING -- 8 octets

这解释了偏移量 490 处的数据结构:

 480    8: . . . . . OBJECT IDENTIFIER rc2CBC (1 2 840 113549 3 2)
         : . . . . . . (RSADSI encryptionAlgorithm)
 490   14: . . . . . SEQUENCE {
 492    2: . . . . . . INTEGER 160
 496    8: . . . . . . OCTET STRING 3E EA 0E 12 37 A8 56 70                 
         : . . . . . . }

(您可以确认 RC2Version 160 (0xa0) 与 EKB 表中的密钥大小 40 位 (0x28) 匹配。)

因此,将它们放在一起:S/MIME 加密算法(RC2 40 位 CBC,偏移量 480)、RC2 密钥(从偏移量 203 处的 blob 解密)、RC2 IV(未加密,偏移量 496)和加密的有效负载(偏移量 506):

$ openssl enc -d -rc2-40-cbc -in email.bin -out email.txt -K 92F6EB53B1 -iv 3EEA0E1237A85670

email.txt应该是你要找的

尖端:

  • 确保不要使用旧版本的xxd,它可能会破坏输入的十六进制数据
  • 结构、大小和偏移量当然会因消息、密钥和算法而异
  • 我发现openssl enc -kfile ...不行,坚持-K
  • openssl smime 可能在加密之前修改了您的消息(CRLF要求)
  • berdump是一个方便的工具,因为它的输出更适合进一步处理。由于 DER 是 BER 的子集,您可以将其直接指向 PKCS#7 DER 文件

S/MIME使用PKCS7您可能必须自己编写代码。

这是一个如何在 C++ 中执行此操作的示例:https ://stackoverflow.com/questions/6369096/how-to-read-the-certificates-file-from-the-pkcs7-p7b-certificate-file-usind-open

这是Java的一个例子:https ://stackoverflow.com/questions/11026588/pkcs7-encryption