PGP 签名消息 - Bouncycastle - 无法使用 JcaPGPObjectFactory 提取完整的原始一次性签名
PGP Signed Message - Bouncycastle - Unable to extract full original One-Pass Signature with JcaPGPObjectFactory
我有一个 byte[]
,它包含完整的 PGP 签名消息,包括:一次性签名、文字数据和签名。让我们假设这是此消息的表示形式:
�
_�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���;��
我想将该消息读入相应的对象,即 PGPOnePassSignature、byte[] 和 PGPSignature。为了做到这一点,我遵循通常的口头禅:
PGPOnePassSignatureList onePassSignatureList = (PGPOnePassSignatureList) pgpObjectFactory.nextObject();
onePassSignatureList.get(0);
这应该给我一次性签名。然后,我还使用 class JcaPGPObjectFactory
的 pgpObjectFactory
继续阅读消息的其余部分。问题是我得到的是带有不完整一次性签名的原始 PGP 签名消息:
�
_�P��8��y_Lorem ipsum dolor sit amet�^_�}
_�P��8��y_�_�ց�� v_0:��^_�V=�ʙ
̡W_"_3�=_�8�8�/��g[;_"e_��_�"6�_�pJ���;��
很明显上面的代码片段不能完全恢复单通签名。
您可以很容易地看到缺少 _CONSOLE
的部分。字节数组大小为 137 条恢复消息与 153 条原始消息。
是否可以完整恢复原始消息?
库版本:
org.bouncycastle:bcpg-jdk15on:1.66
无法分析您的消息,因为作为文本处理的二进制数据是乱码和损坏的,而且您没有显示您的代码。但是你是正确的,PGP 签名的消息 可以 由 onepass-signature 数据包、文字数据包和签名数据包组成。请注意,文字数据不仅仅是数据,它 contains some metadata also。特别是,您似乎关注的 _CONSOLE
是文件名字段的特殊值,而不是实际数据的一部分。
BouncyCastle PGPObjectFactory
returns 此为 PGPOnePassSignatureList PGPLiteralData (not byte[]) PGPSignatureList
。使用此消息在我的测试系统上签名:
$ 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]
$
此代码:
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));
}
}
给出此输出:
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
我错误地认为我遗漏的位是 One-Pass 签名的一部分。这确实是文字数据元数据的一部分,我能够使用以下代码片段读取和 re-construct 需要的文字数据:
private void parseMessage(JcaPGPObjectFactory pgpObjectFactory) throws Exception {
PGPLiteralData pgpLiteralData = (PGPLiteralData) pgpObjectFactory.nextObject();
String filename = pgpLiteralData.getFileName();
Date modificationTime = pgpLiteralData.getModificationTime();
char pgpLiteralDataType = (char) pgpLiteralData.getFormat();
InputStream pgpLiteralDataInputStream = pgpLiteralData.getInputStream();
byte[] message = Streams.readAll(pgpLiteralDataInputStream);
pgpLiteralDataInputStream.close();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
BCPGOutputStream onePassSignatureOutputStream = new BCPGOutputStream(byteArrayOutputStream);
PGPLiteralDataGenerator pgpLiteralDataGenerator = new PGPLiteralDataGenerator();
OutputStream outputStream = pgpLiteralDataGenerator.open(
onePassSignatureOutputStream,
pgpLiteralDataType,
filename,
message.length,
modificationTime
);
outputStream.write(message);
pgpLiteralDataGenerator.close();
outputStream.close();
onePassSignatureOutputStream.close();
byteArrayOutputStream.close();
literalData = byteArrayOutputStream.toByteArray();
}
我有一个 byte[]
,它包含完整的 PGP 签名消息,包括:一次性签名、文字数据和签名。让我们假设这是此消息的表示形式:
�
_�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���;��
我想将该消息读入相应的对象,即 PGPOnePassSignature、byte[] 和 PGPSignature。为了做到这一点,我遵循通常的口头禅:
PGPOnePassSignatureList onePassSignatureList = (PGPOnePassSignatureList) pgpObjectFactory.nextObject();
onePassSignatureList.get(0);
这应该给我一次性签名。然后,我还使用 class JcaPGPObjectFactory
的 pgpObjectFactory
继续阅读消息的其余部分。问题是我得到的是带有不完整一次性签名的原始 PGP 签名消息:
�
_�P��8��y_Lorem ipsum dolor sit amet�^_�}
_�P��8��y_�_�ց�� v_0:��^_�V=�ʙ
̡W_"_3�=_�8�8�/��g[;_"e_��_�"6�_�pJ���;��
很明显上面的代码片段不能完全恢复单通签名。
您可以很容易地看到缺少 _CONSOLE
的部分。字节数组大小为 137 条恢复消息与 153 条原始消息。
是否可以完整恢复原始消息?
库版本:
org.bouncycastle:bcpg-jdk15on:1.66
无法分析您的消息,因为作为文本处理的二进制数据是乱码和损坏的,而且您没有显示您的代码。但是你是正确的,PGP 签名的消息 可以 由 onepass-signature 数据包、文字数据包和签名数据包组成。请注意,文字数据不仅仅是数据,它 contains some metadata also。特别是,您似乎关注的 _CONSOLE
是文件名字段的特殊值,而不是实际数据的一部分。
BouncyCastle PGPObjectFactory
returns 此为 PGPOnePassSignatureList PGPLiteralData (not byte[]) PGPSignatureList
。使用此消息在我的测试系统上签名:
$ 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]
$
此代码:
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));
}
}
给出此输出:
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
我错误地认为我遗漏的位是 One-Pass 签名的一部分。这确实是文字数据元数据的一部分,我能够使用以下代码片段读取和 re-construct 需要的文字数据:
private void parseMessage(JcaPGPObjectFactory pgpObjectFactory) throws Exception {
PGPLiteralData pgpLiteralData = (PGPLiteralData) pgpObjectFactory.nextObject();
String filename = pgpLiteralData.getFileName();
Date modificationTime = pgpLiteralData.getModificationTime();
char pgpLiteralDataType = (char) pgpLiteralData.getFormat();
InputStream pgpLiteralDataInputStream = pgpLiteralData.getInputStream();
byte[] message = Streams.readAll(pgpLiteralDataInputStream);
pgpLiteralDataInputStream.close();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
BCPGOutputStream onePassSignatureOutputStream = new BCPGOutputStream(byteArrayOutputStream);
PGPLiteralDataGenerator pgpLiteralDataGenerator = new PGPLiteralDataGenerator();
OutputStream outputStream = pgpLiteralDataGenerator.open(
onePassSignatureOutputStream,
pgpLiteralDataType,
filename,
message.length,
modificationTime
);
outputStream.write(message);
pgpLiteralDataGenerator.close();
outputStream.close();
onePassSignatureOutputStream.close();
byteArrayOutputStream.close();
literalData = byteArrayOutputStream.toByteArray();
}