Escapar de Crypto Pro. Edición GOST 34.10-2012

En Habré hay un magnífico artículo " Escape from Crypto Pro. Versión del director, edición SMEV ", pero llegó el año 2019 y todas las AC comenzaron a emitir firmas digitales de acuerdo con GOST 34.10-2012 en lugar de GOST 34.10-2001.

Debajo del corte, una historia sobre cómo puede modificar su software en Bouncy Castle para apoyar el trabajo con claves para nuevos invitados.

imagen

Descargo de responsabilidad


No sé nada sobre las complejidades legales de firmar documentos a través de Bouncy Castle y otros sistemas de protección de información criptográfica y no estoy listo para comunicarme. Antes de usar el código en producción, consulte con un abogado.

¿Por qué es esto necesario? Artículo original bien escrito. No me repetiré

Obteniendo una llave del Token


imagen

Todas las CA que conozco emiten claves con certificados en tokens similares. El contenedor cryptoPro con la clave privada y el certificado está escrito en el token. Al exportar una clave a través de CryptoPro CSP, se exporta a un "CryptoPro pfx" especial que no es compatible con nada.

Las solicitudes para emitir una clave en un pfx estándar o cualquier otro contenedor CA típico se ignoran.
Si alguien conoce la CA que emite firmas en contenedores estándar, comparta las coordenadas en los comentarios. Las buenas personas no se avergüenzan de promover.

Para convertir el contenedor CryptoPro a pfx estándar, nosotros, como en el artículo original, usaremos P12FromGostCSP. Las versiones pirateadas antiguas no funcionan con claves para 2012 Gost. Vamos al sitio web de los autores y compramos uno nuevo.

Entonces obtuvimos el pfx estándar con la clave y el certificado.

Actualización del castillo hinchable

Actualizamos Bouncy Castle a 1.60. Las versiones anteriores pueden no ser compatibles con los algoritmos 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> 

Inicializando el castillo hinchable

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

Análisis de 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); } } 

Los alias deben ser cambiados. La utilidad P12FromGostCSP siempre establece el mismo alias "csp_exported" y habrá problemas al procesar la segunda clave.

Para mayor comodidad, la clave de pfx debe cargarse en el Java KeyStore estándar y luego trabajar solo con ella.

Descargar KeyStore

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

Guardar una clave con un certificado en 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(); } } 

Descargue claves y certificados de 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 } } 

Firma de archivo

 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 

Hay una opción JcaContentSignerBuilder ("GOST3411WITHECGOST3410-2012-512") para claves de 512 bits. Mis CA emiten 256 bits sin hacer nada e ignoran las preguntas aclaratorias.

Verificación de firma

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

La verificación de firma es completamente similar a la verificación GOST de 2001. No puedes cambiar nada.

Resumen


Como resultado de todas las acciones anteriores, obtuvimos una forma relativamente fácil de deshacernos de la gran carga de Crypto Pro en 2019.

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


All Articles