This article focuses on how to stop using Crypto Pro and switch to Bouncy Castle in a development / test environment.
At the beginning of the article there will be more about SMEV and its client, at the end - more about converting keys from the finished copy-paste so that you can start right now.
Picture to attract attention:
And immediately the answer:
Of course, it is impossible to escape with Krypto Pro, because it is a stronghold of Russian cryptography. He satisfies the requirements of the competent authorities, he is registered in contracts and contracts, the whole country trusts him, and so on. Therefore, all of the following applies to the developer or test environment, where we are our own masters.
Recently, I needed to figure out how to write a service that works with the System of Interagency Electronic Interaction.
All written is simply the result of a small study, the most abstracted from the work performed. And even approximately to guess something about the decisions actually made is impossible, I checked.
The source code of the client lies on the SMEV3 technology portal, but what a bad luck - they are nailed to CryptoPro. The Word file with the instruction attached to the source code states this in the most direct way. And anyway, the source keys are also in CryptoPro format. Based on production, this is normal, but for the test environment it is terribly inconvenient. I would like to get rid of it.
The site has two versions - "current" and "recommended." Why they are so, and why the current version is not recommended, and the recommended one is not relevant - some kind of dilemma of a copywriter :) Then we’re talking about a client who is “relevant”.
Strictly speaking, it cannot be used, because there is no license text in the source archive, and therefore it is not clear under which license the derivative work should be distributed. I called the support phone number written on the portal, wrote an e-mail, watched a couple of weeks as my application flies between technical support levels and executing organizations, and as a result, it’s still there:
The task is not completed, but completed and closed, amazingly. Well, to hell with them ...
Despite the inability to use it directly in the code, this is a great test example. The fact is that the methodological recommendations of SMEV without pollitres cannot be understood, and the ready-made live code gives an excellent boost to understanding.
The main complaint about the documentation is clericals and a poor description on the Internet.
Remember the meme about the copywriter, who from the paragraph made one sentence in a few words? For SMEV documentation, this is the place to be, for example, here is a chain of refactorins for one phrase taken for granted:
"2. The consumer's IP sends an interdepartmental request to SMEV;"
"2. The consumer’s IP sends a request to SMEV;"
"2. The consumer’s IP sends the request;"
"2. the consumer sends a request;"
"2. consumer request;"
In short, having a ready-made implementation is good.
As an alternative, in a test environment, I would prefer to use Bouncy Castle with a PKCS12 or JKS container. It is open, free and free software.
To the credit of the developers of Krypto Pro, they seem to have taken part in its development.
The code is written quite friendly for the extension, so you can simply take the KeyStoreWrapperJCP class as the basis, and write KeyStoreWrapperBouncyCastlePKCS12, KeyStoreWrapperBouncyCastleJKS in the same way.
Rewrite DigitalSignatureFactory so that it starts to accept the path to the cryptocontainer on the file system and the password from it (for CryptoPro, this is simply not necessary). There is a switch that checks the type of the crypto-provider, add two additional cases to it, for names like BOUNCY_JKS and BOUNCY_PKCS12 and hang up using the appropriate KeyWrapper and call initXmlSec.
In initXmlSec you need to add
1) the ability to accept any provider at all, not just cryptopro (it is just convenient)
2) Security.addProvider (new BouncyCastleProvider ());
3) for XMLDSIG_SIGN_METHOD, make a switch: if CryptoPro, the algorithm is called "GOST3411withGOST3410EL", and if the BouncyCastle algorithm is called "GOST3411WITHECGOST3410".
Well, sort of like everything. If the license for this smav client was known, I would attach specific code under the Apache License 2, and this is just a list of ideas.
Oh yeah, there is one interesting point. There are a lot of advice on SMEV in the network, consisting in manual parsing of XMLk pieces and manually setting the sun at sunset, but this is not our method (and not the method used by the client’s creators)
The batch is that it is easy to generate self-signed keys according to GOST (a copy-paste to SO is searched for in seconds). But to sign - no, because in the opinion of online schoolchildren, allegedly Bouncycastle does not support xml-signature. More specifically, when working with Apache Santuario, XMLSignature from santuario-xmlsec does not understand what to use to handle the "xmldsig-more # gostr34102001-gostr3411" method when calling xmlSignature.sign (privateKey).
Separate Hochma is that the IntelliJ IDEA Community is buggy when you try to detach xmlsec by throwing the step in debugger in the wrong place with the correct source code. I tried all the sensible versions of Idea, so understand how it works blindly, writing tactical letters in Sportloto. This is not a reproach to the Idea, there are no perfect tools, just a factor that influenced the speed of understanding the issue.
To make it work, you need:
1) Implement the SignatureAlgorithmSpi implementation from Apache Santuario. In GetEngineUri, return the string: " http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411 " (or whatever you have). This is the key magic. Absolutely nothing clever this class should not do.
Initialization of the application's lifetime (for example, in the singleton bean of the spring):
2) Download the provider so as not to patch the JDK:
Security.addProvider(new BouncyCastleProvider());
3) Go to runtime just-written class:
String algorithmClassName = "fully qualified name SignatureAlgorithmSpi"; Class.forName(algorithmClassName); SignatureAlgorithm.providerInit(); SignatureAlgorithm.register("http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411", algorithmClassName);
4) Knocking on JCE mapping algorithms:
String ns = "http://www.xmlsecurity.org/NS/#configuration"; Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); Element rootElement = document.createElementNS(ns, "JCEAlgorithmMappings"); Element algorithms = document.createElementNS(ns, "Algorithms");
5) Zapit method for the algorithm:
Element aElem = document.createElementNS(NameSpace, "Algorithm"); aElem.setAttribute("URI", "http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411"); aElem.setAttribute("Description", "GOST R 34102001 Digital Signature Algorithm with GOST R 3411 Digest"); aElem.setAttribute("AlgorithmClass", "Signature"); aElem.setAttribute("RequirementLevel", "OPTIONAL"); aElem.setAttribute("JCEName", "GOST3411WITHECGOST3410"); algorithms.appendChild(aElem);
6) Apply mappings:
org.apache.xml.security.Init.init(); JCEMapper.init(rootElement);
6) PROFIT!
After this, XMLSignature abruptly begins to understand this method, and will begin to make xmlSignature.sign.
If you have OpenSSL on the wall at the beginning of the article, someday it will definitely shoot.
So yes, this is an important point, necessary for the implementation of further text.
openssl_conf = openssl_def
Add lines to the very end of the file:
[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
If we already have real (not self-signed) keys, then it would be completely unreasonable to check them in action. Yes, we are talking about test purposes, but still trust - but check!
If you put the Windows in a virtual machine, roll Krypto Pro there, install the keys and try to export them, then we find an amazing thing: the export to PKCS12 does not work in the exporter, and all other directions in the exporter are blocked (English "grayed out").
I google a mistake, and what do we see on the official forum?
https://www.cryptopro.ru/forum2/default.aspx?g=posts&t=2425
"From Alexey Pisinin the answer was received:
Good day. PKCS12 does not meet the security requirements of the FSB regarding the storage of private keys. In theory, private keys should be stored on so-called "removable" media. Actually, for this reason, the export does not work. "
I read it correctly, what do they have for export, but I don’t have business logic to it?
For any ticks, do not click - there will always be an error on the last step of the wizard !?
What a shame.
Dear god
Please kill them all.
Love, Greg.
You can suffer for a very long time trying to roll up C ++ to subtract the key from the container using OpenSSL and such and such mother. I honestly tried, and crashed about the task like a ship on the rocks (at least, this is a task for more than 1 day for a person who hasn’t been doing this for a long time). On the Internet, we are not the only ones who crashed on the same rocks: http://gigamir.net/techno/pub903517
Here comes the moment "this is joy with tears in his eyes." A certain office called Lissy Soft, for only 2 thousand rubles, gives us the ingenious utility P12FromGostCSP. Its creators did defeat the problem that the community had not mastered, and it tears out the keys in PFX. Joy - because it works.
With tears - because it is proprietary, and she knows how she works.
In the picture, Richard Stallman seems to be surprised and asks: "Are you really fighting proprietary with the help of another proprietary one?"
So the whole instruction on driving the keys looks something like this:
openssl pkcs12 -in p12.pfx -out private.key.pem -name "alias"
openssl pkcs12 -export -out private.key.pkcs12 -in private.key.pem -name "alias"
Since we are doing all this for test purposes, now we come to a climax and begin to give ourselves the keys.
How to issue keys in the Crypto Pro format, I did not particularly bother, because it simply is not necessary in the framework of the current task. But just in case, there is a service for issuing such keys: http://www.cryptopro.ru/certsrv/
This question is widely represented on the Internet, so you can immediately watch Stackoverflow:
http://stackoverflow.com/questions/14580340/generate-gost-34-10-2001-keypair-and-save-it-to-some-keystore
The idea is that since we still use Bouncy Castle, we can also generate the key.
This code is not the most ideal, but it gives a really working implementation (in practice, I ended up with several large classes to make a convenient interface)
Security.addProvider( new org.bouncycastle.jce.provider.BouncyCastleProvider() ); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance( "ECGOST3410", "BC" ); keyPairGenerator.initialize( new ECGenParameterSpec( "GostR3410-2001-CryptoPro-A" ) ); KeyPair keyPair = keyPairGenerator.generateKeyPair(); org.bouncycastle.asn1.x500.X500Name subject = new org.bouncycastle.asn1.x500.X500Name( "CN=Me" ); org.bouncycastle.asn1.x500.X500Name issuer = subject; // self-signed BigInteger serial = BigInteger.ONE; // serial number for self-signed does not matter a lot Date notBefore = new Date(); Date notAfter = new Date( notBefore.getTime() + TimeUnit.DAYS.toMillis( 365 ) ); org.bouncycastle.cert.X509v3CertificateBuilder certificateBuilder = new org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder( issuer, serial, notBefore, notAfter, subject, keyPair.getPublic() ); org.bouncycastle.cert.X509CertificateHolder certificateHolder = certificateBuilder.build( new org.bouncycastle.operator.jcajce.JcaContentSignerBuilder( "GOST3411withECGOST3410" ) .build( keyPair.getPrivate() ) ); org.bouncycastle.cert.jcajce.JcaX509CertificateConverter certificateConverter = new org.bouncycastle.cert.jcajce.JcaX509CertificateConverter(); X509Certificate certificate = certificateConverter.getCertificate( certificateHolder ); KeyStore keyStore = KeyStore.getInstance( "JKS" ); keyStore.load( null, null ); // initialize new keystore keyStore.setEntry( "alias", new KeyStore.PrivateKeyEntry( keyPair.getPrivate(), new Certificate[] { certificate } ), new KeyStore.PasswordProtection( "entryPassword".toCharArray() ) ); keyStore.store( new FileOutputStream( "test.jks" ), "keystorePassword".toCharArray()
In principle, this is not really necessary, because we already have a simple and convenient way to issue JKS, and JKS for Java is the most native solution. But to complete the picture, let it be.
openssl req -newkey gost2001 -pkeyopt paramset:A -passout pass:aofvlgzm -subj "/C=RU/ST=Moscow/L=Moscow/O=foo_bar/OU=foo_bar/CN=developer/emailAddress=olegchiruhin@gmail.com" -keyout private.key.pem -out csr.csr
openssl x509 -req -days 365 -in csr.csr -signkey private.key.pem -out crt.crt
openssl x509 -inform pem -in crt.crt -pubkey -noout > public.key.pem
openssl.exe dgst -hex -sign private.key.pem message.xml
openssl smime -sign -inkey private.key.pem -signer crt.crt -in message.xml
openssl pkcs12 -export -out private.key.pkcs12 -in private.key.pem -name "alias"
As a result of all the above actions, we got a relatively easy way to get rid of the heavy burden of Krypto Pro.
In the future, I would like to continue the fight for the drunk before the final victory: arrange all utilities, key generators, self-written smav clients and so on in the form of one repository on GitHub. Also, I would very much like to receive the rights to modify and distribute under the permissive license of the official client of the SIEV. Then half of this article would simply not be needed, and the problem would be solved by downloading the necessary code from GitHub.
Source: https://habr.com/ru/post/282225/
All Articles