Échapper à Crypto Pro. Édition GOST 34.10-2012

Sur Habré, il y a un magnifique article " Escape from Crypto Pro. Version du réalisateur, édition SMEV ", mais l'année 2019 est venue et toutes les autorités de certification ont commencé à émettre des signatures numériques conformément à GOST 34.10-2012 au lieu de GOST 34.10-2001.

Sous la coupe, une histoire sur la façon dont vous pouvez modifier votre logiciel sur Bouncy Castle pour prendre en charge l'utilisation des clés pour les nouveaux invités.

image

Clause de non-responsabilité


Je ne sais rien des subtilités juridiques de la signature de documents via Bouncy Castle et d'autres systèmes de protection des informations cryptographiques et je ne suis pas prêt à communiquer. Avant d'utiliser le code en production, consultez un avocat.

Pourquoi est-ce même nécessaire? article original bien écrit. Je ne vais pas me répéter.

Obtention d'une clé du Token


image

Toutes les AC que je connais émettent des clés avec des certificats sur des jetons similaires. Le conteneur cryptoPro avec la clé privée et le certificat est écrit sur le jeton. Lorsque vous exportez une clé via CryptoPro CSP, elle est exportée vers un «CryptoPro pfx» spécial qui n'est compatible avec rien.

Les demandes pour émettre une clé dans un pfx standard ou tout autre conteneur CA typique sont ignorées.
Si quelqu'un connaît l'autorité de certification émettant des signatures dans des conteneurs standard, partagez les coordonnées dans les commentaires. Les bonnes personnes n'ont pas honte de promouvoir.

Pour convertir le conteneur CryptoPro en pfx standard, nous, comme dans l'article d'origine, utiliserons P12FromGostCSP. Les anciennes versions piratées ne fonctionnent pas avec les clés pour 2012 Gost. Nous allons sur le site des auteurs et en achetons un nouveau.

Nous avons donc obtenu le pfx standard avec la clé et le certificat.

Mise à jour du château gonflable

Nous mettons à jour Bouncy Castle à 1.60 Les versions plus anciennes peuvent ne pas prendre en charge les algorithmes 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> 

Initialisation du château gonflable

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

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

Les alias doivent être modifiés. L'utilitaire P12FromGostCSP définit toujours le même alias "csp_exported" et il y aura des problèmes de traitement de la deuxième clé.

Pour plus de commodité, la clé de pfx doit être chargée dans le Java KeyStore standard et fonctionner uniquement avec.

Télécharger KeyStore

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

Enregistrement d'une clé avec un certificat dans 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(); } } 

Télécharger des clés et des certificats depuis 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 } } 

Signature du fichier

 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 

Il existe une option JcaContentSignerBuilder ("GOST3411WITHECGOST3410-2012-512") pour les clés de 512 bits. Mes autorités de certification émettent celles de 256 bits sans rien demander et ignorent les questions de clarification.

Vérification de signature

 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 vérification de signature est complètement similaire à la vérification GOST de 2001. Vous ne pouvez rien changer.

Résumé


À la suite de toutes les actions ci-dessus, nous avons obtenu un moyen relativement facile de se débarrasser de la lourde charge de Crypto Pro en 2019.

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


All Articles