📜 ⬆️ ⬇️

We issue a digital certificate and verify the signature using BouncyCastle

The flagship product of our company is Rutoken EDS - a device with Russian on-board cryptography. To integrate the device with browsers, the Rutoken Plugin was released. Demonstration of the plugin's capabilities can be viewed on the test sites . Some of them involve working with digital certificates stored on devices. For example, the Demo Bank, when registering users, issues a user certificate, and when logging into the system, it requests and checks it. To implement these tasks on the server uses the library BouncyCastle.


This article will consider examples of its use for issuing certificates for PKCS # 10, as well as for verifying the CMS signature generated using Russian cryptoalgorithms.

At the core of our "certification center" is the BouncyCastle library. It should be noted that the site bouncycastle.org/csharp/ is an outdated version of the library, which did not work in the solution without fixes. The working version can be taken on github - https://github.com/bcgit/bc-csharp .

There are also tests with a bunch of options for using the library for various needs.
')
We do not need much from all this:
- Work with PKCS # 10 queries
- Extract certificates according to the request

If there is a need to organize the entrance to the site using a certificate, we implement another algorithm, about it below.

The root certificate can be generated by the library and used later. We already have it, in the PEM format. There is also a private key.

Customer


In our system, an IIS server with an ASP.NET web-api is looking to the outside world with a method issuing a certificate upon PKCS # 10 request. On the client, that is, on the demo sites themselves, the application is running on AngularJs, which works with the plugin . Of course, you can write a client on anything, but the essence of work on the client side comes down to the following:

- we pass the data of the fields to the createPkcs10 plugin function to form a PKCS # 10 request, we get the request text.
- we send the text of the PKCS # 10 request with a post-request for the api method, we get a certificate or an error if it is impossible to issue a certificate.
- we transfer the received certificate to the importCertificate plugin function, import it to the device.

The working version of the site with the ability to manage certificates on Rutoken EDS devices is now spinning here - http://ra.rutoken.ru . You can create a key and make a request with the required fields. Next, write a test certificate that will be imported to the token.
! To work, you need to install a plugin and connect Rutoken EDS!

Server


But back to the server side. So, we have a root certificate in the PEM format and a private key to it. We will issue a user certificate upon PKCS # 10 request. The request itself also comes from the client in text form, in PEM format.

/*    */ const string cCACert = @"-----BEGIN CERTIFICATE----- ***   *** -----END CERTIFICATE-----"; /*   */ const string cCAKey = @"-----BEGIN PRIVATE KEY----- ***  *** -----END PRIVATE KEY-----"; //    public string generateTestCert(string pkcs10text) { //    PemReader pRd = new PemReader(new StringReader(cCAKey)); AsymmetricKeyParameter _cCAKey = (AsymmetricKeyParameter)pRd.ReadObject(); pRd.Reader.Close(); //    pRd = new PemReader(new StringReader(cCACert)); var _cCACert = (X509Certificate)pRd.ReadObject(); pRd.Reader.Close(); //  : //X509CertificateParser certParser = new X509CertificateParser(); //var _caCert = certParser.ReadCertificate(Base64.Decode(cCACert.Replace("-----BEGIN CERTIFICATE-----", string.Empty).Replace("-----END CERTIFICATE-----",string.Empty))); Pkcs10CertificationRequest _pkcs10; //  pkcs10 using (StringReader _sr = new StringReader(pkcs10text)) { pRd = new PemReader(_sr); _pkcs10 = (Pkcs10CertificationRequest)pRd.ReadObject(); pRd.Reader.Close(); } //   X509V3CertificateGenerator v3CertGen = new X509V3CertificateGenerator(); var requestInfo = _pkcs10.GetCertificationRequestInfo(); var subPub = _pkcs10.GetPublicKey(); var issPub = _cCACert.GetPublicKey(); //   var randomGenerator = new CryptoApiRandomGenerator(); var random = new SecureRandom(randomGenerator); var serialNumber = BigIntegers.CreateRandomInRange( BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random); v3CertGen.Reset(); v3CertGen.SetSerialNumber(serialNumber); v3CertGen.SetIssuerDN(_cCACert.IssuerDN); v3CertGen.SetNotBefore(DateTime.UtcNow); //    v3CertGen.SetNotAfter(DateTime.UtcNow.AddYears(1)); v3CertGen.SetSubjectDN(requestInfo.Subject); v3CertGen.SetPublicKey(subPub); if (issPub is ECPublicKeyParameters) { //          ,    GOST3411withECGOST3410 ECPublicKeyParameters ecPub = (ECPublicKeyParameters)issPub; if (ecPub.AlgorithmName == "ECGOST3410") { v3CertGen.SetSignatureAlgorithm("GOST3411withECGOST3410"); } else { throw new Exception("   GOST3411withECGOST3410"); } } else { throw new Exception(" GOST3411withECGOST3410"); } // extensions v3CertGen.AddExtension( X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifier(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(subPub))); v3CertGen.AddExtension( X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifier(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(issPub))); v3CertGen.AddExtension( X509Extensions.BasicConstraints, false, new BasicConstraints(false)); X509Certificate _cert = v3CertGen.Generate(_cCAKey); _cert.CheckValidity(); _cert.Verify(issPub); var s = new StringWriter(); PemWriter pw = new PemWriter(s); pw.WriteObject(_cert); pw.Writer.Close(); return s.ToString(); } 


Verifying a signed CMS on the server


We will generate a CMS on the client and send it to the server, where we will verify the signature and chain of certificates.

From BouncyCastle use:
- Signed verification signed CMS
- Building a certificate chain

All verification is reduced to verifying the signature and building a chain of certificates, including the root and the user certificate issued on it. For simplicity, we will not use intermediate certificates and will not work with CRL, although the library does have the ability to organize verification of the list of revoked certificates.

Checking a signed CMS is done like this:

 //    public void verifyCert(X509Certificate cert) { try { //    var pRd = new PemReader(new StringReader(cCACert)); var _cCACert = (X509Certificate)pRd.ReadObject(); pRd.Reader.Close(); //     IList certList = new ArrayList(); certList.Add(_cCACert); certList.Add(cert); IX509Store x509CertStore = X509StoreFactory.Create( "Certificate/Collection", new X509CollectionStoreParameters(certList)); //   ,     ISet trust = new HashSet(); trust.Add(new TrustAnchor(_cCACert, null)); PkixCertPathBuilder cpb = new PkixCertPathBuilder(); X509CertStoreSelector targetConstraints = new X509CertStoreSelector(); targetConstraints.Subject = cert.SubjectDN; PkixBuilderParameters parameters = new PkixBuilderParameters(trust, targetConstraints); parameters.AddStore(x509CertStore); //   crl parameters.IsRevocationEnabled = false; //  ,   -  PkixCertPathBuilderResult result = cpb.Build(parameters); } catch (PkixCertPathBuilderException certPathEx) { throw new PkixCertPathBuilderException(string.Format("  , {0}", certPathEx.Message)); } catch (Exception ex) { throw new Exception(string.Format("  : {0}", cert.SubjectDN), ex); } } //  signed CMS public string verifyCms(string cmsText) { CmsSignedData cms = new CmsSignedData(Base64.Decode(cmsText)); SignerInformationStore sif = cms.GetSignerInfos(); var signers = sif.GetSigners(); var ucrts = cms.GetCertificates("collection"); //var crl = cms.GetCrls("collection"); //   ,     signer    foreach (SignerInformation signer in signers) { ICollection certCollection = ucrts.GetMatches(signer.SignerID); IEnumerator certEnum = certCollection.GetEnumerator(); certEnum.MoveNext(); X509Certificate cert = (X509Certificate)certEnum.Current; if (!signer.Verify(cert)) { throw new CertificateException("   "); } verifyCert(cert); } return "ok"; } 


Once again, the example is suitable for testing or demonstrating solutions that work with Russian certificates.

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


All Articles