Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

So my problem is as follows,

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

So far I am able to create a self signed certificate which 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 the keypair, the two null options are for a provider and a secure random, if needed.

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 the a certificate with the provided attributes.

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

The map simply adds the basic contraint to set the certificate to be a CA. I use a map here since I want to be able to add additional X509Extensions if need be.

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

This will store the certificate and private key in a pem file using the bouncy caste pem writer.

After that the file is generated and I can install the file as well (I use IE and then install it via the Internet Options as a trusted CA. The certificate is also shown to be valid).

After that I create the intermediate certificate, using the following code (note the above code is in the same scope so those variables are available as well)

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 bascially the same, however I add additional X509Extensions:

  • X509Extensions.AuthorityKeyIdentifier = sets the CA certificate as the intermediates parent
  • X509Extensions.SubjectKeyIdentifier = uses the generates public key for the certificate

furthermore 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 (using IE again), it is also shown that the parent certififcate is the generated CA certificate and that the certificate is valid.

Now comes the tricky part where I am making a mistake I guess. I now create a new certificate using the 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 it is the same as creating the intermediate certificate. However I now use the following X509Extension settings:

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

Also the intermediate certificate is used as the issuer and its private key is used to create the certificate.

I can also install the new certificate but when I examine if (again IE), it shows that the certificate is however invalid because "This CA is either not entitled to issue certificates or the certificate can not be used as an end-entity."

So I somehow need to enable the intermediate certificate to be able to create new certificates as well, by adding some KeyUsages/ExtendedKeyUsage I assume.

Does someone know how I enable the intermediate certificate to do what I need it to do or if I do something wrong in general ?

EDIT 1:

So okay I forgot to provide the code for the method which created the certificate and the one that saved it in PEM format (I renamed it to savePemX509Certificate since the old one was misguiding).

Code for the certificate generation:

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 the saveing of the 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 the key in the file, thats all. The result is the following and seems good to me.

-----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 the certificate, I ended up using this method to create either a normal or self signed (if the private key is from the same keyPair as the public key that is) 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;
}
like image 332
DokutoMekki Avatar asked Sep 05 '13 09:09

DokutoMekki


People also ask

How intermediate certificates work?

Intermediate certificate plays a “Chain of Trust” between an end entity certificate and a root certificate. This is how it works. The root CA signs the intermediate root with its private key, which makes it trusted. Then the CA uses the intermediate certificate's private key to sign and issue end user SSL certificates.

How to find the intermediate certificate?

One of the simplest ways to find the intermediate certificate and export it is through an Internet Browser such as Google Chrome. Browse to the website that you need to get an intermediate certificate for and press F12. Browse to the security tab inside the developer tools. Click View certificate.

What is root intermediate and leaf certificate?

Any certificate that sits between the SSL/TLS Certificate and the Root Certificate is called a chain or Intermediate Certificate. The Intermediate Certificate is the signer/issuer of the SSL/TLS Certificate. The Root CA Certificate is the signer/issuer of the Intermediate Certificate.

Why we need intermediate CA?

Why Is the Intermediate Certificate Authority Important? The idea behind the Intermediate CA certificates is to add an extra layer of protection. This higher level of protection is achieved by not having to use the Root CA certificate to issue certificates for end-users.

How do I create self-signed certificates using Bouncycastle?

Self-signed certificates are used frequently during development and testing. This post shows how to create self-signed certificates using BouncyCastle. In the code below, the method generateSelfSignedCert () accepts the common name of the certificate to generate. It first creates a RSA key pair, then creates a certificate that is valid for 2 years.

How to generate a signed certificate file with public key?

An application with two menu options was first made. The first menu option was used to generate a certificate request file and a file with a private certificate key. After that, a certificate request file will be sent to an external or internal CA server to generate a signed certificate file (file with public key).

How do I generate a self-signed RSA Certificate?

In the code below, the method generateSelfSignedCert () accepts the common name of the certificate to generate. It first creates a RSA key pair, then creates a certificate that is valid for 2 years.

How do I create a self-signed server certificate using NuGet?

In this example, I’ll show you how to create a self-signed server certificate using C# and the Bouncy Castle .NET API. In order to follow along, create a new project and then use NuGet to add the “BouncyCastle” package. Create a CertificateGenerator. Set a bunch of properties. Ask it to generate the certificate. What’s in a certificate?


1 Answers

Something looks wrong with the way you're creating the PEM files. You're using a method called, generateSelfSignedPemX509Certificate, but you don't really want a self-signed certificate, you want an end certificate signed by the intermediate private key, and you want an intermediate certificate signed by the CA private key.

Also, you need basic constraints and key usage extensions on your certificates.

For creating certificates signed by other entities (non-self-signed), 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'll need to add a SubjectKeyIdentifier extension. Also, BasicConstraints should be true, and KeyUsage should be:

        new X509KeyUsage(
           X509KeyUsage.keyCertSign|
           X509KeyUsage.cRLSign));
like image 144
gtrig Avatar answered Oct 07 '22 12:10

gtrig