Entfliehen Sie Crypto Pro. GOST 34.10-2012 Ausgabe

Auf Habré gibt es einen großartigen Artikel " Escape from Crypto Pro. Director's Version, SMEV-Edition ", aber das Jahr 2019 kam und alle Zertifizierungsstellen begannen, digitale Signaturen gemäß GOST 34.10-2012 anstelle von GOST 34.10-2001 herauszugeben.

Unter dem Schnitt eine Geschichte darüber, wie Sie Ihre Software auf Bouncy Castle ändern können, um die Arbeit mit Schlüsseln für neue Gäste zu unterstützen.

Bild

Haftungsausschluss


Ich weiß nichts über die rechtlichen Schwierigkeiten beim Signieren von Dokumenten über Bouncy Castle und andere kryptografische Informationsschutzsysteme und bin nicht bereit zu kommunizieren. Bevor Sie den Code in der Produktion verwenden, wenden Sie sich an einen Anwalt.

Warum ist das überhaupt notwendig? gut geschriebener Originalartikel. Ich werde mich nicht wiederholen.

Einen Schlüssel vom Token erhalten


Bild

Alle mir bekannten Zertifizierungsstellen stellen Schlüssel mit Zertifikaten für ähnliche Token aus. Der cryptoPro-Container mit dem privaten Schlüssel und dem Zertifikat wird auf das Token geschrieben. Beim Exportieren eines Schlüssels über CryptoPro CSP wird er in einen speziellen „CryptoPro pfx“ exportiert, der mit nichts kompatibel ist.

Anforderungen zur Ausgabe eines Schlüssels in einem Standard-Pfx oder einem anderen typischen CA-Container werden ignoriert.
Wenn jemand weiß, dass die Zertifizierungsstelle Signaturen in Standardcontainern ausgibt, teilen Sie die Koordinaten in den Kommentaren mit. Gute Leute schämen sich nicht zu fördern.

Um den CryptoPro-Container in Standard-pfx zu konvertieren, verwenden wir wie im Originalartikel P12FromGostCSP. Alte gehackte Versionen funktionieren nicht mit Schlüsseln für 2012 Gost. Wir gehen zur Website des Autors und kaufen eine neue.

Also haben wir den Standard-Pfx mit dem Schlüssel und dem Zertifikat bekommen.

Hüpfburg Update

Wir aktualisieren Bouncy Castle auf 1.60. Ältere Versionen unterstützen möglicherweise keine GOST 2012-Algorithmen.

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

Hüpfburg initialisieren

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

Pfx-Analyse

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

Aliase müssen geändert werden. Das Dienstprogramm P12FromGostCSP setzt immer den gleichen Alias ​​"csp_exported", und es treten Probleme bei der Verarbeitung des zweiten Schlüssels auf.

Der Einfachheit halber muss der Schlüssel von pfx in den Standard-Java-KeyStore geladen werden und dann nur damit funktionieren.

Laden Sie den KeyStore herunter

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

Speichern eines Schlüssels mit einem Zertifikat im 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(); } } 

Laden Sie Schlüssel und Zertifikate aus dem KeyStore herunter

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

Dateisignatur

 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 

Es gibt eine JcaContentSignerBuilder-Option ("GOST3411WITHECGOST3410-2012-512") für 512-Bit-Schlüssel. Meine Zertifizierungsstellen geben 256-Bit-Dateien aus, ohne etwas zu fragen, und ignorieren klärende Fragen.

Überprüfung der Unterschrift

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

Die Signaturüberprüfung ähnelt vollständig der GOST-Überprüfung von 2001. Sie können nichts ändern.

Zusammenfassung


Als Ergebnis all der oben genannten Maßnahmen haben wir eine relativ einfache Möglichkeit gefunden, die schwere Last von Crypto Pro im Jahr 2019 loszuwerden.

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


All Articles