用Bouncycastle编码PKCS7

时间:2019-07-16 16:17:06

标签: java bouncycastle

我的代码依靠sun.security生成用于签名APK的PKCS7签名块:

    private static void writeSignatureBlock(byte[] signatureBytes, X509Certificate publicKey, OutputStream out)
            throws Exception {
        SignerInfo signerInfo = new SignerInfo(new X500Name(publicKey.getIssuerX500Principal().getName()),
                publicKey.getSerialNumber(), AlgorithmId.get("SHA1"), AlgorithmId.get("RSA"), signatureBytes);

        PKCS7 pkcs7 = new PKCS7(new AlgorithmId[] { AlgorithmId.get("SHA1") },
                new ContentInfo(ContentInfo.DATA_OID, null), new X509Certificate[] { publicKey },
                new SignerInfo[] { signerInfo });

        ByteArrayOutputStream o = new ByteArrayOutputStream();
        pkcs7.encodeSignedData(o);
        byte[] expected = o.toByteArray();
    }

由于新的Java版本不喜欢访问sun的内部类,因此我试图用Bouncy城​​堡代替它。

为了用充气城堡复制这种行为,我尝试了以下代码:

        List<java.security.cert.Certificate> certList = new ArrayList<java.security.cert.Certificate>();
        java.security.cert.Certificate certificate = publicKey;
        certList.add(certificate);
        JcaCertStore certs = new JcaCertStore(certList);
        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
        gen.addCertificates(certs);

        final byte[] signedHash = signatureBytes;

        ContentSigner nonSigner = new ContentSigner() {

            @Override
            public byte[] getSignature() {
                return signedHash;
            }

            @Override
            public OutputStream getOutputStream() {
                return new ByteArrayOutputStream();
            }

            @Override
            public AlgorithmIdentifier getAlgorithmIdentifier() {
                return new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1WithRSA");
            }

        };

        org.bouncycastle.asn1.x509.Certificate cert = org.bouncycastle.asn1.x509.Certificate
                .getInstance(ASN1Primitive.fromByteArray(certificate.getEncoded()));
        JcaSignerInfoGeneratorBuilder sigb = new JcaSignerInfoGeneratorBuilder(
                new JcaDigestCalculatorProviderBuilder().build());
        sigb.setDirectSignature(true);
        gen.addSignerInfoGenerator(sigb.build(nonSigner, new X509CertificateHolder(cert)));
        CMSProcessableByteArray msg = new CMSProcessableByteArray(new byte[0]);

        CMSSignedData signedData = gen.generate(msg, false);
        byte[] bouncyCastle = signedData.getEncoded();

生成的输出具有不同的长度并且完全不同:

Expected length: 1489
BouncyCastle length: 1491```

Why do these outputs differ in length (if only the content would be different I'd assume I supplied the data in a wrong format)?

2 个答案:

答案 0 :(得分:1)

唯一的区别是Bouncycastle对证书主题中某些字符串的编码与sun。*类使用的编码稍有不同。 Bouncycastle更符合RFC 3280,但RFC5280稍微放宽了要求。两种编码都可以。

答案 1 :(得分:0)

BouncyCastle的替代品是Oracle的Osdt CERT库,可从Maven-Repository获得。 Gradle依赖性:

// https://mvnrepository.com/artifact/com.oracle.ojdbc/osdt_core
implementation "com.oracle.ojdbc:osdt_cert:19.3.0.0" 

Oracle库比Bouncy城​​堡更易于使用IMO。从现有的X509格式中产生PKCS7 DER格式大约需要3条代码行:

import oracle.security.crypto.cert.PKCS7;
import oracle.security.crypto.cert.X509;

        byte[] certDER = <retrieve the X.509 certificate as DER encoded byte array>
        X509 x509 = new X509(certAsDER);
        PKCS7 pkcs7 = new PKCS7(x509);
        byte pkcs7DER = pkcs7.getEncoded();