Criando corretamente um novo certificado com um certificado intermediário usando o bouny castle

Então, meu problema é o seguinte,

Basicamente eu quero criar uma cadeia de certificados usando o bouncy castle (jdk16 versão 1.46). Eu sou bastante novo para bouncy castle e java.security em geral, por isso, se a minha abordagem pode ser completamente errada, mas de qualquer maneira é isso que eu fiz:

Até agora eu posso criar um certificado auto-assinado que eu uso como o certificado raiz. Isso é feito usando o seguinte código:

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

Isso basicamente cria o par de chaves, as duas opções nulas são para um provedor e um aleatório seguro, se necessário.

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);

Isso criará o certificado com os atributos fornecidos.

serial = simplesmente a hora atual em milissegundosstart = igual a serial basicamente (pode ter 1 ou 2 milissegundos de diferença)end = start + 2 days

O mapa simplesmente adiciona a restrição básica para definir o certificado como uma CA. Eu uso um mapa aqui desde que eu quero poder adicionar X509Extensions adicionais se necessário.

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

Isso armazenará o certificado e a chave privada em um arquivo pem usando o gravador bounce pem da casta.

Depois que o arquivo é gerado e eu posso instalar o arquivo também (eu uso o IE e, em seguida, instale-o através das opções da Internet como uma CA confiável. O certificado também é mostrado para ser válido).

Depois disso, eu crio o certificado intermediário, usando o seguinte código (note que o código acima está no mesmo escopo para que essas variáveis ​​também estejam disponíveis)

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);

O procedimento é bascially o mesmo, porém eu adiciono X509Extensions adicionais:

X509Extensions.AuthorityKeyIdentifier = define o certificado da AC como o pai intermediárioX509Extensions.SubjectKeyIdentifier = usa a chave pública de geração para o certificado

Além disso, a CA é usada como emissor e a chave privada da CA é usada para criar o certificado intermediário.

Isso também funciona e eu posso instalar o certificado intermediário (usando o IE novamente), também é mostrado que o certififcate pai é o certificado de CA gerado e que o certificado é válido.

Agora vem a parte complicada onde estou cometendo um erro, eu acho. Eu agora criar um novo certificado usando o certificado intermediário, usando o código a seguir.

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);

Essencialmente, é o mesmo que criar o certificado intermediário. No entanto, agora uso as seguintes configurações do X509Extension:

X509Extensions.AuthorityKeyIdentifier = define o certificado intermediário como o pai dos certificadosX509Extensions.SubjectKeyIdentifier = usa a chave pública de geração para o certificado

Além disso, o certificado intermediário é usado como o emissor e sua chave privada é usada para criar o certificado.

Também posso instalar o novo certificado, mas quando examino if (denovo IE), ele mostra que o certificado é, no entanto, inválido porque "Esta CA não está autorizada a emitir certificados ou o certificado não pode ser usado como uma entidade final".

Então, de alguma forma eu preciso habilitar o certificado intermediário para poder criar novos certificados também, adicionando alguns KeyUsages / ExtendedKeyUsage que eu assumo.

Alguém sabe como eu habilito o certificado intermediário para fazer o que eu preciso ou se eu faço algo errado em geral?

EDIT 1:

Tão bem eu esqueci de fornecer o código para o método que criou o certificado e aquele que salvou no formato PEM (eu renomei parasavePemX509Certificate desde que o antigo estava enganando).

Código para a geração de certificados:

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;
}

Código para salvar o certificado e a chave:

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;
}

Como você pode ver, eu basicamente coloco o certificado e a chave no arquivo, isso é tudo. O resultado é o seguinte e parece bom para mim.

-----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-----

Então, depois que o gtrig me forneceu a maneira correta de criar o certificado, acabei usando esse método para criar um certificado normal ou auto-assinado (se a chave privada for do mesmo keyPair que a chave pública que é)

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;
}

questionAnswers(1)

yourAnswerToTheQuestion