Correctly creating a new certificate with an intermediate certificate using bouny castle

So my problem is this,

Basically, I want to create a certificate chain using bouncy castle (jdk16 version 1.46). I am new to bouncy castle and java.security in general, so if my approach could be completely wrong, but anyway this is what I did:

So far, I can create a self-signed certificate that I use as the root certificate. This is done using the following code:

//-----create CA certificate with key KeyPair caPair = Signing.generateKeyPair("DSA", 1024, null, null); 

This basically creates a key pair, two null options for the provider, and safe random ones if necessary.

 Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> caMap = new HashMap<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>>(); caMap.put(X509Extensions.BasicConstraints, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(true, new BasicConstraints(true))); //------this creates the self signed certificate X509Certificate caCert = X509CertificateGenerator.generateX509Certificate(serial, "CN=CA", "CN=CA", start, end, "SHA1withDSA", caPair.getPrivate(), caPair.getPublic(), null, caMap); 

This will create a certificate with the provided attributes.

  • serial = just the current time in milliseconds
  • start = same as serial (may have a difference of 1 or 2 milliseconds)
  • end = start + 2 days

The card simply adds a basic restriction to install the certificate as a CA. I use the map here, since I want to be able to add additional X509Extensions, if necessary.

 //-----save ca certificate in PEM format X509CertificateGenerator.savePemX509Certificate(caCert, caPair.getPrivate(), caWriter); 

This will save the certificate and private key in the pem file using an inflatable caste writer.

After that, a file is created and I can install it as well (I use IE and then install it through Internet Options as a trusted CA. The certificate is also considered valid).

After that, I create an intermediate certificate using the following code (note that the above code is in the same scope as these variables)

 KeyPair intermediatePair = Signing.generateKeyPair("DSA", 1024, null, null); Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> intermediateMap = new HashMap<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>>(); intermediateMap.put(X509Extensions.AuthorityKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new AuthorityKeyIdentifierStructure(caCert))); intermediateMap.put(X509Extensions.SubjectKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new SubjectKeyIdentifierStructure(intermediatePair.getPublic()))); X509Certificate intermediateCert = X509CertificateGenerator.generateX509Certificate(serial.add(BigInteger.valueOf(1l)), "CN=intermediate", caCert.getSubjectX500Principal().toString(), start, end, "SHA1withDSA", caPair.getPrivate(), intermediatePair.getPublic(), null, intermediateMap); //-----save intermediate certificate in PEM format X509CertificateGenerator.savePemX509Certificate(intermediateCert, intermediatePair.getPrivate(), intermediateWriter); 

The procedure is basically the same, however I add additional X509Extensions:

  • X509Extensions.AuthorityKeyIdentifier = sets the CA certificate as an intermediate parent
  • X509Extensions.SubjectKeyIdentifier = uses public key generator for certificate

In addition, the CA is used as the issuer, and the CA private key is used to create the intermediate certificate.

This also works, and I can install the intermediate certificate (again using IE), it is also shown that the parent certififcate is a generated CA certificate and that the certificate is valid.

Now comes the tricky part where I make a mistake, I think. Now I am creating a new certificate using an intermediate certificate using the following code.

 KeyPair endPair = Signing.generateKeyPair("DSA", 1024, null, null); Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> endMap = new HashMap<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>>(); endMap.put(X509Extensions.AuthorityKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new AuthorityKeyIdentifierStructure(intermediateCert))); endMap.put(X509Extensions.SubjectKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new SubjectKeyIdentifierStructure(endPair.getPublic()))); X509Certificate endCert = X509CertificateGenerator.generateX509Certificate(serial.add(BigInteger.valueOf(1l)), "CN=end", intermediateCert.getSubjectX500Principal().toString(), start, end, "SHA1withDSA", intermediatePair.getPrivate(), endPair.getPublic(), null, endMap); X509CertificateGenerator.savePemX509Certificate(endCert, endPair.getPrivate(), endWriter); 

Essentially, this is the same as creating an intermediate certificate. However, now I use the following X509Extension settings:

  • X509Extensions.AuthorityKeyIdentifier = sets the intermediate certificate as the parent certificate
  • X509Extensions.SubjectKeyIdentifier = uses public key generator for certificate

Also, an intermediate certificate is used as an issuer, and its private key is used to create a certificate.

I can also install a new certificate, but when I check if (again IE), it shows that the certificate, however, is invalid because "this CA either does not have the right to issue certificates, or the certificate cannot be used as an end user, an object "

Therefore, I need to somehow enable the intermediate certificate in order to be able to create new certificates by adding some KeyUsages / ExtendedKeyUsage, which I assume.

Does anyone know how I allow an intermediate certificate to do what I need, or if I do something wrong at all?

EDIT 1:

So, I forgot to provide the code for the method that created the certificate, and the one that saved it in PEM format (I renamed it savePemX509Certificate, because the old one was wrong).

Code for creating a certificate:

 public static X509Certificate generateX509Certificate(BigInteger serialnumber, String subject, String issuer, Date start , Date end, String signAlgorithm, PrivateKey privateKey, PublicKey publicKey, String provider, Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> map) throws CertificateEncodingException, InvalidKeyException, IllegalStateException, NoSuchProviderException, NoSuchAlgorithmException, SignatureException { if(serialnumber!=null && subject!=null && issuer!=null && start!=null && end!=null && signAlgorithm !=null && privateKey!=null && publicKey!=null) { //-----GENERATE THE X509 CERTIFICATE X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); X509Principal dnSubject = new X509Principal(subject); X509Principal dnIssuer = new X509Principal(issuer); certGen.setSerialNumber(serialnumber); certGen.setSubjectDN(dnSubject); certGen.setIssuerDN(dnIssuer); certGen.setNotBefore(start); certGen.setNotAfter(end); certGen.setPublicKey(publicKey); certGen.setSignatureAlgorithm(signAlgorithm); //-----insert extension if needed if(map!=null) for(ASN1ObjectIdentifier extension : map.keySet()) certGen.addExtension(extension, map.get(extension).getKey(), map.get(extension).getValue()); return certGen.generate(privateKey, provider); } return null; } 

Code for saving certificate and key:

 public static boolean savePemX509Certificate(X509Certificate cert, PrivateKey key, Writer writer) throws NoSuchAlgorithmException, NoSuchProviderException, CertificateEncodingException, SignatureException, InvalidKeyException, IOException { if(cert!=null && key!=null && writer!=null) { PEMWriter pemWriter = new PEMWriter(writer); pemWriter.writeObject(cert); pemWriter.flush(); if(key!=null) { pemWriter.writeObject(key); pemWriter.flush(); } pemWriter.close(); return true; } return false; } 

As you can see, I basically put the certificate and key into a file, that's all. The result is the following and it seems to me good.

 -----BEGIN CERTIFICATE----- MIICdjCCAjagAwIBAgIGAUDuXLRLMAkGByqGSM44BAMwDTELMAkGA1UEAwwCQ0Ew HhcNMTMwOTA1MTM0MzA3WhcNMTMwOTA3MTM0MzA3WjANMQswCQYDVQQDDAJDQTCC AbcwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADD Hj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gE exAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/Ii Axmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4 V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozI puE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4Vrl nwaSi2ZegHtVJWQBTDv+z0kqA4GEAAKBgAeFoGATLbIr8+QNuxcbYJ7RhbefKWSC Br67Pp4Ynikxx8FZN4kCjGX7pwT1KffN3gta7jxIXNM5G3IFbs4XnYljh5TbdnjP 9Ge3kxpwncsbMQfCqIwHh8T5gh55KaxH7yYV2mrtEEqj7NBL4thQhJe2WGwgkB9U NxNmLoMq3m4poyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAJ BgcqhkjOOAQDAy8AMCwCFFm5ybLY09y8y2uGsEnpceffy2KaAhQIyshgy3ohCLxQ q3CmnvC+cfT2VQ== -----END CERTIFICATE----- -----BEGIN DSA PRIVATE KEY----- MIIBuwIBAAKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR +1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb +DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdg UI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlX TAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCj rh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQB TDv+z0kqAoGAB4WgYBMtsivz5A27FxtgntGFt58pZIIGvrs+nhieKTHHwVk3iQKM ZfunBPUp983eC1ruPEhc0zkbcgVuzhediWOHlNt2eM/0Z7eTGnCdyxsxB8KojAeH xPmCHnkprEfvJhXaau0QSqPs0Evi2FCEl7ZYbCCQH1Q3E2YugyrebikCFDJCJHtt NWB4LWYc4y4QvJ/l46ap -----END DSA PRIVATE KEY----- 

So, after gtrig provided me with the correct way to create a certificate, I ended up using this method to create a normal or self-signed (if the private key belongs to the same key as the public key) certificate

 public static X509Certificate createX509V3Certificate(X500Principal name, BigInteger serial, Date start, Date end, PublicKey pubKey, String algorithm, PrivateKey privateKey, Map<ASN1ObjectIdentifier, Entry<Boolean, ASN1Object>> map, X509Certificate parentCert) throws IOException, OperatorCreationException, CertificateException { if(serial!=null && start!=null && end!=null && name!=null && pubKey!=null && algorithm!=null && privateKey!=null) { ContentSigner signer = new JcaContentSignerBuilder(algorithm).build(privateKey); X509v3CertificateBuilder certBldr = null; if(parentCert==null) certBldr = new JcaX509v3CertificateBuilder(name, serial, start, end, name, pubKey); else certBldr = new JcaX509v3CertificateBuilder(parentCert, serial, start, end, name, pubKey); if(map!=null) for(ASN1ObjectIdentifier extension : map.keySet()) certBldr.addExtension(extension, map.get(extension).getKey(), map.get(extension).getValue()); return new JcaX509CertificateConverter().setProvider("BC").getCertificate(certBldr.build(signer)); } return null; } 
+6
source share
1 answer

Something is not so similar to how you create PEM files. You are using a method called generateSelfSignedPemX509Certificate , but you do not need a self-signed certificate, you want the final certificate to be signed with an intermediate private key, and you want to receive an intermediate certificate signed with a CA private key.

In addition, you need basic constraints and key usage extensions.

To create certificates signed by other objects (non-self-propelled), I use these methods from Bouncy Castle to create an "end" certificate.

  ASN1Sequence seq= (ASN1Sequence) new ASN1InputStream(parentPubKey.getEncoded()).readObject(); SubjectPublicKeyInfo parentPubKeyInfo = new SubjectPublicKeyInfo(seq); ContentSigner signer = new JcaContentSignerBuilder(algorithm).build(parentPrivKey); X509v3CertificateBuilder certBldr = new JcaX509v3CertificateBuilder( parentCert, serialNum, startDate, endDate, distName, pubKey) .addExtension( new ASN1ObjectIdentifier("2.5.29.35"), false, new AuthorityKeyIdentifier(parentPubKeyInfo)) .addExtension( new ASN1ObjectIdentifier("2.5.29.19"), false, new BasicConstraints(false)) // true if it is allowed to sign other certs .addExtension( new ASN1ObjectIdentifier("2.5.29.15"), true, new X509KeyUsage( X509KeyUsage.digitalSignature | X509KeyUsage.nonRepudiation | X509KeyUsage.keyEncipherment | X509KeyUsage.dataEncipherment)); // Build/sign the certificate. X509CertificateHolder certHolder = certBldr.build(signer); X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC) .getCertificate(certHolder); 

For a CA or intermediate certificate, you need to add the SubjectKeyIdentifier extension. In addition, BasicConstraints must be true , and KeyUsage must be:

  new X509KeyUsage( X509KeyUsage.keyCertSign| X509KeyUsage.cRLSign)); 
+6
source

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


All Articles