使用 Bouncycastle 编码 PKCS7
Encode PKCS7 with 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();
}
我正在尝试用 Bouncy castle 替换它,因为较新的 Java 版本不喜欢访问 sun 的内部 类。
为了用充气城堡复制这种行为,我尝试了这个代码:
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)?
唯一的区别是 Bouncycastle 在证书主题中使用的某些字符串编码与 sun.* 类 略有不同。 Bouncycastle更符合RFC 3280,但是RFC5280稍微放宽了要求。任何一种编码都应该没问题。
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 castle 更易于使用。从现有的 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();
我的代码依赖于 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();
}
我正在尝试用 Bouncy castle 替换它,因为较新的 Java 版本不喜欢访问 sun 的内部 类。
为了用充气城堡复制这种行为,我尝试了这个代码:
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)?
唯一的区别是 Bouncycastle 在证书主题中使用的某些字符串编码与 sun.* 类 略有不同。 Bouncycastle更符合RFC 3280,但是RFC5280稍微放宽了要求。任何一种编码都应该没问题。
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 castle 更易于使用。从现有的 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();