在哈布雷(Habré)上有一篇宏伟的文章“
从Crypto Pro逃脱。导演的版本,SMEV版本 ”,但是到了2019年,所有CA都开始按照GOST 34.10-2012而不是GOST 34.10-2001发行数字签名。
在剪辑中,讲述了如何在Bouncy Castle上修改软件以支持为新访客使用密钥的故事。

免责声明
我对通过Bouncy Castle和其他密码信息保护系统签署文档的法律复杂性一无所知,因此我不准备交流。 在生产中使用该代码之前,请咨询律师。
为什么还要这样做? 写得很好的原始文章。 我不会重复自己。
从令牌获取密钥

我所知道的所有CA都使用相似令牌的证书颁发密钥。 带有私钥和证书的cryptoPro容器写在令牌上。 通过CryptoPro CSP导出密钥时,会将其导出到与任何东西都不兼容的特殊“ CryptoPro pfx”。
在标准pfx或任何其他典型CA容器中发出密钥的请求将被忽略。
如果有人知道标准容器中的CA签发签名,请在注释中共享坐标。 好人不以促进为耻。要将CryptoPro容器转换为标准pfx,如原始文章中所述,我们将使用P12FromGostCSP。 旧的被黑版本无法与2012 Gost的密钥一起使用。 我们转到作者
网站并购买一个新
网站 。
因此,我们获得了带有密钥和证书的标准pfx。
充气城堡更新我们将Bouncy Castle更新为1.60。旧版本可能不支持GOST 2012算法。
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.60</version> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpkix-jdk15on</artifactId> <version>1.60</version> </dependency>
初始化Bouncy Castle static { BouncyCastleProvider bcProvider = new BouncyCastleProvider(); String name = bcProvider.getName(); Security.removeProvider(name);
Pfx解析 KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC"); ByteArrayInputStream baos = new ByteArrayInputStream(pfxFileContent); keyStore.load(baos, password.toCharArray()); Enumeration<String> aliases = keyStore.aliases(); while (aliases.hasMoreElements()) { String alias = aliases.nextElement(); if (keyStore.isKeyEntry(alias )) { Key key = keyStore.getKey(alias , keyPassword.toCharArray()); java.security.cert.Certificate certificate = keyStore.getCertificate(alias ); addKeyAndCertificateToStore((PrivateKey)key, (X509Certificate)certificate); } }
别名必须更改。 P12FromGostCSP实用程序始终设置相同的别名“ csp_exported”,并且在处理第二个密钥时会出现问题。
为了方便起见,必须将pfx中的密钥加载到标准Java KeyStore中,然后才可以使用它。
下载密钥库 FileInputStream is = new FileInputStream(keystorePath); keystore = KeyStore.getInstance(KeyStore.getDefaultType()); char[] passwd = keystorePassword.toCharArray(); keystore.load(is, passwd);
在证书库中保存带有证书的证书 public void addKeyAndCertificateToStore(PrivateKey key, X509Certificate certificate) { synchronized (this) { keystore.setKeyEntry(alias.toLowerCase(), key, keyPassword.toCharArray(), new X509Certificate[] {certificate}); FileOutputStream out = new FileOutputStream(keystorePath); keystore.store(out, keystorePassword.toCharArray()); out.close(); } }
从KeyStore下载密钥和证书 Enumeration<String> aliases = keystore.aliases(); while (aliases.hasMoreElements()) { String alias = aliases.nextElement(); if (keystore.isKeyEntry(alias)) { Key key = keystore.getKey(alias, keyPassword.toCharArray()); keys.put(alias.toLowerCase(), key);
文件签名 CMSProcessableByteArray msg = new CMSProcessableByteArray(dataToSign); List certList = new ArrayList(); certList.add(cert); Store certs = new JcaCertStore(certList); CMSSignedDataGenerator gen = new CMSSignedDataGenerator(); ContentSigner signer = new org.bouncycastle.operator.jcajce.JcaContentSignerBuilder("GOST3411WITHECGOST3410-2012-256").setProvider("BC").build(privateKey); gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(signer, certificate)); gen.addCertificates(certs); CMSSignedData sigData = gen.generate(msg, false); byte[] sign = sigData.getEncoded();
对于512位密钥,有一个JcaContentSignerBuilder选项(“ GOST3411WITHECGOST3410-2012-512”)。 我的CA不问任何问题就发出256位证书,而忽略了澄清的问题。
签名验证 byte[] data = ...;
签名验证与2001 GOST验证完全相似。 您无法更改任何内容。
总结
由于上述所有措施,我们有了相对简单的方法来摆脱2019年Crypto Pro的沉重负担。