复制 OpenSSL 命令以在 Java 中签署文件

Replicate OpenSSL command to sign a file in Java

我需要签署一个文件,到目前为止我已经使用了工作正常的 openssl 命令(文件已签署和验证)。

openssl smime -sign -in unsigned.mobileconfig -out signed.mobileconfig -signer myCrtFile.crt -inkey myKeyFile.key -certfile bundleCertificate.crt -outform der -nodetach

但现在我需要在运行时执行此操作,因此我需要以编程方式对文件进行签名。我正在使用 BouncyCastle 但我愿意切换到另一个库。

我不擅长证书,更不擅长 BouncyCastle。这是我带来的。

openssl命令对应的文件如下:

myCrtFile.crt -> signerCertHolder
myKeyFile.key -> privateKeyInfo
bundleCertificate.crt -> certificateHolder

public byte[] sign(String message) throws IOException, CMSException, OperatorCreationException, CertificateEncodingException, MessagingException, CertificateException {
    Security.addProvider(new BouncyCastleProvider());

    PrivateKeyInfo privateKeyInfo = loadInKey();
    X509CertificateHolder signerCertHolder = loadSigner();
    X509CertificateHolder certificateCertHolder = loadCertfile();

    PrivateKey inKey = new JcaPEMKeyConverter().getPrivateKey(privateKeyInfo);

    JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter();
    X509Certificate signer = certificateConverter.getCertificate(signerCertHolder);
    X509Certificate certificate = certificateConverter.getCertificate(certificateCertHolder);

    List<X509Certificate> certificateList = new ArrayList();
    certificateList.add(signer);
    certificateList.add(certificate);

    Store<?> certs = new JcaCertStore(certificateList);
    ContentSigner sha1signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(inKey);
    JcaSignerInfoGeneratorBuilder jcaSignerInfoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build());

    CMSSignedDataGenerator signGen = new CMSSignedDataGenerator();
    signGen.addSignerInfoGenerator(jcaSignerInfoGeneratorBuilder.build(sha1signer, certificate));
    signGen.addCertificates(certs);

    CMSTypedData content = new CMSProcessableByteArray(message.getBytes());
    CMSSignedData signedData = signGen.generate(content, false);
    byte[] signeddata = signedData.getEncoded();

    return signeddata;
} 

然后我将 byte[] 保存到文件中。当我打开文件(它是一个 MDM mobileConfig 文件)时,它显示 "The file is signed but could not be verified"。我觉得我接近解决方案,但我不知道我做错了什么。

有人可以帮我弄清楚我缺少什么来验证文件吗?

PS;证书来自 SSL 证书 (GoDaddy),bundleCertificate.crt 是 GoDaddy 捆绑证书。

我成功签署了文件。不出所料,我已经接近最终解决方案了。这是完整的代码。

public byte[] signMobileConfig(byte[] mobileconfig) 
        throws CertificateEncodingException, PEMException, FileNotFoundException, IOException, CertificateException, OperatorCreationException, CMSException {
    Security.addProvider(new BouncyCastleProvider());

    X509CertificateHolder caCertificate = loadCertfile();

    JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter();
    X509Certificate serverCertificate = certificateConverter.getCertificate(loadSigner());

    PrivateKeyInfo privateKeyInfo = loadInKey();
    PrivateKey inKey = new JcaPEMKeyConverter().getPrivateKey(privateKeyInfo);
    ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(inKey);

    CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
    JcaDigestCalculatorProviderBuilder digestProviderBuilder = new JcaDigestCalculatorProviderBuilder().setProvider("BC");
    JcaSignerInfoGeneratorBuilder generatotBuilder = new JcaSignerInfoGeneratorBuilder(digestProviderBuilder.build());

    generator.addSignerInfoGenerator(generatotBuilder.build(sha1Signer, serverCertificate));
    generator.addCertificate(new X509CertificateHolder(serverCertificate.getEncoded()));
    generator.addCertificate(new X509CertificateHolder(caCertificate.getEncoded()));

    CMSProcessableByteArray bytes = new CMSProcessableByteArray(mobileconfig);
    CMSSignedData signedData = generator.generate(bytes, true);

    return signedData.getEncoded();
}

这是我加载文件的方式:

public X509CertificateHolder loadSigner() throws FileNotFoundException, IOException {
    InputStream inputStream = externalResourcesFacade.getResourceAsStream("path/to/.crt");
    PEMParser parser = new PEMParser(new InputStreamReader(inputStream));
    return (X509CertificateHolder) parser.readObject();
}

public PrivateKeyInfo loadInKey() throws FileNotFoundException, IOException {
    InputStream inputStream = externalResourcesFacade.getResourceAsStream("path/to/.key");
    PEMParser parser = new PEMParser(new InputStreamReader(inputStream));
    return (PrivateKeyInfo) parser.readObject();
}

public X509CertificateHolder loadCertfile() throws FileNotFoundException, IOException {
    InputStream inputStream = externalResourcesFacade.getResourceAsStream("path/to/.crt");
    PEMParser parser = new PEMParser(new InputStreamReader(inputStream));
    return (X509CertificateHolder) parser.readObject();
}