How to sign a message using the GOST R 34.11 / 34.10-2001 algorithm in Java
In this article I will tell you how to sign an arbitrary message with a private key and a certificate according to the GOST R 34.11 / 34.10-2001 algorithm with an attached signature in the Java language.
For the electronic document management project, I needed to make a signature using the GOST algorithm. Despite the fact that he appeared a long time ago, to my surprise, I could not find a single completed example on the network that would receive a message, key and certificate on the input, and would give a signed message on the output.
All the examples found either used third-party paid CryptoPro software, or did not compile with modern versions of Java, or signed messages were then not validated.
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 a private key.
They were given to me 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, the built-in support for GOST was removed, it must be connected in a confused way, which I did not take off on the move. So I just downloaded the old version 1.0.2.
Add the following lines to the openssl.cfg config:
We launch the console and enter the command (without it, I didn’t have the config):
Export the key to pkcs12 format:
We translate the key into pkcs8 format:
Export certificate:
When executing the commands, a password from pfx will be requested, of course, you need to know it.
To sign in Java, I used the BouncyCastle library, it supports GOST.
I have a project on Maven, I added dependencies in pom.xml:
Signing Method Code:
The code for the method of reading the key from the file:
The code for the method of reading a certificate from a file:
And finally, an example signature:
The resulting .dat file passes the signature verification successfully, for example, here .
Hope this example is helpful. If necessary, to translate into another language, I think, will not be difficult.
For the electronic document management project, I needed to make a signature using the GOST algorithm. Despite the fact that he appeared a long time ago, to my surprise, I could not find a single completed example on the network that would receive a message, key and certificate on the input, and would give a signed message on the output.
All the examples found either used third-party paid CryptoPro software, or did not compile with modern versions of Java, or signed messages were then not validated.
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 a private key.
They were given to me 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, the built-in support for GOST was removed, it must be connected in a confused way, which I did not take off on 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 launch the console and enter the command (without it, I didn’t have the config):
set OPENSSL_CONF=C:\папка_с_openssl\bin\openssl.cfg
Export the key to 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 the commands, a password from pfx will be requested, of course, you need to know it.
To sign in Java, I used the BouncyCastle library, it supports GOST.
I have a project on Maven, I added dependencies in pom.xml:
org.bouncycastle bcprov-jdk15on 1.59 org.bouncycastle bcpkix-jdk15on 1.59
Signing Method Code:
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();
}
The code for the method of reading the key from the file:
public static byte[] readEncodedKeyFromPk8File(String filename) throws Exception {
byte[] content = Files.readAllBytes(Paths.get(filename));
ArrayList 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;
}
The code for the method of reading a certificate from a 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 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 passes the signature verification successfully, for example, here .
Hope this example is helpful. If necessary, to translate into another language, I think, will not be difficult.