NOTE: to actually use these methods securely could, and probably does, require decent understanding of data encryption!
If you are in doubt, feel free to leave a message and I'll try to be of assistance.
Setup
Before we can start encrypting and signing messages we'll require some keys and certificates. Let's create a self-signed certificate based on a new key with the subject "PKCS#7 example".
openssl req -x509 -nodes -newkey rsa:1024 -keyout keyfile.key -out certificate.cer -subj "/CN=PKCS#7 example"
This should result in the creation of two new files:
- certificate.cer - containing a PEM-encoded X.509 certificate
- keyfile.key - containing a PEM-encoded (RSA) private key
alias oad='openssl asn1parse -inform der -in'
alias oap='openssl asn1parse -inform pem -in'
oad is used to "OpenSSL ASN.1 dump DER"
oap is used to "OpenSSL ASN.1 dump PEM"
e.g.
oap certificate.cer
0:d=0 hl=4 l= 512 cons: SEQUENCE
4:d=1 hl=4 l= 361 cons: SEQUENCE
8:d=2 hl=2 l= 3 cons: cont [ 0 ]
10:d=3 hl=2 l= 1 prim: INTEGER :02
13:d=2 hl=2 l= 9 prim: INTEGER :D28124BDCEECCCCC
24:d=2 hl=2 l= 13 cons: SEQUENCE
26:d=3 hl=2 l= 9 prim: OBJECT :sha1WithRSAEncryption
37:d=3 hl=2 l= 0 prim: NULL
39:d=2 hl=2 l= 25 cons: SEQUENCE
41:d=3 hl=2 l= 23 cons: SET
43:d=4 hl=2 l= 21 cons: SEQUENCE
45:d=5 hl=2 l= 3 prim: OBJECT :commonName
50:d=5 hl=2 l= 14 prim: UTF8STRING :PKCS#7 example
66:d=2 hl=2 l= 30 cons: SEQUENCE
68:d=3 hl=2 l= 13 prim: UTCTIME :130412203318Z
83:d=3 hl=2 l= 13 prim: UTCTIME :130512203318Z
98:d=2 hl=2 l= 25 cons: SEQUENCE
100:d=3 hl=2 l= 23 cons: SET
102:d=4 hl=2 l= 21 cons: SEQUENCE
104:d=5 hl=2 l= 3 prim: OBJECT :commonName
109:d=5 hl=2 l= 14 prim: UTF8STRING :PKCS#7 example
125:d=2 hl=3 l= 159 cons: SEQUENCE
128:d=3 hl=2 l= 13 cons: SEQUENCE
130:d=4 hl=2 l= 9 prim: OBJECT :rsaEncryption
141:d=4 hl=2 l= 0 prim: NULL
143:d=3 hl=3 l= 141 prim: BIT STRING
287:d=2 hl=2 l= 80 cons: cont [ 3 ]
289:d=3 hl=2 l= 78 cons: SEQUENCE
291:d=4 hl=2 l= 29 cons: SEQUENCE
293:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Subject Key Identifier
298:d=5 hl=2 l= 22 prim: OCTET STRING [HEX DUMP]:0414B2ED2165623A8E3E5EB9652E781590C314EFC5B6
322:d=4 hl=2 l= 31 cons: SEQUENCE
324:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Authority Key Identifier
329:d=5 hl=2 l= 24 prim: OCTET STRING [HEX DUMP]:30168014B2ED2165623A8E3E5EB9652E781590C314EFC5B6
355:d=4 hl=2 l= 12 cons: SEQUENCE
357:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Basic Constraints
362:d=5 hl=2 l= 5 prim: OCTET STRING [HEX DUMP]:30030101FF
369:d=1 hl=2 l= 13 cons: SEQUENCE
371:d=2 hl=2 l= 9 prim: OBJECT :sha1WithRSAEncryption
382:d=2 hl=2 l= 0 prim: NULL
384:d=1 hl=3 l= 129 prim: BIT STRING
Encryption
Encryption can be used to make a message only available to the target receiver and prevents eavesdropping. The message is encrypted with a public key, quiet often stored in a certificate. Because of the mathematical properties of the private and public key, the message can only be read with possession of the private key. In this example I'll show you how to encrypt a message that is only readable when decrypted with the private key created before. To encrypt a message we'll be using the newly created certificate we're using the smime command of OpenSSL. Have a look at the help for all the available options of this command (openssl smime --help).
Store the message we'll be encrypting in a file:
echo "This message won't be readable until decrypted again." > plain-original.txt
Then encrypt this message using the key from the certificate.cer created earlier. If the outform isn't specified the default output format is smime, for now I'll use pem:
openssl smime -encrypt -in plain-original.txt -outform pem -out encrypted.p7 certificate.cer
Verifiy the encrypted.p7 contains content that looks a bit like this:
oap encrypted.p7
-----BEGIN PKCS7-----
MIIBPAYJKoZIhvcNAQcDoIIBLTCCASkCAQAxgcAwgb0CAQAwJjAZMRcwFQYDVQQD
DA5QS0NTIzcgZXhhbXBsZQIJANKBJL3O7MzMMA0GCSqGSIb3DQEBAQUABIGAh/s9
JYTGgh46iAH95n2UQNuDNQWgG01akoVuj0KR9ChbRQuQPSas+p+eMK1ipyCPnLtv
7Mhcjsf0yPGkFod39LxiOhcepe2vRcwYxTaGgBHk7VMS2ilZwKZMYki1gxjCKe6b
BlK3kVGx5dA0YEEhIWNPbL4IZhn+Orx2HrL8mgAwYQYJKoZIhvcNAQcBMBoGCCqG
SIb3DQMCMA4CAgCgBAjzFuJU6hSmNoA49ZNTG5kYKkttlsot8ak+jaDPAbrwobFL
5BPmv4MyRtmBaRiHToCJMK+DfigMi3smVE2FeYC3YGQ=
-----END PKCS7-----
Decryption
A PKCS#7 encrypted message can be recognized by the PKCS#7 content type, which is pkcs7-envelopedData:
oap encrypted.p7
0:d=0 hl=4 l= 316 cons: SEQUENCE
4:d=1 hl=2 l= 9 prim: OBJECT :pkcs7-envelopedData
15:d=1 hl=4 l= 301 cons: cont [ 0 ]
19:d=2 hl=4 l= 297 cons: SEQUENCE
23:d=3 hl=2 l= 1 prim: INTEGER :00
26:d=3 hl=3 l= 192 cons: SET
29:d=4 hl=3 l= 189 cons: SEQUENCE
32:d=5 hl=2 l= 1 prim: INTEGER :00
35:d=5 hl=2 l= 38 cons: SEQUENCE
37:d=6 hl=2 l= 25 cons: SEQUENCE
39:d=7 hl=2 l= 23 cons: SET
41:d=8 hl=2 l= 21 cons: SEQUENCE
43:d=9 hl=2 l= 3 prim: OBJECT :commonName
48:d=9 hl=2 l= 14 prim: UTF8STRING :PKCS#7 example
64:d=6 hl=2 l= 9 prim: INTEGER :D28124BDCEECCCCC
75:d=5 hl=2 l= 13 cons: SEQUENCE
77:d=6 hl=2 l= 9 prim: OBJECT :rsaEncryption
88:d=6 hl=2 l= 0 prim: NULL
90:d=5 hl=3 l= 128 prim: OCTET STRING [HEX DUMP]:7A7782B34EC3C63ABAD847DC4C028AFB8C1072CB1A43154A7CBFB2CB5B31874FB0D5EF8607AE442762595CDB6C15BD6C0373F0CD21B25396AF457F3699BC87C09B7F2552BB7A9A03EE7EBD3FF8961A00D161BED2CF3214E491D18A26B3992DD1129AC2F4FC860A3E1A84C6F2115E788F6436EAD35A257FAD50D871D34E50F335
221:d=3 hl=2 l= 97 cons: SEQUENCE
223:d=4 hl=2 l= 9 prim: OBJECT :pkcs7-data
234:d=4 hl=2 l= 26 cons: SEQUENCE
236:d=5 hl=2 l= 8 prim: OBJECT :rc2-cbc
246:d=5 hl=2 l= 14 cons: SEQUENCE
248:d=6 hl=2 l= 2 prim: INTEGER :A0
252:d=6 hl=2 l= 8 prim: OCTET STRING [HEX DUMP]:A05AF334241D3C3C
262:d=4 hl=2 l= 56 prim: cont [ 0 ]
To decrypt a PKCS#7 envelopedData message we need access to the private key. In this example I will use the private key we've stored in the keyfile.key before. The OpenSSL smime command in used again:
openssl smime -decrypt -inform pem -in encrypted.p7 -inkey keyfile.key
This message won't be readable until decrypted again.
Signing
Signing a message in PKCS#7 format is almost as simple as encrypting it. Signing is done with the private key. This allows anyone who has access to the public key to verify that the message was signed by the owner of the matching private key and is used to proof the origin of a message.
Create a message to be signed:
echo "This message could only have been sent by me." > unsigned-original.txt
In this example I'm adding the -nodetach option. This option tells OpenSSL to include the original message in the PKCS#7 structure too. Besides the private key the certificate is also required for signing. This is used by OpenSSL to include a reference to the certificate in the signed message (an Issuer - SerialNumber combination, which should be unique). This reference enables the receiver to find the matching certificate, and thus public key. The command I'm using is as follows:
openssl smime -sign -nodetach -in unsigned-original.txt -out signed.p7 -outform pem -inkey keyfile.key -signer certificate.cer
The content is again PEM-encoded and can be parsed with asn1parse:
cat signed.p7
-----BEGIN PKCS7-----
MIIEFgYJKoZIhvcNAQcCoIIEBzCCBAMCAQExCzAJBgUrDgMCGgUAMD4GCSqGSIb3
DQEHAaAxBC9UaGlzIG1lc3NhZ2UgY291bGQgb25seSBoYXZlIGJlZW4gc2VudCBi
eSBtZS4NCqCCAgQwggIAMIIBaaADAgECAgkA0oEkvc7szMwwDQYJKoZIhvcNAQEF
BQAwGTEXMBUGA1UEAwwOUEtDUyM3IGV4YW1wbGUwHhcNMTMwNDEyMjAzMzE4WhcN
MTMwNTEyMjAzMzE4WjAZMRcwFQYDVQQDDA5QS0NTIzcgZXhhbXBsZTCBnzANBgkq
hkiG9w0BAQEFAAOBjQAwgYkCgYEAnvFUnLFlzYScwPXCTBdp+e3pBAV/wFc10gq2
bKCcHg6WgWrnW39HJxaYdn6Edzt8ipdTvX2Kl6iQXtjAIs8oI7YBdqxZMknddM1M
mvbe0HVYPoF0lIWiLLIySYeV6GC2X0eYyWE1FLs1qJsRSrHA8iHlDH4gngGMu71d
WCEbXg8CAwEAAaNQME4wHQYDVR0OBBYEFLLtIWViOo4+XrllLngVkMMU78W2MB8G
A1UdIwQYMBaAFLLtIWViOo4+XrllLngVkMMU78W2MAwGA1UdEwQFMAMBAf8wDQYJ
KoZIhvcNAQEFBQADgYEAG1PiV0P2iZhh88juujpr/BrlMm64BlX1hrsLyyNR6e85
iBC4kFJNndkFiCFz31EAJkzoMIUdgn07cENk2KAjf3BIrgBaeY6mp94s6lpRcfK7
lEsfbyADOQqszCuzadMHZd44XReMaf43Kw/WYwVmsHpBDpzgAwH1sxtSKcHnFmox
ggGnMIIBowIBATAmMBkxFzAVBgNVBAMMDlBLQ1MjNyBleGFtcGxlAgkA0oEkvc7s
zMwwCQYFKw4DAhoFAKCB2DAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqG
SIb3DQEJBTEPFw0xMzA0MTIyMTExMTVaMCMGCSqGSIb3DQEJBDEWBBS0KhYhWkxR
nvCUyavULWwbRXaF1jB5BgkqhkiG9w0BCQ8xbDBqMAsGCWCGSAFlAwQBKjALBglg
hkgBZQMEARYwCwYJYIZIAWUDBAECMAoGCCqGSIb3DQMHMA4GCCqGSIb3DQMCAgIA
gDANBggqhkiG9w0DAgIBQDAHBgUrDgMCBzANBggqhkiG9w0DAgIBKDANBgkqhkiG
9w0BAQEFAASBgHq2NEUYm1csunOIOlekNBPW12EWMU3lTK+phxPtKZl+y2233NLN
TWaLUaRF7ePjapr71IYlrLfse9Kv8xl8o2QU+XMhj1p2I6Ng0lt3VfwjgJ5isidk
GM/suaYFITr4PxGlhZKEtoYQh4BOr0r0+iy6aFJGKAhn4slB6qdhn025
-----END PKCS7-----
oap signed.p7
0:d=0 hl=4 l=1046 cons: SEQUENCE
4:d=1 hl=2 l= 9 prim: OBJECT :pkcs7-signedData
15:d=1 hl=4 l=1031 cons: cont [ 0 ]
19:d=2 hl=4 l=1027 cons: SEQUENCE
23:d=3 hl=2 l= 1 prim: INTEGER :01
26:d=3 hl=2 l= 11 cons: SET
28:d=4 hl=2 l= 9 cons: SEQUENCE
30:d=5 hl=2 l= 5 prim: OBJECT :sha1
37:d=5 hl=2 l= 0 prim: NULL
39:d=3 hl=2 l= 62 cons: SEQUENCE
41:d=4 hl=2 l= 9 prim: OBJECT :pkcs7-data
52:d=4 hl=2 l= 49 cons: cont [ 0 ]
54:d=5 hl=2 l= 47 prim: OCTET STRING :This message could only have been sent by me.
103:d=3 hl=4 l= 516 cons: cont [ 0 ]
107:d=4 hl=4 l= 512 cons: SEQUENCE
111:d=5 hl=4 l= 361 cons: SEQUENCE
115:d=6 hl=2 l= 3 cons: cont [ 0 ]
117:d=7 hl=2 l= 1 prim: INTEGER :02
120:d=6 hl=2 l= 9 prim: INTEGER :D28124BDCEECCCCC
131:d=6 hl=2 l= 13 cons: SEQUENCE
133:d=7 hl=2 l= 9 prim: OBJECT :sha1WithRSAEncryption
144:d=7 hl=2 l= 0 prim: NULL
146:d=6 hl=2 l= 25 cons: SEQUENCE
148:d=7 hl=2 l= 23 cons: SET
150:d=8 hl=2 l= 21 cons: SEQUENCE
152:d=9 hl=2 l= 3 prim: OBJECT :commonName
157:d=9 hl=2 l= 14 prim: UTF8STRING :PKCS#7 example
173:d=6 hl=2 l= 30 cons: SEQUENCE
175:d=7 hl=2 l= 13 prim: UTCTIME :130412203318Z
190:d=7 hl=2 l= 13 prim: UTCTIME :130512203318Z
205:d=6 hl=2 l= 25 cons: SEQUENCE
207:d=7 hl=2 l= 23 cons: SET
209:d=8 hl=2 l= 21 cons: SEQUENCE
211:d=9 hl=2 l= 3 prim: OBJECT :commonName
216:d=9 hl=2 l= 14 prim: UTF8STRING :PKCS#7 example
232:d=6 hl=3 l= 159 cons: SEQUENCE
235:d=7 hl=2 l= 13 cons: SEQUENCE
237:d=8 hl=2 l= 9 prim: OBJECT :rsaEncryption
248:d=8 hl=2 l= 0 prim: NULL
250:d=7 hl=3 l= 141 prim: BIT STRING
394:d=6 hl=2 l= 80 cons: cont [ 3 ]
396:d=7 hl=2 l= 78 cons: SEQUENCE
398:d=8 hl=2 l= 29 cons: SEQUENCE
400:d=9 hl=2 l= 3 prim: OBJECT :X509v3 Subject Key Identifier
405:d=9 hl=2 l= 22 prim: OCTET STRING [HEX DUMP]:0414B2ED2165623A8E3E5EB9652E781590C314EFC5B6
429:d=8 hl=2 l= 31 cons: SEQUENCE
431:d=9 hl=2 l= 3 prim: OBJECT :X509v3 Authority Key Identifier
436:d=9 hl=2 l= 24 prim: OCTET STRING [HEX DUMP]:30168014B2ED2165623A8E3E5EB9652E781590C314EFC5B6
462:d=8 hl=2 l= 12 cons: SEQUENCE
464:d=9 hl=2 l= 3 prim: OBJECT :X509v3 Basic Constraints
469:d=9 hl=2 l= 5 prim: OCTET STRING [HEX DUMP]:30030101FF
476:d=5 hl=2 l= 13 cons: SEQUENCE
478:d=6 hl=2 l= 9 prim: OBJECT :sha1WithRSAEncryption
489:d=6 hl=2 l= 0 prim: NULL
491:d=5 hl=3 l= 129 prim: BIT STRING
623:d=3 hl=4 l= 423 cons: SET
627:d=4 hl=4 l= 419 cons: SEQUENCE
631:d=5 hl=2 l= 1 prim: INTEGER :01
634:d=5 hl=2 l= 38 cons: SEQUENCE
636:d=6 hl=2 l= 25 cons: SEQUENCE
638:d=7 hl=2 l= 23 cons: SET
640:d=8 hl=2 l= 21 cons: SEQUENCE
642:d=9 hl=2 l= 3 prim: OBJECT :commonName
647:d=9 hl=2 l= 14 prim: UTF8STRING :PKCS#7 example
663:d=6 hl=2 l= 9 prim: INTEGER :D28124BDCEECCCCC
674:d=5 hl=2 l= 9 cons: SEQUENCE
676:d=6 hl=2 l= 5 prim: OBJECT :sha1
683:d=6 hl=2 l= 0 prim: NULL
685:d=5 hl=3 l= 216 cons: cont [ 0 ]
688:d=6 hl=2 l= 24 cons: SEQUENCE
690:d=7 hl=2 l= 9 prim: OBJECT :contentType
701:d=7 hl=2 l= 11 cons: SET
703:d=8 hl=2 l= 9 prim: OBJECT :pkcs7-data
714:d=6 hl=2 l= 28 cons: SEQUENCE
716:d=7 hl=2 l= 9 prim: OBJECT :signingTime
727:d=7 hl=2 l= 15 cons: SET
729:d=8 hl=2 l= 13 prim: UTCTIME :130412211115Z
744:d=6 hl=2 l= 35 cons: SEQUENCE
746:d=7 hl=2 l= 9 prim: OBJECT :messageDigest
757:d=7 hl=2 l= 22 cons: SET
759:d=8 hl=2 l= 20 prim: OCTET STRING [HEX DUMP]:B42A16215A4C519EF094C9ABD42D6C1B457685D6
781:d=6 hl=2 l= 121 cons: SEQUENCE
783:d=7 hl=2 l= 9 prim: OBJECT :S/MIME Capabilities
794:d=7 hl=2 l= 108 cons: SET
796:d=8 hl=2 l= 106 cons: SEQUENCE
798:d=9 hl=2 l= 11 cons: SEQUENCE
800:d=10 hl=2 l= 9 prim: OBJECT :aes-256-cbc
811:d=9 hl=2 l= 11 cons: SEQUENCE
813:d=10 hl=2 l= 9 prim: OBJECT :aes-192-cbc
824:d=9 hl=2 l= 11 cons: SEQUENCE
826:d=10 hl=2 l= 9 prim: OBJECT :aes-128-cbc
837:d=9 hl=2 l= 10 cons: SEQUENCE
839:d=10 hl=2 l= 8 prim: OBJECT :des-ede3-cbc
849:d=9 hl=2 l= 14 cons: SEQUENCE
851:d=10 hl=2 l= 8 prim: OBJECT :rc2-cbc
861:d=10 hl=2 l= 2 prim: INTEGER :80
865:d=9 hl=2 l= 13 cons: SEQUENCE
867:d=10 hl=2 l= 8 prim: OBJECT :rc2-cbc
877:d=10 hl=2 l= 1 prim: INTEGER :40
880:d=9 hl=2 l= 7 cons: SEQUENCE
882:d=10 hl=2 l= 5 prim: OBJECT :des-cbc
889:d=9 hl=2 l= 13 cons: SEQUENCE
891:d=10 hl=2 l= 8 prim: OBJECT :rc2-cbc
901:d=10 hl=2 l= 1 prim: INTEGER :28
904:d=5 hl=2 l= 13 cons: SEQUENCE
906:d=6 hl=2 l= 9 prim: OBJECT :rsaEncryption
917:d=6 hl=2 l= 0 prim: NULL
919:d=5 hl=3 l= 128 prim: OCTET STRING [HEX DUMP]:7AB63445189B572CBA73883A57A43413D6D76116314DE54CAFA98713ED29997ECB6DB7DCD2CD4D668B51A445EDE3E36A9AFBD48625ACB7EC7BD2AFF3197CA36414F973218F5A7623A360D25B7755FC23809E62B2276418CFECB9A605213AF83F11A5859284B6861087804EAF4AF4FA2CBA685246280867E2C941EAA7619F4DB9
Verification
Verifying a PKCS#7 message is done to verify the authenticity of the message and to make sure it was sent by someone who has access to the private key. OpenSSL also tries to verify the certificate included in the message (or supplied on the commandline in some cases). Because I'm using a self signed certificate in this example the verification of the certificate will fail; because it's not signed by a trusted party.
openssl smime -verify -in signed.p7 -inform pem
Verification failure
2675740:error:21075075:PKCS7 routines:PKCS7_verify:certificate verify error:pk7_smime.c:342:Verify error:self signed certificate
To overcome this we could add the -noverify option. This might look a bit weird (openssl verify -noverify...), but the message is still verified against the certificate. It's just the verification of the certificate itself that is skipped. Nevertheless this option should of course only be used when the full implications are understood!
openssl smime -verify -in signed.p7 -inform pem -noverify
This message could only have been sent by me.
Verification successful
Summary
OpenSSL is a verify powerful tool that can be a bit hard to operate. Depending on your (security) requirements some of the examples provided might be more suitable to your situation than others. Using these commands, or tailored versions, for troubleshooting or in testing environments should be OK. If however you plan on using these in a production environment, please, and don't take this lightly, consult someone with decent understanding of data encryption and key management!
openssl smime -encrypt -in plain-original.txt -outform pem -out encrypted.p7 certificate.cer
openssl smime -decrypt -inform pem -in encrypted.p7 -inkey keyfile.key
openssl smime -sign -nodetach -in unsigned-original.txt -out signed.p7 -outform pem -inkey keyfile.key -signer certificate.cer
openssl smime -verify -in signed.p7 -inform pem
I need to do it programmatically. Can you point me to any manual or tutorial? The OpenSSL documentation is GNU-style: it teaches all car mechanics, but not how to drive. :-P
ReplyDeleteThank you for such descriptive post! It's a great introduction for PKCS7.
ReplyDeleteI'm impressed. Thank you!
ReplyDeleteI have a problem.
ReplyDeleteCould you explain why providing external content to be checked against signed content causes an error
openssl smime -verify -in signed.p7 -inform pem -noverify -content unsigned-original.txt
I get
This message could only have been sent by me.
Verification failure
140190814914240:error:21071065:PKCS7 routines:PKCS7_signatureVerify:digest failure:pk7_doit.c:1097:
140190814914240:error:21075069:PKCS7 routines:PKCS7_verify:signature failure:pk7_smime.c:410:
Dear Bartek,
DeleteSorry for my very late reply. Your message some how went unnoticed.
If your still having problems with this, could you maybe send me the files you're having trouble with? That why I could try and see what's causing your problems.
Hi Chris,
ReplyDeleteI can see you are profi regarding decrypting and verifying PKCS#7 messages with OpenSSL. I would like to ask you for help.
I have got data "encrypted with X.509 certificates based on PKCS#7 message standard". Now I need to decrypt it using PHP. There is the OpenSSL library installed on the Apache server so it should be no problem. However I spent already days to make it without success. Can you advise me what PHP function is the right one and how to make it? I have the required X.509 certificate and RSA private key (both in one .pem file).
Thanks a lot!
Vladek
Hi Vladek,
DeleteThe one you're looking for is probably openssl_pkcs7_decrypt.
http://php.net/manual/en/function.openssl-pkcs7-decrypt.php
Hi Chris,
DeleteThank for reply! I was trying to use this function but without success.
Would it be possible to send you somehow the files so you can try to find the solution? Would you be so kind?
Thanks a lot
Vladek
Sure, I'll have a look.
DeleteSorry for the late reply, I was on a holiday.