关于创建MANIFEST-INF/CERT.RSA及基于BouncyCastle重实现JAR/APK V1签名writeSignatureBlock方法的技术求助
关于创建MANIFEST-INF/CERT.RSA及基于BouncyCastle重实现JAR/APK V1签名writeSignatureBlock方法的技术求助
我太懂你这种困境了——高版本JDK里那些老旧的PKCS7、SignerInfo类要么被标记为废弃,要么直接从核心库移除了,想用BouncyCastle重写这个V1签名的writeSignatureBlock方法确实得费点功夫。我之前也踩过类似的坑,给你整理一套可行的实现方案:
首先,你得先引入BouncyCastle的依赖(如果是Maven项目的话):
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpkix-jdk15on</artifactId> <version>1.70</version> <!-- 建议用最新稳定版 --> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.70</version> </dependency>
然后,重写writeSignatureBlock方法。核心思路是用BouncyCastle的CMS(Cryptographic Message Syntax)API来替代原来的JDK内置PKCS7实现,步骤和你原来的代码逻辑完全对应:
import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.cms.CMSProcessableByteArray; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.CMSSignedDataGenerator; import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import java.io.OutputStream; import java.security.PrivateKey; import java.security.Security; import java.security.cert.X509Certificate; private static void writeSignatureBlock(X509Certificate publicKey, PrivateKey privateKey, OutputStream out) throws Exception { // 首先注册BouncyCastle安全提供者,这一步绝对不能少,不然会找不到对应算法 if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { Security.addProvider(new BouncyCastleProvider()); } // 1. 创建签名器,对应原代码中signature.sign()的核心逻辑 ContentSigner contentSigner = new JcaContentSignerBuilder("SHA1withRSA") .setProvider(BouncyCastleProvider.PROVIDER_NAME) .build(privateKey); // 2. 构建SignerInfo实例,和原代码的SignerInfo初始化逻辑对齐 JcaSignerInfoGeneratorBuilder signerInfoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder( new JcaDigestCalculatorProviderBuilder() .setProvider(BouncyCastleProvider.PROVIDER_NAME) .build()); // 设置直接签名模式,对应原代码中ContentInfo.DATA_OID为null的场景 signerInfoGeneratorBuilder.setDirectSignature(true); // 3. 构建PKCS7结构(BouncyCastle里叫CMSSignedData) CMSSignedDataGenerator generator = new CMSSignedDataGenerator(); generator.addSignerInfoGenerator( signerInfoGeneratorBuilder.build(contentSigner, publicKey)); generator.addCertificate(publicKey); // 原代码中没有传入实际数据,所以这里用空字节数组模拟 CMSSignedData signedData = generator.generate( new CMSProcessableByteArray(new byte[0]), false); // 4. 将PKCS7结构编码输出到流,对应原代码的pkcs7.encodeSignedData(out) signedData.getEncoded().writeTo(out); }
这里有几个关键细节要划重点:
- 原代码中
ContentInfo传入了DATA_OID但内容为null,所以我们用CMSProcessableByteArray(new byte[0])模拟这个逻辑,同时调用generate时传false表示不封装实际数据 - 必须提前注册BouncyCastle提供者,否则会出现「算法找不到」的异常
- 原代码用的是SHA1哈希+RSA签名的组合,对应BouncyCastle里的
SHA1withRSA算法标识符 - 我把原方法里的
Signature参数换成了PrivateKey,因为原代码的signature本质是用私钥初始化的,直接传私钥逻辑更清晰(如果你一定要保留Signature参数,也可以从signature中提取私钥,但步骤会冗余一些)
如果你还需要补全V1签名的完整流程(比如生成MANIFEST.MF、CERT.SF文件),可以参考BouncyCastle的CMS官方文档,但针对你问的writeSignatureBlock重实现,上面的代码应该能完美替代你注释掉的原逻辑。
内容来源于stack exchange
相关产品推荐
相关产品推荐

