java - 使用 Java 将外部 PKCS1 字节数组和证书添加到 CMS 容器

标签 java digital-signature bouncycastle pkcs#7

我们有创建 PKCS1 v2.1 数字签名的客户端应用程序(Applets 和 Silverlight)。数字签名被创建为 PKCS1,因为原始内容没有下载到客户端,只有内容的哈希被发送到客户端以节省带宽。

我们正在尝试创建一个 PKCS7/CMS 容器服务器端,based on the information from this post :

  • 读取证书并加载为 X509Certificate 类型
  • 将 PKCS1 签名读取为 base64 并加载为字节数组
  • 实例化新的 ASN1ObjectIdentifier 并设置 PKCS1 OID (1.2.840.113549.1.1)
  • 创建新的 CMSTypedData CMSProcessableByteArray,使用 asn1 对象和 signare byte[] 作为参数
  • 创建新的 CMSSignedGenerator 并添加证书
  • 使用 CMSTypedData 类型
  • 创建新的 CMSSignedData 作为分离签名

    但是,当进入第 5 步和第 6 步时,事情就中断了,因为 BC CMSSignedGenerator 和 CMSSignedData 类不支持在没有私钥的情况下添加签名者:

    CMS创建:
        // Add BC to environment
        Security.addProvider(new BouncyCastleProvider());
    
        // Read certificate and convert to X509Certificate
        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
        Path certPath = Paths.get("C:\\MyCertificate.cer");
        byte[] certData = Files.readAllBytes(certPath);
        InputStream in = new ByteArrayInputStream(certData);
        X509Certificate cert = (X509Certificate)certFactory.generateCertificate(in);
    
        // Add signer certificates to List and add them to store
        List<X509Certificate> certList = new ArrayList<X509Certificate>();
        certList.add(cert);
        Store certs = new JcaCertStore(certList);
    
        // Get signature in Base64, decode and convert to byte array
        // Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
        String signatureBase64 = "gjTbsD0vSOi6nMlRVbpTLRQ5j+g2h8iEH1DgQx93PDBuwzWT47urKxMAS+75dAhQrkreLt9TGZaDN85e5xEpIF12mK1G+AgCNc370I1bjxOvUU67IVxHkZ+IX8kzSiD2uNuQtk3IrwUqyL30TIo+LDAXmY1AQVZwXAaOYG4bXxI=";
        BASE64Decoder decoder = new BASE64Decoder();
        byte[] signatureByte = decoder.decodeBuffer(signatureBase64);
    
        // Instantiate new ANS1ObjectIdentifier to identify PKCS1 signature
        ASN1ObjectIdentifier asn1OidPkcs1 = new ASN1ObjectIdentifier("1.2.840.113549.1.1");
    
        // Table generator
        /*AttributeTable attrT = new AttributeTable();
        SimpleAttributeTableGenerator sAttrTGen = new SimpleAttributeTableGenerator();*/
    
        // Instantiate new CMSProcessable object
        CMSTypedData msg = new CMSProcessableByteArray(asn1OidPkcs1, signatureByte);
    
        // Instantiate new CMSSignedDataGenerator object
        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
    
        // ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").s
        gen.addCertificates(certs);
        CMSSignedData sigData = gen.generate(msg, false);
    
    
        // BASE64Encoder encoder = new BASE64Encoder();
        new File("C:\\MyCMS.p7s");
        FileOutputStream fileOuputStream = new FileOutputStream("C:\\Users\\gregwerner\\Documents\\Archivos\\miFirma.p7s"); 
        fileOuputStream.write(sigData.getEncoded());
        fileOuputStream.flush();
        fileOuputStream.close();
    
    }
    

    关于如何完成 CMS 容器的任何想法?也许使用 AttributeTable 为时间戳等添加多个 OID,但这似乎也不起作用。

    最佳答案

    看了这个引用项目https://code.google.com/p/j4ops/ 找到了答案.本指南也有很大帮助,尽管它专门处理使用 iText 的 PDF,这些 PDF 使用来自加密操作的 BC:http://itextpdf.com/book/digitalsignatures20130304.pdf .诀窍是通过实现使用例如 sign(byte[] toEncrypt) 方法的 Signer 接口(interface)将签名操作委托(delegate)给外部提供者(PKCS11、PKCS12 等)。这样,可以设置提供程序,然后只需调用 sign 方法并将有关如何签名的实现细节留给提供程序本身。

    Bouncy CaSTLe 使用带有 SignerInf 内部类的 CMSSignedDataGenerator 类来分别构建 CMS 容器和签名者信息。所以诀窍是构建一个不需要私钥的 SignerInf 对象,因为 sign() 操作应该委托(delegate)给提供者。特别是在使用智能卡时,私钥甚至可能不可用。此外,在对哈希进行签名和构建 CMS 容器时,需要考虑需要添加为签名属性和/或未签名属性的信息。所以这些是解决问题的基本步骤:

    // Build the items to encrypt, objects for method parameters would be obtained previously.
    byte[] toEncrypt = externalSignerInfoGenerator.getCmsBytesToSign(hash, 
                signingTime, 
                PKCSObjectIdentifiers.data, 
                x509Cert, 
                timeStampToken, 
                ocsp);
    // The externalSignerInfoGenerator.getCmsBytesToSign is a method from a re implemention of the 
    // SignerInf inner class from CMSSignedDataGenerator and is used to get a byte array from an 
    // org.bouncycastle.asn1.ASN1EncodableVector. To build the vector one should add attributes to
    // their corresponding OID's using the org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers interface,
    // for example:
    ASN1EncodableVector signedAttrVector = buildSignedAttributes (hash, signingTime, contentType,
    x509Cert, ocspResp);
    // This would call the buildSignedAttributes method to build the signed attributes vector
    ASN1EncodableVector signedAttrVector = new ASN1EncodableVector();
    // Add CMS attributes
    signedAttrVector.add (new Attribute(CMSAttributes.contentType, new DERSet (contentType)));
    signedAttrVector.add (new Attribute (CMSAttributes.signingTime, new DERSet(new Time (signingTime))));
    signedAttrVector.add(new Attribute(CMSAttributes.messageDigest, new DERSet(new DEROctetString(hash))));
    // Not all attributes are considered in BC's CMSAttributes interface, therefore one would have to add 
    // an additional step:
    signedAttrVector.add(buildOcspResponseAttribute(ocspResp));
    // This method would call buildOcspResponseAttribute to add the object as a PKCSObjectIdentifier
    protected Attribute buildOcspResponseAttribute (byte[] ocspResp) throws IOException, CMSException {
        return new Attribute (PKCSObjectIdentifiers.id_aa_ets_revocationRefs, 
        new DERSet(DERUtil.readDERObject(ocspResp)));
    }  
    
    // Call sign method from provider, such as PKCS11, PKCS12, etc.
    byte [] signature = getSignProvider().sign(toEncrypt);
    // Now build standard org.bouncycastle.cms.SignerInfoGenerator with hash, signed data 
    // and certificate to add to CMS, create attached or detached signature
    // create signed envelope
    CMSSignedData envdata = externalCMSSignedDataGenerator.generate(false);                
    byte[] enveloped = envdata.getEncoded();
    

    关于java - 使用 Java 将外部 PKCS1 字节数组和证书添加到 CMS 容器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22470156/

    相关文章:

    android - 为什么我在尝试使用 bouncycaSTLe 加密时在 android 中得到 NoSuchAlgorithmException?

    java - Spring Boot 多个配置文件数据源

    java - UIMA 鲁塔 : Creating new annotations by combining existing annotation's features in plain Java

    java.sql.BatchUpdateException : Table doesn't exist

    java - 并行测试中的控制台输出显示在不同的测试树中,而不是在

    java - 创建 PKCS#7 分离签名

    php - 在excel上使用php进行数字签名

    java - 如何在启用嵌入式时间戳和 LTV 的情况下签署 PDF?

    java - JSCEP-第三个参数类型错误。发现 : 'org.spongycaSTLe.pkcs.PKCS10CertificationRequest' , 需要: 'org.bouncycaSTLe.pkcs.PKCS10CertificationRequest'

    java - Java 中的 Flush PGP 加密 Bouncy CaSTLe 输出流