Reputation: 272
My code relies on sun.security
to generate a PKCS7 signature block for signing an APK:
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();
}
I am trying to replace this with Bouncy castle, since newer Java versions do not like accesses to sun's internal classes.
In order to replicate this behavior with bouncy castle, I tried this code:
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();
The generated outputs have different lengths and are quite different:
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)?
Upvotes: 0
Views: 1416
Reputation: 4578
Alternative to BouncyCastle is the Osdt CERT library from Oracle, available from Maven-Repository. Gradle-dependency:
// https://mvnrepository.com/artifact/com.oracle.ojdbc/osdt_core
implementation "com.oracle.ojdbc:osdt_cert:19.3.0.0"
The Oracle library is IMO much simpler to use then Bouncy castle. Producing a PKCS7 DER format from an existing X509 format is a matter 3 code lines:
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();
Upvotes: 0
Reputation: 41958
The only difference is that Bouncycastle uses a slightly different encoding of certain strings in the certificate subject than sun.* classes. Bouncycastle is more in line with RFC 3280, but RFC5280 relaxed the requirements somewhat. Either encoding should be fine.
Upvotes: 1