javacertificatex509certificatebouncycastlesubject-alternative-name

Can somebody help me to implement extension Subject Alternative Names using BouncyCastle?


I have some string, that is separated with comma. I have to add all extension that match any of GeneralName for Subject Alternative Names extension. Can somebody finish for loop for me?

    @Override
public boolean saveKeypair(String arg0) {

    KeyPair keyPair = generateKeyPair(Integer.parseInt(access.getPublicKeyParameter()));

    PrivateKey privateKey = keyPair.getPrivate();
    PublicKey publicKey = keyPair.getPublic();

    X500Name name = new X500Name(access.getSubject());
    BigInteger serial = new BigInteger(access.getSerialNumber());
    Date notBefore = access.getNotBefore();
    Date notAfter = access.getNotAfter();
    X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(name, serial, notBefore, notAfter, name,
            publicKey);

    // BEGIN extensions
    // certificate policies
    boolean isCritPol = access.isCritical(3);
    PolicyInformation[] policies = new PolicyInformation[1];
    policies[0] = new PolicyInformation(new ASN1ObjectIdentifier("2.16.840.1.101.2.1.11.5"),
            new DERSequence(new PolicyQualifierInfo(access.getCpsUri())));
    try {
        certBuilder.addExtension(Extension.certificatePolicies, isCritPol, new CertificatePolicies(policies));
    } catch (CertIOException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }

    // END CP

    // subject alternative name
    List<GeneralName> altNames = new ArrayList<GeneralName>();
    String [] altSubNames = access.getAlternativeName(5);

    for(String altName : altSubNames){
        // I NEED THIS LOOP, AND I DON'T KNOW HOW TO DO IT
    }

    // END SAN

    // END extensions


    try {
        // Content Signer
        Security.addProvider(new BouncyCastleProvider());
        ContentSigner sigGen = new JcaContentSignerBuilder(signatureAlgorithm).setProvider(providerName)
                .build(privateKey);

        // Certificate
        X509Certificate certificate = new JcaX509CertificateConverter().setProvider(providerName)
                .getCertificate(certBuilder.build(sigGen));

     certificate.verify(publicKey);

        X509Certificate[] chain = new X509Certificate[1];       
        chain[0] = certificate;
        keyStore.setKeyEntry(arg0, privateKey, password.toCharArray(), chain);

    } catch (OperatorCreationException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (CertificateException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchProviderException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (SignatureException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (KeyStoreException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    return true;
}

// BEGIN of functions for saveKeypair

public KeyPair generateKeyPair(int keySize) {
    KeyPair keyPair = null;

    try {
        KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(algorithm);
        keyGenerator.initialize(keySize);
        keyPair = keyGenerator.generateKeyPair();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }

    return keyPair;
}



// END of functions for saveKeypair

The rest of the function is working.

I am using BouncyCastle in Java. altSubName is an array, of some Strings. And those Strings should be somehow checked which of SubjectAlternativeName they are, and the Extension containing all that general names should be added.


Solution

  • According to RFC 5280, some fields of the Subject Alternative Name extension have a defined format:

    And so on (take a look at the RFC 5280 link, it's very detailed).

    So, to know what's the field corresponding to each String, you must check if they have the format defined in each of the fields.

    For rfc822Name I've found this monster regex:

    String rfc822Regex = "(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])";
    String s = "test_ad.fade@mail.com";
    if (s.matches(rfc822Regex)) {
        // is valid email
    }
    

    For the other fields, you can search regexes for each specific format. I think the only problem is the otherName field, because it can have any format (it must be specified for each Certificate Authority):

    OtherName ::= SEQUENCE {
        type-id    OBJECT IDENTIFIER,
        value      [0] EXPLICIT ANY DEFINED BY type-id }
    

    Anyway, a generic code will be like this (assuming you already searched how to validate each of the specific formats):

    // check the format and add with the correct field type
    if (isValidEmail(altName)) {
        altNames.add(new GeneralName(GeneralName.rfc822Name, "user@mail.com"));
    } else if (isValidDnsName(altName)) {
        altNames.add(new GeneralName(GeneralName.dNSName, "test.com"));
    } else if (isValidIpAddress(altName)) {
        altNames.add(new GeneralName(GeneralName.iPAddress, "127.0.0.1"));
    }
    // ... and so on, for all GeneralName types
    

    Then you add the extension to certBuilder:

    GeneralNames subjectAltNames = GeneralNames.getInstance(new DERSequence((GeneralName[]) altNames.toArray(new GeneralName[] {})));
    certBuilder.addExtension(Extension.subjectAlternativeName, false, subjectAltNames);