Fuja do Crypto Pro. Edição GOST 34.10-2012

Em Habré, há um artigo magnífico " Escape from Crypto Pro. Versão do diretor, edição SMEV ", mas chegou o ano de 2019 e todas as CAs começaram a emitir assinaturas digitais de acordo com GOST 34.10-2012, em vez de GOST 34.10-2001.

Abaixo, uma história sobre como você pode modificar seu software no Bouncy Castle para dar suporte ao trabalho com chaves para novos convidados.

imagem

Isenção de responsabilidade


Não sei nada sobre os meandros legais da assinatura de documentos por meio do Bouncy Castle e outros sistemas de proteção de informações criptográficas e não estou pronto para me comunicar. Antes de usar o código na produção, consulte um advogado.

Por que isso é necessário? artigo original bem escrito. Eu não vou me repetir.

Obtendo uma chave do token


imagem

Todas as CAs conhecidas por mim emitem chaves com certificados em tokens semelhantes. O contêiner cryptoPro com a chave privada e o certificado está gravado no token. Ao exportar uma chave pelo CryptoPro CSP, ela é exportada para um "CryptoPro pfx" especial que não é compatível com nada.

Solicitações para emitir uma chave em um pfx padrão ou em qualquer outro contêiner de CA típico são ignoradas.
Se alguém souber que a CA está emitindo assinaturas em contêineres padrão, compartilhe as coordenadas nos comentários. Boas pessoas não têm vergonha de promover.

Para converter o contêiner CryptoPro em pfx padrão, nós, como no artigo original, usaremos P12FromGostCSP. As versões hackeadas antigas não funcionam com chaves para o Gost de 2012. Vamos ao site dos autores e compramos um novo.

Portanto, obtivemos o PFX padrão com a chave e o certificado.

Atualização do castelo insuflável

Atualizamos o Bouncy Castle para 1,60. As versões mais antigas podem não suportar os 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 o Castelo Saltitante

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

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

Os aliases devem ser alterados. O utilitário P12FromGostCSP sempre define o mesmo alias "csp_exported" e haverá problemas ao processar a segunda chave.

Por conveniência, a chave do pfx deve ser carregada no Java KeyStore padrão e, em seguida, trabalhar apenas com ela.

Baixar KeyStore

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

Salvando uma chave com um certificado no 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(); } } 

Baixe chaves e certificados do 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 } } 

Assinatura de arquivo

 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 

Existe uma opção JcaContentSignerBuilder ("GOST3411WITHECGOST3410-2012-512") para chaves de 512 bits. Minhas CAs emitem as de 256 bits sem fazer nada e ignoram as perguntas de esclarecimento.

Verificação de Assinatura

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

A verificação de assinatura é completamente semelhante à verificação GOST de 2001. Você não pode mudar nada.

Sumário


Como resultado de todas as ações acima, temos uma maneira relativamente fácil de se livrar da carga pesada do Crypto Pro em 2019.

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


All Articles