Search code examples
cryptographydigital-signaturebouncycastlepgpopenpgp

PGP Signed Message - Bouncycastle - Unable to extract full original One-Pass Signature with JcaPGPObjectFactory


I have a byte[], which holds a complete PGP Signed Message, consisting of: One-Pass Signature, Literal Data and Signature. Let's assume this is the representation of this message:

�
_�P��8��y_�(b__CONSOLE_C�}Lorem ipsum dolor sit amet�^_�}
    _�P��8��y_�_�ց��    v_0:��^_�V=�ʙ
̡W_"_3�=_�8�8�/��g[;_"e_��_�"6�_�pJ���;��

I want to read that message into corresponding objects, i.e. PGPOnePassSignature, byte[] and PGPSignature. In order to do so, I follow the usual mantra:

PGPOnePassSignatureList onePassSignatureList = (PGPOnePassSignatureList) pgpObjectFactory.nextObject();
onePassSignatureList.get(0);

This should give me One-Pass Signature. I then carry on reading the remainder of the message also using pgpObjectFactory of class JcaPGPObjectFactory. The problem is what I get is the original PGP Signed Message with incomplete One-Pass Signature:

�
_�P��8��y_Lorem ipsum dolor sit amet�^_�}
    _�P��8��y_�_�ց��    v_0:��^_�V=�ʙ
̡W_"_3�=_�8�8�/��g[;_"e_��_�"6�_�pJ���;��

It's clearly the above code snippet which is not capable of recovering One-Pass Signature in full.

You can easily see the part inlcuding _CONSOLE is missing. Byte array size is 137 recovered message vs 153 original message.

Is it possible to recover original message in full?

Library version:

org.bouncycastle:bcpg-jdk15on:1.66


Solution

  • It's impossible to analyze your message, because binary data handled as text is garbled and corrupted, and you don't show your code. But you are correct that a PGP signed message can consist of onepass-signature packet, literal data packet, and signature packet. Note that literal data is not just data, it contains some metadata also. In particular, the _CONSOLE you seem to focus on is a special value for the filename field, and not part of the actual data.

    BouncyCastle PGPObjectFactory returns this as PGPOnePassSignatureList PGPLiteralData (not byte[]) PGPSignatureList. Using this message signed on my test system:

    $ od -tx1 zgpgs0
    0000000 90 0d 03 00 08 11 81 e0 c1 00 62 85 ee 50 01 ac
    0000020 10 62 02 79 31 5f 44 29 59 66 6f 6f 62 61 72 0d
    0000040 0a 88 5e 04 00 11 08 00 06 05 02 5f 44 29 5a 00
    0000060 0a 09 10 81 e0 c1 00 62 85 ee 50 5b 9d 01 00 da
    0000100 6c d0 53 7b fb 21 1c df 5a 5d b1 05 d9 48 b6 37
    0000120 79 56 16 73 dc bf a8 4d 37 cd a9 af bb f0 43 01
    0000140 00 91 14 de 16 c5 41 b6 3d c4 f0 52 a9 1e d9 82
    0000160 65 3e 65 a4 a5 03 c2 55 f1 46 27 c3 aa c4 12 5c
    0000200 73
    0000201
    $ gpg --list-packets zgpgs0
    :onepass_sig packet: keyid 81E0C1006285EE50
            version 3, sigclass 0x00, digest 8, pubkey 17, last=1
    :literal data packet:
            mode b (62), created 1598302553, name="y1",
            raw data: 8 bytes
    :signature packet: algo 17, keyid 81E0C1006285EE50
            version 4, created 1598302554, md5len 0, sigclass 0x00
            digest algo 8, begin of digest 5b 9d
            hashed subpkt 2 len 4 (sig created 2020-08-24)
            subpkt 16 len 8 (issuer key ID 81E0C1006285EE50)
            data: [256 bits]
            data: [256 bits]
    $
    

    this code:

        JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(new FileInputStream(args[0]));
        Object t; while( (t = pgpFact.nextObject()) != null ){
            System.out.println (t.getClass().getName());
            if( t instanceof PGPLiteralData ){
                PGPLiteralData lit = (PGPLiteralData)t;
                System.out.println("format="+ lit.getFormat());
                System.out.println("filename="+lit.getFileName());
                System.out.println("modtime="+lit.getModificationTime());
                InputStream s = lit.getInputStream();
                byte[] buf = new byte[1000]; int len = s.read(buf);
                System.out.println("data:"+new String(buf,0,len,StandardCharsets.ISO_8859_1));
            }
        }
    

    gives this output:

    org.bouncycastle.openpgp.PGPOnePassSignatureList
    org.bouncycastle.openpgp.PGPLiteralData
    format=98
    filename=y1
    modtime=Mon Aug 24 20:55:53 UTC 2020
    data:foobar
    
    org.bouncycastle.openpgp.PGPSignatureList