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 2019 came and all the CAs began to issue digital signatures in accordance with GOST 34.10-2012 instead of GOST 34.10-2001.

    Under the cut, a story about how you can modify your software on Bouncy Castle to support working with keys for new guests.

    image

    Disclaimer


    I don’t know anything about the legal intricacies of signing documents through Bouncy Castle and other cryptographic information protection systems and I’m not ready to communicate. Before using the code in production, consult with a lawyer.

    Why is this even necessary? well written original article. I will not repeat myself.

    Obtaining a key from the Token


    image

    All CAs known to me issue keys with certificates on similar tokens. The cryptoPro container with the private key and certificate is written on the token. When exporting a key through CryptoPro CSP, it is exported to a special “CryptoPro pfx” that is not compatible with anything.

    Requests to issue a key in a standard pfx or any other typical CA 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 promote.

    To convert the CryptoPro container to standard pfx, we, as in the original article, will use P12FromGostCSP. Old hacked versions do not work with keys for 2012 Gost. We go to the authors website and buy a new one.

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

    Bouncy Castle update We update Bouncy Castle

    to 1.60 Older versions may not support GOST 2012 algorithms.

    org.bouncycastlebcprov-jdk15on1.60org.bouncycastlebcpkix-jdk15on1.60

    Initializing Bouncy Castle

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

    Pfx parsing

                   
    KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");
    ByteArrayInputStream baos = new ByteArrayInputStream(pfxFileContent);
    keyStore.load(baos, password.toCharArray());
    Enumeration 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 be changed. The P12FromGostCSP utility always sets the same alias “csp_exported” and there will be problems processing the second key.

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

    Download KeyStore

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

    Saving a key with a 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();
       }
    }
    

    Download keys and certificates from KeyStore

    
    Enumeration 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 a JcaContentSignerBuilder option ("GOST3411WITHECGOST3410-2012-512") for 512 bit keys. My CAs issue 256 bit ones without asking anything and ignore 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 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;
    }
    

    Signature verification is completely similar to the 2001 GOST verification. 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 load of Crypto Pro in 2019.

    Also popular now: