使用BouncyCastle进行OpenPGP签名时会给出不完整的消息

时间:2014-05-31 01:43:43

标签: java php android bouncycastle openpgp

我正在尝试实现一个字节数组的OpenPGP签名(也可能是一个字符串,因为源是JSON而不应该很重要)但是我从Java BouncyCastle实现得到了错误的输出。我正在测试,我想模仿gnupg的--sign选项。这是我从示例用法中修改的代码:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.SignatureException;
import java.util.Date;
import java.util.Iterator;

import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;

public class OpenPGP {

private static PGPSecretKey readSecretKey(final InputStream input)
        throws IOException, PGPException {
    final PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
            PGPUtil.getDecoderStream(input));

    final Iterator keyRingIter = pgpSec.getKeyRings();
    while (keyRingIter.hasNext()) {
        final PGPSecretKeyRing keyRing = (PGPSecretKeyRing) keyRingIter
                .next();

        final Iterator keyIter = keyRing.getSecretKeys();
        while (keyIter.hasNext()) {
            final PGPSecretKey key = (PGPSecretKey) keyIter.next();

            if (key.isSigningKey())
                return key;
        }
    }

    throw new IllegalArgumentException(
            "Can't find signing key in key ring.");
}

public static byte[] sign(final byte[] data, final String key)
        throws IOException, PGPException, SignatureException {
    PGPSecretKey pgpSec;

    pgpSec = readSecretKey(new ByteArrayInputStream(key.getBytes()));

    PGPPrivateKey pgpPrivKey;

    pgpPrivKey = pgpSec
            .extractPrivateKey(new JcePBESecretKeyDecryptorBuilder()
                .setProvider("BC").build("".toCharArray()));

    final PGPSignatureGenerator sGen = new PGPSignatureGenerator(
            new JcaPGPContentSignerBuilder(pgpSec.getPublicKey()
                    .getAlgorithm(), PGPUtil.SHA1).setProvider("BC"));

    sGen.init(PGPSignature.BINARY_DOCUMENT, pgpPrivKey);

    @SuppressWarnings("rawtypes")
    final Iterator it = pgpSec.getPublicKey().getUserIDs();
    if (it.hasNext()) {
        final PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();

        spGen.setSignerUserID(false, (String) it.next());
        sGen.setHashedSubpackets(spGen.generate());
    }

    final PGPCompressedDataGenerator compressDataGenerator = new PGPCompressedDataGenerator(
            PGPCompressedData.ZLIB);
    final ByteArrayOutputStream out = new ByteArrayOutputStream();
    final BCPGOutputStream bOut = new BCPGOutputStream(
            compressDataGenerator.open(out));

    sGen.generateOnePassVersion(false).encode(bOut);

    final PGPLiteralDataGenerator lGen = new PGPLiteralDataGenerator();
    OutputStream lOut;
    lOut = lGen.open(bOut, PGPLiteralData.BINARY, "score.dat", new Date(),
            data);

    final InputStream fIn = new ByteArrayInputStream(data);
    int ch;

    while ((ch = fIn.read()) >= 0) {
        lOut.write(ch);
        sGen.update((byte) ch);
    }

    lGen.close();

    sGen.generate().encode(bOut);
    compressDataGenerator.close();
    return out.toByteArray();
}
}

现在,sign函数的结果是base64编码,然后对url保持安全,并且似乎具有适当的长度,但在解码时消息无效。以下是解码内容的示例:

0:
    version: 3
    signature_type: 0
    hash_algorithm: 2
    key_algorithm: 1
    key_id: 4F65C291695F46BA
    nested: 0
    tag: 4
    size: <NULL>
    data: <NULL>

这是一个非常印刷的PHP对象,从我的旧版php openpgp lib的端口到更新的标准,它在github @ mudhairless / openpgp上,是

的结果
\OpenPGP\Message::parse($message);

key_id有正确但消息与gnupg生成的消息有很大不同,缺少实际数据:

0:
    0:
        version: 3
        signature_type: 0
        hash_algorithm: 2
        key_algorithm: 1
        key_id: 4F65C291695F46BA
        nested: 1
        tag: 4
        size: <NULL>
        data: <NULL>

    1:
        format: b
        filename: data.txt
        timestamp: 1400821038
        tag: 11
        size: 12
        data: 1234567890

    2:
        version: 4
        signature_type: 0
        hash_algorithm: 2
        key_algorithm: 1
        hashed_subpackets:
            0:
                tag: 2
                size: <NULL>
                data: 1400821038


        unhashed_subpackets:
            0:
                tag: 16
                size: <NULL>
                data: 4F65C291695F46BA


        hash_head: 51977
        trailer: S~�.�
        tag: 2
        size: <NULL>
        data:
            0: <long string of random characters>

我已经在这个问题上做了很多搜索,没有运气,所以任何助理都会受到赞赏。

0 个答案:

没有答案