📜 ⬆️ ⬇️

How to sign a message using Java GOST R 34.11 / 34.10-2001 algorithm

In this article I will tell you how to sign an arbitrary message with a private key and certificate according to the algorithm GOST R 34.11 / 34.10-2001 attached (attached) signature in the Java language.

For the project of electronic document management, I needed to make a signature using the GOST algorithm. Despite the fact that it appeared long ago, to my surprise, I could not find a single completed example on the network that would receive a message, a key and a certificate at the input, and would give a signed message at the output.

All the examples found either used third-party paid software CryptoPro, or were not compiled with modern versions of Java, or the signed messages were not validated later.
')
In general, I spent a lot of time to figure it out, and decided that a complete ready-made example would be useful to someone.

To sign we need a certificate and private key.
I received them in pfx format, the components must be extracted from it.

I did everything on windows and used the OpenSsl build with GOST support. For other operating systems, I think the actions will be similar. In OpenSsl from version 1.1.0 GOST built-in support was removed, it must be connected in a confused way, which I did not take off with the move. So I just downloaded the old version 1.0.2.

Add the following lines to the openssl.cfg config:

openssl_conf = openssl_def [openssl_def] engines = engine_section [engine_section] gost = gost_section [gost_section] engine_id = gost dynamic_path = gost.dll default_algorithms = ALL CRYPT_PARAMS = id-Gost28147-89-CryptoPro-A-ParamSet 

We start the console and enter the command (without it, I had the config on it):

 set OPENSSL_CONF=C:\__openssl\bin\openssl.cfg 

We export the key to the pkcs12 format:

 openssl pkcs12 -in my.pfx -nocerts -nodes -out my.pem 

We translate the key into pkcs8 format:

 openSSL pkcs8 -in my.pem -topk8 -nocrypt -out key.pk8 

Export certificate:

 openssl pkcs12 -in my.pfx -nokeys -out my.cer 

When executing commands, a password from pfx will be requested, of course, you need to know it.

For Java signing, I used the BouncyCastle library, it supports GOST.
I have a project on Maven, I added dependencies to pom.xml:

 <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.59</version> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpkix-jdk15on</artifactId> <version>1.59</version> </dependency> 

Code signing method:

 public static byte[] signWithGost3410(byte[] data, X509Certificate certificate, byte[] encodedPrivateKey) throws Exception { X509Certificate[] certificates = new X509Certificate[1]; certificates[0] = certificate; PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey); KeyFactory keyFactory = KeyFactory.getInstance("ECGOST3410", "BC"); PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec); CMSTypedData msg = new CMSProcessableByteArray(data); Store certStore = new JcaCertStore(Arrays.asList(certificates)); CMSSignedDataGenerator gen = new CMSSignedDataGenerator(); ContentSigner signer = new org.bouncycastle.operator.jcajce.JcaContentSignerBuilder("GOST3411withECGOST3410").setProvider("BC").build(privateKey); gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(signer, (X509Certificate) certificates[0])); gen.addCertificates(certStore); CMSSignedData sigData = gen.generate(msg, true); return sigData.getEncoded(); } 

Code for reading key from file:

 public static byte[] readEncodedKeyFromPk8File(String filename) throws Exception { byte[] content = Files.readAllBytes(Paths.get(filename)); ArrayList<String> lines = new ArrayList<>(Arrays.asList(new String(content).split("\n"))); lines.remove(0); lines.remove(lines.size() -1); String base64 = String.join("", lines); byte[] encoded = Base64.getDecoder().decode(base64); return encoded; } 

Code for reading certificate from file:

 public static X509Certificate readX509CertificateFromCerFile(String filename) throws Exception { CertificateFactory factory = CertificateFactory.getInstance("X.509"); Certificate certificate = factory.generateCertificate(new FileInputStream(filename)); return (X509Certificate) certificate; } 

And finally, an example of a signature:

 @Test public void signTest() throws Exception{ Security.addProvider(new BouncyCastleProvider()); byte[] key = readEncodedKeyFromPk8File("key.pk8"); X509Certificate certificate = readX509CertificateFromCerFile("my.cer"); byte[] data = Files.readAllBytes(Paths.get("my.xml")); byte[] signedData = signWithGost3410(data, certificate, key); try(FileOutputStream stream = new FileOutputStream("signed.dat")){ stream.write(signedData); } } 

The resulting .dat file successfully passes the signature verification, for example, here .

I hope this example will be useful. If necessary, translate into another language, I think, is not difficult.

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


All Articles