10

I'd like to know if it's possible to use openssl command to retrieve and decrypt the key for encrypting/decryptig email content. I know I can decrypt the whole encrypted mail by something like this

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

However, I'd like to see the steps in between. If I understand the procedure right, the actual content of the email is being encrypted not by the recipient's pubkey, but with randomly generated key on sender's side. This key is then encrypted with recipient's pubkey and attached to the encrypted message. Am I right? Is it possible to use openssl to show me the attached encrypted key and decrypt it separately?

Thanks.

Jakub Žitný
  • 379
  • 1
  • 2
  • 9

2 Answers2

11

Yes you can. This example uses openssl smime with the default RC2 CBC with a 40-bit key. The newer cms sub-command behaves slightly differently, and uses 3-DES by default. You probably shouldn't be using either of those algorithms to encrypt important data ;-)

There are two minor caveats: firstly, I'm also going to use a couple of other tools (though OpenSSL is used for all the heavy lifting), and secondly I'm going to make some assumptions about how the email was encrypted.

The en/decryption is along the lines of most RSA-using methods: use (slow, expensive) RSA to en/decrypt a symmetric key, and use the fast symmetric key to en/decrypt the real data. (See this question or this for more background).

Take your email, extract the P7M part, and decode it. If you have a single .p7m part that is base64 encoded you can do this easily with metamail:

$ metamail -wy enc_mail.eml

Save the P7M file. This is an ASN.1 DER encoded CMS (PKCS#7) file, so we can peek inside:

  $ 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 ...]

I'm using dumpasn1 because openssl asn1parse is disinclined to display or dump the various blobs we're interested in. The structure of your email will vary from the above of course. Columns 1 and 2 are the offset and size of each (possibly nested) sub-structure.

The interesting parts are at offsets:

  • 188 the RSA encryption details, followed at offset 203 by encrypted data
  • 463 the S/MIME encryption details and parameters (RC2 CBC)
  • 506 the encrypted blob

At offset 188 we can see RSA is used, followed by 256 bytes of data, so extract that data blob (offset 203) and convert it to binary:

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

(Note starting offset -203 and the use of tail to skip over the first line of output. This is a little convoluted, but unfortunately both dumpasn1 and openssl asn1parse leave the type-length prefix intact when you try to slice and dice objects.)

Decrypt this data using RSA:

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

In this case we get 5 bytes (40-bit) output, the symmetric key we need. The input was PKCS#1 v1.5 padded (see §8.1) for various reasons, hence the size disparity.

The main payload (email) is in the blob at offset 506, extract that to a file:

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

Now here's the slightly tricky bit, for RC2 refer to section 6 of RFC 2268:

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

This explains the data structure at offset 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                 
         : . . . . . . }

(You can confirm that RC2Version 160 (0xa0) matches key size 40 bit (0x28) in the EKB table.)

So, putting it all together: the S/MIME encryption algorithm (RC2 40 bit CBC, offset 480), the RC2 key (decrypted from blob at offset 203), the RC2 IV (not encrypted, offset 496) and the encrypted payload (offset 506):

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

and email.txt should be what you're looking for.

Tips:

  • make sure not to use an ancient version of xxd, it may mangle input hex data
  • the structure, sizes and offsets will of course vary by message, keys and algorithm
  • I find openssl enc -kfile ... does not work, stick to -K
  • openssl smime may have modified your message before encryption (CRLF requirements)
  • berdump is a handy tool as its output is more amenable to further processing. Since DER is a subset of BER, you can point it directly at a PKCS#7 DER file
mr.spuratic
  • 7,937
  • 25
  • 37
  • Thanks a lot for the great answer. What if I have the email content somehow chunked? I have a long payload that look something like [this](https://gist.github.com/jakubzitny/7432264).. Apparently, it's encrypted with DES-EDE3 in CBC mode. How do I decode this? – Jakub Žitný Nov 12 '13 at 14:58
  • Not sure. It appears the data has the "constructed" bit set, so it also has ASN.1 structure. Since every part within is a multiple of 8 (3-DES block size), try decrypting (`openssl enc -d -des-ede3-cbc ...`) each block in turn. It's possible the message is encrypted line by line, which is odd. If that's the case, then it seems the IV is being reused, but I'm just guessing here. You'll need to get your hands dirty with some ASN.1 coding, or perhaps [berdump](https://github.com/hallgrimurhg/berdump) for scripting hackery. – mr.spuratic Nov 12 '13 at 19:06
  • I'll try to work this out, thanks.. One last thing though, who decides which symetric encryption algorithm would be used (rc2/3des/others)? Is it the mail client app? – Jakub Žitný Nov 13 '13 at 05:27
  • 1
    See [RFC 3851 §2.7.1](http://tools.ietf.org/html/rfc3851.html#section-2.7.1), 3-DES is the only "required" symmetric encryption for S/MIME v3.1. Recipient capabilities can be indicated in a signed message, or by way of extensions in their certificate ([RFC 4262](http://tools.ietf.org/html/rfc4262)), so the MUA can do the right thing. – mr.spuratic Nov 13 '13 at 13:30
  • It seems to me that `openssl smime -pk7out -in en_mail.eml` can provide the same result as `metamail -wy enc_mail.eml`. Am I wrong? – not2savvy Jan 17 '18 at 19:36
  • 1
    @not2savvy `openssl smime` is fussy, `metamail` will extract parts from a "multipart" MIME, `openssl` will not. Also, `openssl -pk7out` will only output PEM format PKCS#7 (adding `-outform DER` has no effect), so an extra step is required to inspect with `dumpasn1` (binary DER/BER input only) – mr.spuratic Jan 19 '18 at 19:40
0

S/MIME uses PKCS7. You probably have to write the code yourself.

Here is an example how to do that in C++: https://stackoverflow.com/questions/6369096/how-to-read-the-certificates-file-from-the-pkcs7-p7b-certificate-file-usind-open

And this is an example for Java: https://stackoverflow.com/questions/11026588/pkcs7-encryption

ceving
  • 462
  • 2
  • 7
  • Thanks. I get the idea though that this only lists certificates from the p7s file.. I can also do that with openssl with `openssl pkcs7 -in smime.p7s -inform DER -print_certs`. I want something more, I want to see the key for encryption/decryption of the body of the message. That is not part of any certificate, right? You see what I mean? – Jakub Žitný Nov 10 '13 at 01:46
  • You are looking for the key for the symmetric encryption. The way how it is stored in the PKCS7 file is defined in [RFC 5652](http://tools.ietf.org/html/rfc5652). But I can not tell you the exact position. – ceving Nov 10 '13 at 10:28