📜 ⬆️ ⬇️

Escape from Crypto Pro. GOST 34.10-2012 edition

On Habré there is a magnificent article " Escape from Crypto Pro. Director's version, SMEV-edition ", but the year came in 2019 and all CA started issuing EDS according to GOST 34.10-2012 instead of GOST 34.10-2001.

Under the cut, the story of how to modify your software at Bouncy Castle to support the work with keys for new guests.

image

Disclaimer


I don’t know anything about the legal intricacies of signing documents through Bouncy Castle and other CPSFs and I’m not ready to talk. Before using the code in production consult a lawyer.

And why is it even necessary? well written original article. I will not repeat.

Getting the key from Token


image

All CAs known to me issue keys with certificates on similar tokens. A token contains a CryptoPro container with a private key and a certificate. When exporting a key through the CryptoPro CSP, it is exported to a special CryptoPro pfx incompatible with anything.

Requests to issue a key in a standard pfx or any other typical container are ignored.
If anyone knows the CA issuing signatures in standard containers, share the coordinates in the comments. Good people are not ashamed to popiarit.

To convert the CryptoPro container to standard pfx, we will use P12FromGostCSP as in the original article. Old hacked versions do not work with keys according to 2012 GOST. Go to the authors site and buy a new one.

So we got a standard pfx with a key and a certificate.

Bouncy Castle Update

We update Bouncy Castle to 1.60 Older versions may not support GOST 2012 algorithms.

<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> 

Initializing Bouncy Castle

  static { BouncyCastleProvider bcProvider = new BouncyCastleProvider(); String name = bcProvider.getName(); Security.removeProvider(name); // remove old instance Security.addProvider(bcProvider); } 

Parsing 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); } } 

Aliases must change. The P12FromGostCSP utility always sets the same alias “csp_exported” and there will be problems when processing the second key.

For convenience, the key from pfx must be loaded into the standard Java KeyStore and only work with it further.

Download KeyStore

 FileInputStream is = new FileInputStream(keystorePath); keystore = KeyStore.getInstance(KeyStore.getDefaultType()); char[] passwd = keystorePassword.toCharArray(); keystore.load(is, passwd); 

Saving key with certificate in KeyStore

 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(); } } 

Downloading keys and certificates from 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); //any key,value collection Certificate certificate = keystore.getCertificate(alias); if (certificate instanceof X509Certificate) certificates.put(alias.toLowerCase(), (X509Certificate) certificate); //any key,value collection } } 

File signature

 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(); //result here 

There is an option JcaContentSignerBuilder ("GOST3411WITHECGOST3410-2012-512") for 512 bit keys. My CAs give out 256 bits without asking anything and ignore the clarifying questions.

Signature verification

 byte[] data = ...; //signed file data byte[] signature = ...;//signature boolean checkResult = false; CMSProcessable signedContent = new CMSProcessableByteArray(data); CMSSignedData signedData; try { signedData = new CMSSignedData(signedContent, signature); } catch (CMSException e) { return SIGNATURE_STATUS.ERROR; } SignerInformation signer; try { Store<X509CertificateHolder> certStoreInSing = signedData.getCertificates(); signer = signedData.getSignerInfos().getSigners().iterator().next(); Collection certCollection = certStoreInSing.getMatches(signer.getSID()); Iterator certIt = certCollection.iterator(); X509CertificateHolder certHolder = (X509CertificateHolder) certIt.next(); X509Certificate certificate = new JcaX509CertificateConverter().getCertificate(certHolder); checkResult = signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(certificate)); } catch (Exception ex) { return SIGNATURE_STATUS.ERROR; } 

The verification of the signature is completely analogous to the verification in accordance with GOST 2001. You can not change anything.

Summary


As a result of all the above actions, we got a relatively easy way to get rid of the heavy burden of Krypto Pro in 2019.

Source: https://habr.com/ru/post/440882/