Bouncycastle 签名数据消息中的附加八位字节字符串

Additional Octet String in Bouncycastle signed data message

我正在创建国家签名认证机构主列表签名数据,但在成功创建之后,我在签名消息中看到了两个八位字节字符串,而不是一个。请检查源代码和输出文件

CMS 使用 BC

// load master list signer key  
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(FileTools.readFiletoBuffer("CSCAFiles/MLS.key"));
        ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(spec.getEncoded()));
        PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject());
        String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId();

        PrivateKey MLSkey = KeyFactory.getInstance(algOid).generatePrivate(spec);

              // load master list signing cert
        X509Certificate MLSCert = CertTools.getCertfromByteArray(FileTools.readFiletoBuffer("CSCAFiles/MLSCert.der"),
                X509Certificate.class);

        // create master list
        X509CertificateHolder x509CertificateHolder = new X509CertificateHolder(MLSCert.getEncoded());

        String pubkeyAlgorithm = MLSCert.getPublicKey().getAlgorithm();
        String certAlgorithm = "";

        if (pubkeyAlgorithm.equals("RSA")) {
            certAlgorithm = "SHA256WITHRSA";
        } else if (pubkeyAlgorithm.equals("ECDSA")) {
            certAlgorithm = "SHA256WITHECDSA";
        } else if (pubkeyAlgorithm.equals("DSA")) {
            certAlgorithm = "SHA256WITHDSA";
        } else {
            certAlgorithm = "SHA256WITHRSA";
        }

        // csca cert
        X509Certificate cscaCert = CertTools.getCertfromByteArray(FileTools.readFiletoBuffer("CSCAFiles/CSCACert.der"),
                X509Certificate.class);

        // create master list content
        JcaX509CertificateHolder[] hollist = CertTools
                .convertToX509CertificateHolder(new X509Certificate[] { cscaCert });

        org.bouncycastle.asn1.x509.Certificate certList[] = new org.bouncycastle.asn1.x509.Certificate[1];
        certList[0] = hollist[0].toASN1Structure();
        //certList[1] = hollist[0].toASN1Structure();

        CscaMasterList ml = new CscaMasterList(certList);

        System.out.println(">>>>> encoded data : " + new java.math.BigInteger(1, ml.getEncoded()).toString(16));

        CMSTypedData message = new CMSProcessableByteArray(ICAOObjectIdentifiers.id_icao_cscaMasterList,
                ml.toASN1Primitive().getEncoded());   

        AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(certAlgorithm);

        AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
        AsymmetricKeyParameter privateKeyParameter = PrivateKeyFactory.createKey(MLSkey.getEncoded());

        BcContentSignerBuilder signBuilder = null;
        if (pubkeyAlgorithm.equals("RSA")) {
            signBuilder = new BcRSAContentSignerBuilder(sigAlgId, digAlgId);
        } else if (pubkeyAlgorithm.equals("EC")) {
            signBuilder = new BcECContentSignerBuilder(sigAlgId, digAlgId);
        } else if (pubkeyAlgorithm.equals("DSA")) {
            signBuilder = new BcDSAContentSignerBuilder(sigAlgId, digAlgId);
        } else {
            signBuilder = new BcRSAContentSignerBuilder(sigAlgId, digAlgId);
        }

        ContentSigner signer = signBuilder.build(privateKeyParameter);

        SignerInfoGeneratorBuilder signerInfoGeneratorBuilder = new SignerInfoGeneratorBuilder(
                new BcDigestCalculatorProvider());
        SignerInfoGenerator infoGenerator = signerInfoGeneratorBuilder.build(signer, x509CertificateHolder);

        CMSSignedDataGenerator dataGenerator = new CMSSignedDataGenerator();
        dataGenerator.addSignerInfoGenerator(infoGenerator);

        dataGenerator.addCertificate(x509CertificateHolder);
        dataGenerator.addCertificate(new X509CertificateHolder(FileTools.readFiletoBuffer("CSCAFiles/CSCACert.der")));

        CMSSignedData signedData = dataGenerator.generate(message, true);

        System.out.println(">>>>> ML data : " + new java.math.BigInteger(1, signedData.getEncoded()).toString(16));     

>>>>>>编码数据:

308202b2020100318202ab308202a730820210a003020102021024c2de802df5eb9a5ffc421546f39635300d06092a864886f70d01010b0500303f3111300f06035504030c084353434154657374310d300b060355040b0c0464656d6f310e300c060355040a0c05647265616d310b3009060355040613024b52301e170d3139313033313032343830305a170d3334313032373032353830305a303f3111300f06035504030c084353434154657374310d300b060355040b0c0464656d6f310e300c060355040a0c05647265616d310b3009060355040613024b5230819f300d06092a864886f70d010101050003818d0030818902818100b23a5a61e46f522c5d0e43325b9485065cabb6db0d88e32bb6ceb0d32ebb740485458e1e4e1cdfe317829e3d785f2de66d0f270120919f46d759a64e944c3579661cf850714af2fd2088f5dc91b1838571802f7d4d7b61219da296a8ae7f55362aa28836cea220635660b0b8c8f3cd596234bbe5ccb04873aa3e301c05f86eb70203010001a381a33081a0300f0603551d130101ff040530030101ff300e0603551d0f0101ff040403020106302b0603551d1004243022800f32303139313033313032353830305a810f32303234313033313032353830305a301d0603551d0e0416041456d7d0f4db824006e0dc864129ddd6991b36c467301f0603551d2304183016801456d7d0f4db824006e0dc864129ddd6991b36c46730100603551d200409300730050603290101300d06092a864886f70d01010b05000381810027ebd5fe0fd6e8df20e485d07f5b3420cb9719d2bad3dec7db5611cb690087d26f398b4849dd7d99a2abf3a2a71f7b108941a12df1a8a9c42a9c48a0750fded7a5ba38f692780cb6663febfb28d879bb53155113d773b8349e0c658c4ddc5c3d1ec7f8d35d4e72223efaacfdc53698e9157ac4590c708f91346c286d1b7e4fe4

>>>>> 机器学习数据:

308006092a864886f70d010702a0803080020103310f300d0609608648016503040201050030800606678108010102a0802480048202b6308202b2020100318202ab308202a730820210a003020102021024c2de802df5eb9a5ffc421546f39635300d06092a864886f70d01010b0500303f3111300f06035504030c084353434154657374310d300b060355040b0c0464656d6f310e300c060355040a0c05647265616d310b3009060355040613024b52301e170d3139313033313032343830305a170d3334313032373032353830305a303f3111300f06035504030c084353434154657374310d300b060355040b0c0464656d6f310e300c060355040a0c05647265616d310b3009060355040613024b5230819f300d06092a864886f70d010101050003818d0030818902818100b23a5a61e46f522c5d0e43325b9485065cabb6db0d88e32bb6ceb0d32ebb740485458e1e4e1cdfe317829e3d785f2de66d0f270120919f46d759a64e944c3579661cf850714af2fd2088f5dc91b1838571802f7d4d7b61219da296a8ae7f55362aa28836cea220635660b0b8c8f3cd596234bbe5ccb04873aa3e301c05f86eb70203010001a381a33081a0300f0603551d130101ff040530030101ff300e0603551d0f0101ff040403020106302b0603551d1004243022800f32303139313033313032353830305a810f32303234313033313032353830305a301d0603551d0e0416041456d7d0f4db824006e0dc864129ddd6991b36c467301f0603551d2304183016801456d7d0f4db824006e0dc864129ddd6991b36c46730100603551d200409300730050603290101300d06092a864886f70d01010b05000381810027ebd5fe0fd6e8df20e485d07f5b3420cb9719d2bad3dec7db5611cb690087d26f398b4849dd7d99a2abf3a2a71f7b108941a12df1a8a9c42a9c48a0750fded7a5ba38f692780cb6663febfb28d879bb53155113d773b8349e0c658c4ddc5c3d1ec7f8d35d4e72223efaacfdc53698e9157ac4590c708f91346c286d1b7e4fe4000000000000a0803082044c308203b5a00302010202142710e65ec4b56813da4e0e1e0223b7d485aa64d9300d06092a864886f70d01010b0500303f3111300f06035504030c084353434154657374310d300b060355040b0c0464656d6f310e300c060355040a0c05647265616d310b3009060355040613024b52301e170d3139313033313032343830325a170d3239313032383032343830325a304f3121301f06035504030c184d61737465724c6973745369676e65724365727454657374310d300b060355040b0c0464656d6f310e300c060355040a0c05647265616d310b3009060355040613024b5230820122300d06092a864886f70d01010105000382010f003082010a0282010100cb4ef691bb600dd6f514569ed79853d56f053257ddcaf11cd7323824499d011d5cc7652e50789977922c76d8c0d937426ce81e74b5b4f52a419481aab713114916859c23a53ac3937ac22a73c1f31b281f74c5c31574ab2f5f270d31667ee3fe9f69d231957dd33a4c97a20c19c0cabcfddc467786c6be42c6fef962c00f44d17e5d50c39443a14d9f44baf89974e5b5620381e4c2096008e7994f4eb1ee70689fce105a22dc1316d4fea5753673c68cc1fc9b6636b088408672bc57ccbc3f2cd74855939c1ab6bf7cb9b7a56f9ace27c92abf431381659ce9d99326f8fd485a24306b7514b5c5903bf417f8a460aec2893f7913c184638641804891836844cb0203010001a38201af308201ab301f0603551d2304183016801456d7d0f4db824006e0dc864129ddd6991b36c46730818e06082b06010505070101048181307f302006082b060105050730028614687474703a2f2f6578616d706c652e636f6d2f30302006082b060105050730028614687474703a2f2f6578616d706c652e636f6d2f31303906082b06010505073001862d687474703a2f2f63612d646566696e65642e6f6373702e736572766963652e6c6f6361746f722e75726c2e7377302c0603551d110425302381214d61737465724c6973745369676e657243657274546573744064656d6f2e636f6d30140603551d250101ff040a3008060667810801010330570603551d1f0450304e3025a023a021861f687474703a2f2f7777772e64656d6f2e6f72672f6261722f6261722e63726c3025a023a021861f687474703a2f2f7777772e64656d6f2e6f72672f666f6f2f666f6f2e63726c301d0603551d0e041604148d2713fb3995e04b0dda8a02a1a3d9141ba01914302b0603551d1004243022800f32303139313033313032343830325a810f32303230303133313032343830325a300e0603551d0f0101ff040403020780300d06092a864886f70d01010b050003818100004f1211d31b962c3f7060ca29af5895593aaaecd4a0ec4657fc7e4e54d8f427771c0ff58db6ee5d11fe74074f0838cfbc541fd4a793d10baa4bd0b8b5a4aae8eb6eb330dafcae2f88f9f94981ccc8983565724365ea8dc793bf80b131ffa2f29e9aa4943597fcce72b0ddf2228c942ec4be3a3490935846cdef8664bf0bc5c1308202a730820210a003020102021024c2de802df5eb9a5ffc421546f39635300d06092a864886f70d01010b0500303f3111300f06035504030c084353434154657374310d300b060355040b0c0464656d6f310e300c060355040a0c05647265616d310b3009060355040613024b52301e170d3139313033313032343830305a170d3334313032373032353830305a303f3111300f06035504030c084353434154657374310d300b060355040b0c0464656d6f310e300c060355040a0c05647265616d310b3009060355040613024b5230819f300d06092a864886f70d010101050003818d0030818902818100b23a5a61e46f522c5d0e43325b9485065cabb6db0d88e32bb6ceb0d32ebb740485458e1e4e1cdfe317829e3d785f2de66d0f270120919f46d759a64e944c3579661cf850714af2fd2088f5dc91b1838571802f7d4d7b61219da296a8ae7f55362aa28836cea220635660b0b8c8f3cd596234bbe5ccb04873aa3e301c05f86eb70203010001a381a33081a0300f0603551d130101ff040530030101ff300e0603551d0f0101ff040403020106302b0603551d1004243022800f32303139313033313032353830305a810f32303234313033313032353830305a301d0603551d0e0416041456d7d0f4db824006e0dc864129ddd6991b36c467301f0603551d2304183016801456d7d0f4db824006e0dc864129ddd6991b36c46730100603551d200409300730050603290101300d06092a864886f70d01010b05000381810027ebd5fe0fd6e8df20e485d07f5b3420cb9719d2bad3dec7db5611cb690087d26f398b4849dd7d99a2abf3a2a71f7b108941a12df1a8a9c42a9c48a0750fded7a5ba38f692780cb6663febfb28d879bb53155113d773b8349e0c658c4ddc5c3d1ec7f8d35d4e72223efaacfdc53698e9157ac4590c708f91346c286d1b7e4fe400003182021a308202160201013057303f3111300f06035504030c084353434154657374310d300b060355040b0c0464656d6f310e300c060355040a0c05647265616d310b3009060355040613024b5202142710e65ec4b56813da4e0e1e0223b7d485aa64d9300d06096086480165030402010500a08195301506092a864886f70d01090331080606678108010102301c06092a864886f70d010905310f170d3139313130313030323033365a302d06092a864886f70d0109343120301e300d06096086480165030402010500a10d06092a864886f70d01010b0500302f06092a864886f70d010904312204202784558fbdeca49e7f905b161924b1b8306df980ed191907f1469ab46b379953300d06092a864886f70d01010b0500048201005d1ffa5ce0cef0304899b192eb873a8d9d656b28ed7fc1a4dfdeea9cc9325a9a228b9a7fb0962358c29d98f6a2b679e40fe5b918f615ecf12624764439c5e5b81665ceaca0c718e6b63bb57b8e8ea0b0579dc0b32923c9add0191b0864ff20b3245175ab7231e10c39d7867d7df1e712b81ba4cc4e0f300ede5c66ec73af6a6a3caf70358844a71c49d7b355d0e584d622c46dbdfc9ecb9a7fdea03a1a3e891f78c57420d2260b317c896312c982885d8f9438eddcef0c9ae19a775f0a7725440a6d4f0ad67daf2c8095e3b6668169e79f2e23b34876c2c8daf622bf0d0ec31cbd9259929dce951050564a7dcf92ad2f30d7945c2500f89653d3cfa2f4f7cc1a000000000000

错误的主列表(使用 BC)

预期主列表(使用sun.security.pkcs)

----PKCS#7 使用 sun----

    // load master list signer key
        /* Check to see if this is in an EncryptedPrivateKeyInfo structure. */
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(FileTools.readFiletoBuffer("CSCAFiles/MLS.key"));
        /*
         * Now it's in a PKCS#8 PrivateKeyInfo structure. Read its Algorithm OID
         * and use that to construct a KeyFactory.
         */
        ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(spec.getEncoded()));
        PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject());
        String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId();

        PrivateKey MLSkey = KeyFactory.getInstance(algOid).generatePrivate(spec);

        X509Certificate MLSCert = CertTools.getCertfromByteArray(FileTools.readFiletoBuffer("CSCAFiles/MLSCert.der"),
                X509Certificate.class);

        // create the output stream
        ByteArrayOutputStream bOut = new ByteArrayOutputStream();

        bOut.write(FileTools.readFiletoBuffer("CSCAFiles/CSCACert.der"));
        bOut.write(FileTools.readFiletoBuffer("CSCAFiles/DSCert.der"));
        bOut.write(FileTools.readFiletoBuffer("CSCAFiles/DLSCert.der"));

        bOut.close();

        // create ml
        // set up the generator
        ////////////////////////
        X509CertificateHolder x509CertificateHolder = new X509CertificateHolder(MLSCert.getEncoded());

        String pubkeyAlgorithm = MLSCert.getPublicKey().getAlgorithm();
        String certAlgorithm = "";

        if (pubkeyAlgorithm.equals("RSA")) {
            certAlgorithm = "SHA256WITHRSA";
        } else if (pubkeyAlgorithm.equals("ECDSA")) {
            certAlgorithm = "SHA256WITHECDSA";
        } else if (pubkeyAlgorithm.equals("DSA")) {
            certAlgorithm = "SHA256WITHDSA";
        } else {
            certAlgorithm = "SHA256WITHRSA";
        }

        // csca cert
        X509Certificate cscaCert = CertTools.getCertfromByteArray(FileTools.readFiletoBuffer("CSCAFiles/CSCACert.der"),
                X509Certificate.class);

        // create master list content
        JcaX509CertificateHolder[] hollist = CertTools
                .convertToX509CertificateHolder(new X509Certificate[] { cscaCert });

        org.bouncycastle.asn1.x509.Certificate certList[] = new org.bouncycastle.asn1.x509.Certificate[2];
        certList[0] = hollist[0].toASN1Structure();
        certList[1] = hollist[0].toASN1Structure();

        CscaMasterList ml = new CscaMasterList(certList);

        ///////////////////////

        // Data to sign
        byte[] dataToSign = ml.getEncoded();

        // compute signature:
        Signature signature = Signature.getInstance(certAlgorithm);
        signature.initSign(MLSkey);
        signature.update(dataToSign);
        byte[] signedData = signature.sign();

        // load X500Name
        sun.security.x509.X500Name xName = new sun.security.x509.X500Name(cscaCert.getSubjectDN().getName());
        // load serial number
        BigInteger serial = hollist[0].getSerialNumber();
        // laod digest algorithm
        AlgorithmId digestAlgorithmId = new AlgorithmId(AlgorithmId.SHA_oid);
        // load signing algorithm
        AlgorithmId signAlgorithmId = new AlgorithmId(AlgorithmId.RSAEncryption_oid);

        // Create SignerInfo:
        SignerInfo sInfo = new SignerInfo(xName, serial, digestAlgorithmId, signAlgorithmId, signedData);
        // Create ContentInfo:
        ContentInfo cInfo = new ContentInfo(new ObjectIdentifier("2.23.136.1.1.2"),
                new DerValue(DerValue.tag_OctetString, dataToSign));
        // Create PKCS7 Signed data
        PKCS7 p7 = new PKCS7(new AlgorithmId[] { digestAlgorithmId }, cInfo,
                new java.security.cert.X509Certificate[] { cscaCert }, new SignerInfo[] { sInfo });
        // Write PKCS7 to bYteArray
        ByteArrayOutputStream bOut1 = new DerOutputStream();
        p7.encodeSignedData(bOut1);

        System.out.println(">>>>> ML data : " + new java.math.BigInteger(1, bOut1.toByteArray()).toString(16));         

更正! 虽然 PKCS7 和 CMS 的创建方式有所不同,但根据评论,此处 的区别在于 Bouncy使用了 'constructed' 编码(不定长度 ,虽然这在这里不太重要),参见 wikipedia。 ASN.1 中的一项 'constructed' 项目有一个标签长度前缀,并且 附加 标签长度前缀用于构成一个或多个元素构建项目的价值。

$ openssl asn1parse <58653210.cms -inform der -i
    0:d=0  hl=2 l=inf  cons: SEQUENCE
    2:d=1  hl=2 l=   9 prim:  OBJECT            :pkcs7-signedData
   13:d=1  hl=2 l=inf  cons:  cont [ 0 ]
   15:d=2  hl=2 l=inf  cons:   SEQUENCE
   17:d=3  hl=2 l=   1 prim:    INTEGER           :03
   20:d=3  hl=2 l=  15 cons:    SET
   22:d=4  hl=2 l=  13 cons:     SEQUENCE
   24:d=5  hl=2 l=   9 prim:      OBJECT            :sha256
   35:d=5  hl=2 l=   0 prim:      NULL
   37:d=3  hl=2 l=inf  cons:    SEQUENCE
   39:d=4  hl=2 l=   6 prim:     OBJECT            :2.23.136.1.1.2
   47:d=4  hl=2 l=inf  cons:     cont [ 0 ]
   49:d=5  hl=2 l=inf  cons:      OCTET STRING
   51:d=6  hl=4 l= 694 prim:       OCTET STRING      [HEX DUMP]:308202B202010031 
[snip rest of (correct) body]
  749:d=6  hl=2 l=   0 prim:       EOC
  751:d=5  hl=2 l=   0 prim:      EOC
  753:d=4  hl=2 l=   0 prim:     EOC
  755:d=3  hl=2 l=inf  cons:    cont [ 0 ]
  757:d=4  hl=4 l=1100 cons:     SEQUENCE 
[snip certificate]
 1861:d=4  hl=4 l= 679 cons:     SEQUENCE 
[snip certificate]
 2544:d=4  hl=2 l=   0 prim:     EOC
 2546:d=3  hl=4 l= 538 cons:    SET
 2550:d=4  hl=4 l= 534 cons:     SEQUENCE 
[snip SignerInfo]
 3088:d=3  hl=2 l=   0 prim:    EOC
 3090:d=2  hl=2 l=   0 prim:   EOC
 3092:d=1  hl=2 l=   0 prim:  EOC

如果我们查看偏移量 49(十六进制)处的实际字节,我们会发现

24 80 -- tag for OCTET STRING _constructed_, length indefinite
-- a constructed item contains (consists of) one or more following elements
04 82 02 B6 -- tag for OCTET STRING _primitive_, length 02B6
-- this is the only element within the constructed item

并且在 49+6+0x2b6=749 我们发现 00 00 即 'end of contents',终止构造的项目。因此,外部构造的 OCTET STRING 实际上由一个元素组成,该元素是原始 OCTET STRING。

据推测,Bouncy 对内容和 encapContentInfo 以及包含它们的外部 SignedData 使用了 constructed+indefinite,以允许它们大于内存中的容量——在加密中通常称为 'online' (一个奇怪的术语)或更明智的 'streaming'。但它也将此用于证书和 signerInfos 组件,它们从来没有这个问题——也许只是为了方便?

太阳类估计没有这样做,虽然你没有post数据证实(或者使用的代码可以看一下)。

这实际上是 BER,原因有二; DER不允许或者构造OCTET STRING 或者不定长度。


(原创但不适用的答案供参考)

这是 PKCS7(RSALabs 的原始版本)和 CMS(其 IETF 化后继版本)之间的一个小差异,很容易被忽略。它实际上是在 1999 年的 RFC 2630 中引入的,但直到 2002 年的 RFC 3369 sec 5.2.1 才明确记录。请注意 version(SignedData 开头的 Integer,即外部 Context-0 EXPLICIT SEQUENCE)也不同;在 Bouncy 创建的 CMS 版本中它是 3,而在 sun 中(你没有显示源代码,但显然是 PKCS7)它是 1。PKCS7 只定义版本最多 1,所以任何(结构)版本> 1 必须是 CMS。