# Format of hybrid crypto envelope structure?

From posts including the accepted answer to

How to encrypt file with RSA and AES that anyone with RSA key can decrypt them?

hybrid crypto (AKA envelope encryption) a GPG-encrypted file has an element that is the symmetric-key-encoded payload and also an element that is the asymmetric-key-encoded symmetric-key.

I need to manually construct such a file using e.g. RSA and symmetric crypto APIs in a legacy system.

(Edited the following to clarify...) I know from reading up at e.g. https://futureboy.us/pgp.html#Symmetric, https://www.gnupg.org/gph/en/manual/x110.html, https://tools.ietf.org/html/rfc4880 that an OpenPGP message follows this structure. But is there a good reference with examples having the required structure of a composite GPG-encrypted file?

Does anyone know of resources that include example envelopes of this kind?

• Why not create one for PGP and then PGP-dump the result? – Maarten Bodewes Dec 5 at 12:45

First, OpenPGP/GPG is not the only standard for hybrid encryption, often called enveloping. For files/messages there are also PKCS7/CMS and SMIME which are ASN.1 (plus MIME) based, XMLenc which is XML based, and JWE (JSON web encryption) which is JSON based. There are also several communication schemes (at least SSL/TLS, SSH, IPsec/IKE) which are not directly applicable here.

All of these are standards and one of the defining characteristics of a standard is that it is specified in the document(s) that specify the standard. For OpenPGP, the current standard is RFC4880 as could easily found for example in the wikipedia article which is one place someone with any initiative would look. (RFCs -- Internet 'request for comment' documents -- are actually available numerous other ways, which used to be important decades ago before the web. For more about RFCs see the wikipedia article.)

PGP's encrypted-data functionality is introduced/outlined in 2.1 (and 2.4) and recapitulated in 11.3. The core of the document is organized bottom-up: data elements and packet structure are defined in 3 and 4; specific packets are defined in 5 with pk-encrypted-key in 5.1 and old-style encrypted data in 5.7 and the new improved encrypted+MDC-data in 5.13; optional compressed-data is in 5.6 and 'armoring' in 6. Note that PGP uses a not-quite-standard mode of symmetric encryption, described in 5.7 and expanded in 13.9: CFB with IV of zeros but a nonce block prepended (which serves the same security function) and a 2-byte 'stutter' that provides a quick check for wrong key.

The main implementation (now) of OpenPGP is GnuPG which is opensource, but is a pretty big program. For learning only a small piece like this, if you know or are willing to learn Java, I recommend BouncyCastle which is also opensource and (IMHO) well-organized and coded (though not extensively commented) and being Java isn't cluttered by much of the housekeeping code needed in C or even C++. Either of those programs can also be used to create (example) messages for your code to read, or check/verify messages created by your code.

Added: since you are so intent on an example, here is one, using the 'new' (symm+MDC) format preferred by my pretty elderly GPG 2.0.14 so I don't have to bother finding the option(s?) to turn it off:

$od -tx1 cry64585.enc 0000000 85 01 0c 03 8a 30 13 e2 06 1f b9 42 01 07 fe 22 0000020 1b 8c bd 1b d3 d7 92 72 42 ee 86 aa e6 9b e1 eb 0000040 bb bc 88 b8 48 93 bd 56 28 49 28 77 59 e9 9c 61 0000060 69 06 0a 6c 86 5e d4 c7 48 24 c1 c3 c7 7e ee aa 0000100 e4 e4 08 4d 8b 64 ce 46 3a 91 64 98 52 e1 71 bd 0000120 4f b7 64 c2 9a c0 a4 cd b2 33 6f 41 9f 4d 5d eb 0000140 5a 1d 76 b5 19 5f 11 d4 2e ea 0b 50 04 34 f9 ed 0000160 15 b0 ae 63 6b d4 1d ed 9f 2e c8 6c 4f c3 1a ab 0000200 2a 1b f7 13 39 ac 14 de 30 f7 00 0f ae 61 d7 c4 0000220 c1 cc b2 33 0f fc a8 c7 61 7f a9 f7 23 f7 18 e1 0000240 59 3c b9 a5 5f b6 fe b4 26 65 3e fb 40 43 c3 46 0000260 7d 2b 32 25 6c b7 e3 8d a4 32 fd 69 a9 dd 69 65 0000300 4f 95 55 f8 f9 dc f9 50 e1 4d fe 5c 4e be 68 6d 0000320 8f 4f a8 bb 1a 05 07 05 57 22 51 37 87 2e 54 af 0000340 1a 69 2d c6 af 03 78 9f 6c db 71 e4 38 f5 03 f6 0000360 dc 80 93 63 ce b9 bd 61 d2 7e d0 98 26 15 8c 40 0000400 e9 35 da 60 5c e5 6d 3a c8 5a 26 08 26 69 dc d2 0000420 40 01 8f ce 2c ff 95 a8 65 09 22 5d 2c 40 37 c9 0000440 ac b3 f4 64 f9 72 f1 c7 3c a5 91 46 9d a9 3f 73 0000460 39 9a 8d e2 cb 66 f7 1e a2 19 73 e1 77 e9 ab 0b 0000500 b2 91 96 0c 3d 3b 77 17 25 1d 8f ec 21 f2 0a 53 0000520 5a 0000521  The 0x10f bytes at offset zero are the pk-enc-sk packet (tag 1), and the 0x42 bytes at offset 0x10f = 0417 are the symm-mdc-data packet (tag 18). As a bonus, here's the public key used to encrypt: $ od -tx1 cry64585.pub
0000000 99 01 0d 04 59 3b a3 a5 01 08 00 c0 77 a2 45 43
0000020 79 91 7b 7e 7b 88 e3 2f f8 19 5a b0 6a e8 67 87
0000040 56 a3 11 96 d7 fd c5 e2 33 19 f6 9d d5 1a 48 08
0000060 38 29 71 0e 4f 98 56 e9 5d 5c 09 b0 d6 6c 92 51
0000100 33 d4 a5 65 df fd 05 57 e6 ea 32 a4 88 55 01 e4
0000120 9b 27 1e 68 8a 10 45 df 72 26 2c bd 8a d8 04 59
0000140 e7 a1 79 21 90 33 e0 b9 51 48 ab 31 04 7a 3a 61
0000160 a1 88 6a 7f 6b b4 d6 49 3d ae 40 80 f5 33 f0 5f
0000200 1c 7d ad 30 2e a8 e8 3b 24 8f ca a1 54 4c 3e eb
0000220 61 03 78 91 72 dd 36 7d da 3d 92 46 f3 ad 7c 7b
0000240 c7 39 ae f0 4e 51 fd fe dc 09 26 3f 9f b0 24 4d
0000260 f1 d5 aa f0 84 2e 18 77 a7 b2 7d 5c fa d6 2b ae
0000300 f3 55 5a f2 af c2 15 de 82 82 37 14 8f 9b 52 3b
0000320 dd 63 8e 21 53 f2 cb 6b bb 50 bd 0e 08 f6 44 eb
0000340 8a 2f 3f 15 32 1f e3 d4 32 3c 5c 23 dc 30 ea b4
0000360 0c 3e 1a 69 0d 82 6a 74 fc 2c 3e 3d 63 a8 d0 83
0000400 b7 3e 79 3a 0a 60 81 65 e2 94 17 00 11 01 00 01
0000420 b4 13 72 73 61 40 74 65 73 74 20 3c 72 73 61 40
0000440 74 65 73 74 3e 89 01 38 04 13 01 02 00 22 05 02
0000460 59 3b a3 a5 02 1b 03 06 0b 09 08 07 03 02 06 15
0000500 08 02 09 0a 0b 04 16 02 03 01 02 1e 01 02 17 80
0000520 00 0a 09 10 d1 d6 73 c6 3b ed f9 8f e2 e9 07 fd
0000540 1a 85 15 61 d4 af 7f b2 94 95 c3 c2 ca b1 eb 1a
0000560 80 31 cd bd ec 26 78 0f 0b 97 29 85 9e 92 27 1b
0000600 ad 3c 7e 3c 35 eb 59 f9 57 ce 94 9e 13 e4 d1 50
0000620 fd 82 0f 3a 1f e7 75 56 70 e3 40 bf b7 6a 00 5f
0000640 b6 e9 83 f6 ba f1 28 12 4f 10 d5 72 a3 de d1 06
0000660 90 91 b0 dd e0 f7 95 b3 be 94 9b 43 ea 56 86 d7
0000700 d4 c3 6b 66 b4 db ed 07 6a 8e b7 97 72 b2 b2 dc
0000720 b8 db 33 de a9 76 10 7e 81 ae ab f4 19 39 e7 40
0000740 86 d1 32 53 c2 c1 e4 e1 dd e5 fd c5 16 2d 76 8d
0000760 3b 31 3f b7 e4 b8 8f f9 8b 36 ae b3 67 32 6d 40
0001000 83 ee 88 3f 8c ca 63 2c 33 bb 7c 82 04 07 58 d8
0001020 40 5b ec 1a 85 58 32 88 df e0 a8 72 f8 cf 1d f8
0001040 30 86 ad 22 c6 44 17 b7 03 0b e3 f7 a6 d8 43 6c
0001060 39 ea c2 c9 b7 a3 22 09 9b 8b b8 3a ea a6 c2 77
0001100 3d 60 29 9c 4b 02 ea 27 94 b9 77 7a 60 0d af 85
0001120 42 91 7b 0d 02 f9 fc fc 59 81 d5 e4 6b c9 10 45
0001140 b9 01 0d 04 59 3b a3 a5 01 08 00 e4 4c 11 6e 79
0001160 1b 58 da d1 b9 4c c9 6f 4d bd bf ee d8 40 f9 a7
0001200 a1 0f 5d 3e 8d 35 40 d8 36 0f 08 be fd 9d 3a 1a
0001220 13 ea 8d 49 47 1f f1 bc 5a 9a f7 33 ea c8 1f 7a
0001240 41 81 47 52 46 0f 37 7d a6 41 92 ba 2a 4b a0 a5
0001260 dc 83 03 e5 5c 39 3c 96 4a 1a 6e 3c a8 1f 29 8a
0001300 9f 91 10 31 94 f6 e0 d2 b8 c3 e1 60 29 67 d0 26
0001320 13 86 61 17 5d 64 39 4d 00 14 98 1b 67 b8 d8 44
0001340 2e 22 72 a3 25 5c 90 6f 8c 89 a6 3c 97 89 b5 26
0001360 1d 01 2e f5 e5 1b 38 34 63 f7 04 20 2a ce ba 35
0001400 cd 30 f2 91 17 09 a4 e3 d1 ba f9 f6 09 b7 00 ef
0001420 a2 72 76 5b dc 2a c8 10 44 81 84 8a 36 41 79 17
0001440 07 fe 18 6e 79 da ce 3d e1 2d 7d 30 d7 25 31 27
0001460 7b ae 4c 74 d6 17 d4 1b c5 b3 51 61 7d 87 8e 4e
0001500 5a df 43 6a 3d 00 0c 0e 99 dd 98 68 60 b9 87 06
0001520 09 44 36 b7 49 99 56 b8 f3 ce 86 b7 8a 9c 28 e1
0001540 65 82 7a 27 6b 9d e9 ea c7 e8 fd 00 11 01 00 01
0001560 89 01 1f 04 18 01 02 00 09 05 02 59 3b a3 a5 02
0001600 1b 0c 00 0a 09 10 d1 d6 73 c6 3b ed f9 8f 66 8d
0001620 08 00 b4 39 f4 ed ee 3a 2b fe eb 7f 40 8a 22 96
0001640 5e cb af 92 fb f8 70 37 6f e4 84 b1 81 2f 38 05
0001660 f2 28 a9 1a b5 c4 e1 be 3c 3b 64 20 7c 6e 7a a7
0001700 60 b1 3d be 9a e3 7c 8d 61 31 27 f9 a0 e6 a1 e7
0001720 e0 49 12 d4 e5 b2 57 ca ca 51 22 4a 5f 81 0e e0
0001740 a0 c7 41 89 63 01 55 21 23 bf 41 ec 7b 03 d6 7a
0001760 3b e5 8e 80 4e 37 d5 ce be 1e ae f7 a2 7a 8e ec
0002000 b9 64 88 64 dd 1c 36 08 73 7f 0a be e2 7c 48 c9
0002020 ee 8a 9f 72 e7 69 9b 7a ea 4f 4c 0a 21 9a 2e 7c
0002040 5f a6 5f 28 9b 31 b5 b0 fd 46 07 9c 80 9a 24 b4
0002060 86 27 1d 23 12 cb 19 64 aa 4e 40 7f 90 4d f8 c9
0002100 c0 7f b7 c3 93 7f 29 dc df e6 11 4a 5a af 7a 41
0002120 f5 9c cd 17 f7 a2 56 7e b6 3e 80 91 70 e6 9d 3e
0002140 23 55 73 de f4 e7 78 bf 7f ea ed c3 ce ee 4d 77
0002160 05 c7 39 c5 a0 b8 bd 00 ff bf 48 20 e4 89 50 2a
0002200 3c 5d 39 c2 55 53 12 dc 3c be 79 65 55 f6 d6 c4
0002220 65 90
0002222


I don't see how this can possibly help you. If you think you will write a (correct) program that reproduces the above (message) output and compare, that won't work; properly designed encryption, including both the symmetric level and the public-key level (here RSA -- even the outdated RSAES-PKCS1-v1_5 aka block type 2 scheme used by PGP due to its age), is randomized precisely to prevent reproducing it.

• Thanks @dave_thompson_085 for your reply and references. FYI, I have edited my question to clarify that what I am looking for is an example of a structure of the "11.3 ... OpenPGP message" section of RFC4880, for my case of "Encrypted Message". – MikeRoger Dec 6 at 10:32
• see edit (if you didn't get pinged automatically; I'm not sure if answer edits do that) – dave_thompson_085 Dec 6 at 17:04
• Thanks @dave_thompson_085 for those edits. Apologies for the basic questions, on this one I am on a steep learning curve as our legacy system has not had to do GPG encryption for us up to now. However, my use case is limited to a particular defined deliverable of securing content is a gpg-readable transmission, so I can hopefully as you say focus on only a small piece for now. I will indeed look in the BouncyCastle source, thanks for your patience and suggestions. – MikeRoger Dec 7 at 10:44
• In your octal dump @dave_thompson_085, thanks for the notes on RFC4880 (tag 1) content. In testing I generated similar gpg and I do see version (03) followed by the ID of my Public Key followed by 0x10 for AFAIK "16 - Elgamal" per rfc4880#section-9.1. I'm new to parsing the grammar at "11.3 OpenPGP Messages", once I figure out the format incl field length of the subsequent encrypted symm key, I can attempt to replicate on our legacy system. If I can poke out relevant calls in the BouncyCastle source that should assist me too. Once again, thanks for the helpful feedback. – MikeRoger Dec 7 at 11:04