java - Fail to gpg-decrypt BouncyCastlePGP-encrypted message -
when try decrypt message using gnupg encrypted bouncycastle 2 gpg: [don't know]: invalid packet (ctb=xx)
messages , decryption fails.
i using bouncycastle 1.54
, gpg (gnupg) 2.0.30 on osx
details
1) pgp key generated using gpg follows:
$ gpg --gen-key gpg (gnupg) 2.0.30; copyright (c) 2015 free software foundation, inc. free software: free change , redistribute it. there no warranty, extent permitted law. please select kind of key want: (1) rsa , rsa (default) (2) dsa , elgamal (3) dsa (sign only) (4) rsa (sign only) selection? 1 rsa keys may between 1024 , 4096 bits long. keysize want? (2048) requested keysize 2048 bits please specify how long key should valid. 0 = key not expire <n> = key expires in n days <n>w = key expires in n weeks <n>m = key expires in n months <n>y = key expires in n years key valid for? (0) 0 key not expire @ correct? (y/n) y gnupg needs construct user id identify key. real name: foo bar email address: foo@bar.com comment: test key selected user-id: "foo bar (test key) <foo@bar.com>" change (n)ame, (c)omment, (e)mail or (o)kay/(q)uit? o need passphrase protect secret key. need generate lot of random bytes. idea perform other action (type on keyboard, move mouse, utilize disks) during prime generation; gives random number generator better chance gain enough entropy. need generate lot of random bytes. idea perform other action (type on keyboard, move mouse, utilize disks) during prime generation; gives random number generator better chance gain enough entropy. public , secret key created , signed. pub 2048r/79cc322a 2017-08-17 key fingerprint = 93b9 0d06 08d2 eb84 9f83 4cd3 a470 748e 79cc 322a uid [ unknown] foo bar (test key) <foo@bar.com> sub 2048r/21b41e21 2017-08-17 $
2) pgp public key exported in asc form looks like:
-----begin pgp public key block----- version: gnupg v2 mqenbfmwe88bcadb+esy+d2zobru86ztutp7hwydy5w9b2ihlyorli6mq0jh+ya6 cbao9nwt7wc68l1ocwaeak0t/9hx0cdwv6zdaxfeupu8qb53m4m1nacdjp3udeqs uytmpb/kutviuo+yirhtezysfp70phoo7o9tgze5h/qf/hgavmk1/en4osth4hqo vivlsvtnzylt+a2wyn0rppryfxnvrkosxde5roqd/tmamwwd2mbhhh31ib1row2/ i+tvjfrm4zlseuo0ndymwl7zfjpslsfuezuqf5bdmfdii9m3/uwhthmsr6ggg1ko uo0xjabiijwfj1mqsa3txi1yo+rlhyri5ul1abebaag0iezvbybcyxigkfrlc3qg s2v5ksa8zm9vqgjhci5jb20+iqe5bbmbcaajbqjzlhppahsdbwsjcacdagegfqgc cqolbbycawechgecf4aacgkqphb0jnnmmipk+qf/cklsdv+ayhktes/rtaxqbrwg 5negkk8vu5hseesr9tdbzkmpruyxtgg99gjpjzo2nqoyf+3nzd6suiqzp95dhyh0 g3i2ahloyd7w9vvzeieh7vgrrujyyp2n/qifuepdiu3gkqlhdbe14txrheftn5wv bxclz4mwmmvhzsg6nb2rxjb7t5e9apgq/+0o9l5lwgssskcqr53omomsunqp0lrz gqmxzbc6si/fyavaomgnaumfq+8l7ni/tv6r/scn5d4egcic1azvcde+zgkwrrmx 4lcken0xvoljfpunx9d2ciaqttz0nvygxjbabyxt4ofcbpw9gzcnmxko/eup4lkb dqrzlhppaqgaz4tebyltfxzcj4xwzy7wyrw4j+vukmroh5tovgispmaeidbzfyhf oqs0uaamimuyf2hgvgxegappzncjrympdyfpdxg6jb0oyap03hheli1r6h+56pnq znyvzlfq85bqgva5qed1vndbez29jmjlwsvcey3v0siogvzxe5gfmjvakuhpoe+o t5uvurulnfdkxmygnrh5gbnesvx+pmgihc3psrwwybbtu6otnxcvcyqryorahwaf pvb99p6yzp0nc6dbu2zsigsdaqwa3zcow+e997upf22wmfowwknftarnrr1fvlkl exr28njdeabh0r3wpgqjqecgmh9fyqebjwaraqabiqefbbgbcaajbqjzlhppahsm aaojekrwdi55zdiqgaah/39teaauurb+xevr/q+mcxa0pqsrerb2p4javibuzh2/ 6eerycp6biwc5r/gpkivcpg8dmdytobrbj2ydc5o+4tpvgskjugob2l9cryp+acm lx8r0ncn8q53vqtmm7lyc+w1k6txqjzi8vbcc9slguxh0hudp2ldcbnrialv2clz zsxrgoff3f8u/we97z2qwzrqxshvw4gvl3wss511mtcozw8lno1ymt3m5abiszn0 p/avr7zeqsvr0ijoq7wbjlbghkxku03sfcn0xfvdx4vzqyklvn9thhvtofu6h4le loirkgg57dxfrsc93a1gwzn9z764sxq7jgwqnw6a72g= =n8oz -----end pgp public key block-----
3) pgp public key passed in in "public key block"-form , resolved follows: (the pgpkeybytes utf-8 byte encoding of string representation of key)
private pgppublickey resolvepgppublickey(byte[] pgpkeybytes) throws ioexception, pgpexception { pgppublickeyringcollection keyringcollection; try (inputstream in = pgputil.getdecoderstream(new bytearrayinputstream(pgpkeybytes))) { keyringcollection = new pgppublickeyringcollection( pgputil.getdecoderstream(in), new jcakeyfingerprintcalculator()); } iterator<?> keyringiterator = keyringcollection.getkeyrings(); while (keyringiterator.hasnext()) { pgppublickeyring keyring = (pgppublickeyring) keyringiterator.next(); iterator<?> keyiterator = keyring.getpublickeys(); while (keyiterator.hasnext()) { pgppublickey key = (pgppublickey) keyiterator.next(); if (key.ismasterkey()) { continue; } if (key.isencryptionkey()) { return key; } } } throw new servicerequestexception("cannot resolve pgppublickey"); }
4) getting pgp encrypted hex-string representation of 16-byte array (passed in keybytes) follows:
private byte[] encryptkeybytes(byte[] keybytes, byte[] pgpkey) throws generalsecurityexception { bytearrayoutputstream enckeybytes = new bytearrayoutputstream(keybytes.length); try (handle<securerandom> randomhandle = rngsupport.getrandom()) { jcepgpdataencryptorbuilder encryptorbuilder = new jcepgpdataencryptorbuilder(pgpencrypteddatagenerator.aes_256); encryptorbuilder.setwithintegritypacket(true); encryptorbuilder.setsecurerandom(randomhandle.getobject()); encryptorbuilder.setprovider("bc"); pgpencrypteddatagenerator encryptor = new pgpencrypteddatagenerator(encryptorbuilder); try { jcepublickeykeyencryptionmethodgenerator keyencryptionmethodgenerator = new jcepublickeykeyencryptionmethodgenerator(resolvepgppublickey(pgpkey)); keyencryptionmethodgenerator.setprovider("bc"); encryptor.addmethod(keyencryptionmethodgenerator); try ( outputstream ao = new armoredoutputstream(enckeybytes); outputstream eo = encryptor.open(ao, keybytes.length)) { eo.write(bytessupport.encodehex(keybytes).getbytes(standardcharsets.utf_8)); } } catch (servicerequestexception e) { throw e; } catch (exception e) { throw new generalsecurityexception("cannot perform pgp encryption", e); } } return enckeybytes.tobytearray(); }
5) example result of encryption looks like:
-----begin pgp message----- version: bcpg v@release_name@ hqema0a1hkkhtb4haqf+mfda3iljjivdyo+v9gwldxmq1oi8yfe/onfsct2kt6ag rkbiacqvwtqpd95qs3lo9srzyvd64c7+y+pa2e4nsjynilmyeczqfvzsgoi8ibhd ldg+trkaged3uisltju8of/d5supaubvrfh413xz2xg5lbx7z78u4ktaz1imk/xn dn2ncaoviw/ebqzvt8ycddpqrrnfh1zb5ldmlyorujykq08ucrxv9dyqn3wpox/g k7nq3w6q6+vt8lip9ia7neeu3bobnhaq371vq4ujqazoysxpah/rfhdhrtda6r/j f6ca8z28mliqdzxfpyrqkgpwjrsthz4bapunhz6dm9i5atnr4m4jpqefmesgglyd i3mk82codct8znk108boqjsal+cplarb53pitozm21pl1lvwzcq1ixvujjyjeosa 6sccakqfhf8cgq== =a56z -----end pgp message-----
6) when try decrypt message 2 "gpg: [don't know]: invalid packet"-messages , decryption fails:
$ gpg -vv --decrypt /tmp/ct.asc gpg: armor: begin pgp message version: bcpg v@release_name@ :pubkey enc packet: version 3, algo 1, keyid 46b51e4921b41e21 data: [2046 bits] gpg: armor header: gpg: public key 21b41e21 gpg: using subkey 21b41e21 instead of primary key 79cc322a need passphrase unlock secret key user: "foo bar (test key) <foo@bar.com>" gpg: using subkey 21b41e21 instead of primary key 79cc322a 2048-bit rsa key, id 21b41e21, created 2017-08-17 (main key id 79cc322a) gpg: no running gpg-agent - starting 1 gpg: public key encrypted data: dek :encrypted data packet: length: 57 mdc_method: 2 gpg: encrypted 2048-bit rsa key, id 21b41e21, created 2017-08-17 "foo bar (test key) <foo@bar.com>" gpg: aes256 encrypted data gpg: [don't know]: invalid packet (ctb=39) gpg: decryption okay gpg: [don't know]: invalid packet (ctb=44) $
the problem encryptkeybytes
implementation, encrypted pgp message gets built, writes plain-text message octets is, opposed literal data, hence protocol error during decryption attempt.
a correct implementation looks like:
private byte[] encryptkeybytes(string keyname, byte[] keybytes, byte[] pgpkey) throws generalsecurityexception { bytearrayoutputstream enckeybytes = new bytearrayoutputstream(keybytes.length); try (handle<securerandom> randomhandle = rngsupport.getrandom()) { jcepgpdataencryptorbuilder encryptorbuilder = new jcepgpdataencryptorbuilder(pgpencrypteddatagenerator.aes_256); encryptorbuilder.setwithintegritypacket(true); encryptorbuilder.setsecurerandom(randomhandle.getobject()); encryptorbuilder.setprovider("bc"); pgpencrypteddatagenerator encryptor = new pgpencrypteddatagenerator(encryptorbuilder); try { jcepublickeykeyencryptionmethodgenerator keyencryptionmethodgenerator = new jcepublickeykeyencryptionmethodgenerator(resolvepgppublickey(pgpkey)); keyencryptionmethodgenerator.setprovider("bc"); encryptor.addmethod(keyencryptionmethodgenerator); pgpliteraldatagenerator datagenerator = new pgpliteraldatagenerator(); byte[] data = bytessupport.encodehex(keybytes).getbytes(standardcharsets.utf_8); try ( outputstream ao = new armoredoutputstream(enckeybytes); outputstream eo = encryptor.open(ao, keybytes.length); outputstream go = datagenerator.open( eo, pgpliteraldata.utf8, keyname, data.length, new date())) { go.write(data); } } catch (servicerequestexception e) { throw e; } catch (exception e) { throw new generalsecurityexception("cannot perform pgp encryption on content key", e); } } return enckeybytes.tobytearray(); }
notice use of pgpliteraldatagenerator
, abstraction provides output stream on message bytes written to.
Comments
Post a Comment