000023440 - Cert-J: Using existing PKCS #10 Certificate Signing Request (CSR) to create CMPCertRequestMessage

Document created by RSA Customer Support Employee on Jun 16, 2016Last modified by RSA Customer Support Employee on Apr 21, 2017
Version 2Show Document
  • View in full screen mode

Article Content

Article Number000023440
Applies ToCert-J
IssueCert-J: Using existing PKCS #10 Certificate Signing Request (CSR) to create CMPCertRequestMessage
Resolution

To use an existing PKCS #10 Certificate Signing Request (CSR) to create a CMPCertRequestMessage, extract the fields from the PKCS #10 certificate request and populate a CertTemplate for the CMPCertRequestMessage.

/*
** Modified version of CMPRequest.java sample
**
*/
package com.rsa.certj.sample.provider;

import com.rsa.certj.sample.common.CertUtilities;
import com.rsa.certj.sample.common.NameUtilities;

import com.rsa.asn1.ASN1;
import com.rsa.certj.CertJ;
import com.rsa.certj.DatabaseService;
import com.rsa.certj.PKIService;
import com.rsa.certj.Provider;
import com.rsa.certj.cert.*;
import com.rsa.certj.crmf.CertTemplate;
import com.rsa.certj.provider.db.MemoryDB;
import com.rsa.certj.provider.path.X509V1CertPath;
import com.rsa.certj.provider.pki.cmp.*;
import com.rsa.certj.provider.revocation.CRLCertStatus;
import com.rsa.certj.spi.pki.PKIStatusInfo;
import com.rsa.jsafe.JSAFE_KeyPair;
import com.rsa.jsafe.JSAFE_SecureRandom;
/* ADDED */
import com.rsa.jsafe.JSAFE_PrivateKey;
import com.rsa.jsafe.JSAFE_PublicKey;
import com.rsa.certj.cert.attributes.V3ExtensionAttribute;
import com.rsa.certj.cert.attributes.X501Attribute;
import com.rsa.certj.cert.extensions.X509V3Extension;
import java.util.Arrays;

import java.io.File;
import java.io.PrintStream;
import java.util.Date;

public class CMPRequest
    extends com.rsa.certj.sample.common.AppApplet {

    public static void main(String[] args)
        throws Exception {
        CMPRequest requester = new CMPRequest();
        requester.go();
    }

    public void go()
        throws Exception {
        println("Starting CMPRequest.java sample program.");
        println();
        if (isApplet()) {
            throw new Error("This sample can not be run as applet.");
        }
        // When using a FIPS 140 compliant version of the toolkit,
        // check that the required power-up self-tests passed
        // before proceeding.
        if (CertJ.isFIPS140Compliant() && !CertJ.selfTestPassed()) {
            println("Cert-J is disabled, power-up self-testing failed");
            return;
        }

        CertJ certJ = null;
        DatabaseService dbService = null;
        PKIService cmpService = null;

        char[] password = {
            'p', 'a', 's', 's', 'w', 'o', 'r', 'd'
        };

        try {
            /* In order to determine the status of a given certificate, a
             * CertJ object needs to be initialized with a group of
             * providers.  At least one database provider is needed, as is a
             * random provider.  Also necessary, are a path provider, and a
             * revocation provider.  These will all be created below. */
            Provider memProvider = null;
            Provider pathProvider = null;
            Provider statusProvider = null;

            println("Creating provider objects: ");
            /* Instantiate the memory provider. */
            print("  MemoryDB...  ");
            memProvider = new MemoryDB("Memory Database Provider");
            println("Success!");

            /* A path verification object is needed, so create an instance
             * of a X509V1CertPath object. */
            print("  X509V1CertPath...  ");
            pathProvider = new X509V1CertPath("Certificate Path Provider");
            println("Success!");

            /* To determine the status of a certificate, a revocation
             * provider is needed. */
            print("  CRLCertStatus...  ");
            statusProvider = new CRLCertStatus("Certificate Status Provider");
            println("Success!");

            Provider[] providers = {
                memProvider, pathProvider, statusProvider
            };

            /* Print out the names of the created providers. */
            println("Instantiated providers:");
            for (int i = 0; i < providers.length; i++) {
                println("  " + providers[i].getName());
            }
            println();

            /* Create a CertJ object that will keep track of the
             * providers. */
            print("Instantiating CertJ object...");
            certJ = new CertJ(providers);
            println("  Done.");

            /* The device is a Crypto-J device specification.  All Crypto-J
             * algorithm objects that Cert-J creates will use the specified
             * device.  For more information on Crypto-J device
             * specifications, consult the Crypto-J documentation. */
            println("Setting device to 'Native/Java'");
            certJ.setDevice("Native/Java");

            dbService = (DatabaseService) certJ.bindServices(CertJ.SPT_DATABASE);

            /* Create the CMP provider and bind to it.  The configuration
             * file specified here contains protocol specific information
             * such as server host name, etc.  See the documentation for the
             * CMP class for more information. */
            Provider cmpProvider = new CMPDebug("CMP Provider", new File("cmp.cfg"), new File("store"));
            ((CMP) cmpProvider).setCMPTCPOptions(CMP.CMPTCP_FLAGS_CLOSE_CONN, -1);
            certJ.addProvider(cmpProvider);

            cmpService = (PKIService) certJ.bindService
                (CertJ.SPT_PKI, "CMP Provider");

            /* In order to create the CertPathCtx, a Database service is
             * needed.  Create one that is bound to all the available
             * database services. */
            dbService = (DatabaseService) certJ.bindServices(CertJ.SPT_DATABASE);

        }
        catch (Exception anyException) {
            println("Caught an exception while setting up providers.");
            anyException.printStackTrace(new PrintStream(this.getOutputStream()));
            throw (anyException);
        }

        X509Certificate recipient =
            CertUtilities.loadX509Certificate("cmpserver.bin");
        if (recipient == null) {
            throw new Exception("Unable to load CMP server certificate.");
        }
        Certificate[] caCertificates = {recipient};
        X509Certificate[] caCerts = new X509Certificate[caCertificates.length];
        for (int i = 0; i < caCerts.length; i++) {
            caCerts[i] = (X509Certificate) caCertificates[i];
        }
        X509Certificate newCert = null;
        JSAFE_KeyPair keyPair = null;

        /* This first try/catch block encapsulates the necessary code for
         * submitting and responding to the initialization
         * request/response messages.  The certificate request process is
         * covered in the next try/catch block below. */
        try {
            /* Generate an RSA public/private key pair. */
            /* CHANGED: COMMENT OUT TO USE AN EXISTING CSR
            keyPair = generateKeys();
            dbService.insertPrivateKeyByPublicKey(keyPair.getPublicKey(),
                keyPair.getPrivateKey());
            */
            /* ADDED: Read PKCS #10 from file */
            PKCS10CertRequest p10 = CertUtilities.loadPKCS10CertRequest("pkcs10.bin");
            if (p10 == null) {
              throw new Exception("Unable to load PKCS #10 certificate request.");
            }
            /* ADDED: Get subject name from PKCS #10 */
            println("Loading subject name.");
            X500Name subjectName = p10.getSubjectName();

            /* ADDED: Get public key from PKCS #10 */
            println("Loading subject public key.");
            JSAFE_PublicKey subjectPublicKey = null;
            try {
              subjectPublicKey = p10.getSubjectPublicKey("Java");
            }
            catch (Exception anyException) {
              println("Error retrieveing subject public key from request.");
              anyException.printStackTrace(new PrintStream(this.getOutputStream()));
              throw (anyException);
            }
            println();


            CertTemplate template = new CertTemplate();
            String algorithm = "DES/CBC/PKCS5Padding";
            int[] algorithmParams = {56};

            /* Create a certificate template.  This template will contain
             * the subject name and public key of the client submitting the
             * initialization request. */
            println("Creating X.509 certificate template...");
            /* CHANGED: Get subject name from PKCS #10
               template.setSubjectName(createSubjectName("Jane Newhire4")); */
            template.setSubjectName(subjectName);
            println("  Subject Name:");
            NameUtilities.printNameInfo(template.getSubjectName(), this);
            /* CHANGED: Get public key from PKCS #10
               template.setSubjectPublicKey(keyPair.getPublicKey()); */
            template.setSubjectPublicKey(subjectPublicKey);
            println("  Public Key:");
            println("-----BEGIN RSA PUBLIC KEY-----", false);
            /* CHANGED
               printBase64(keyPair.getPublicKey().getKeyData("RSAPublicKeyBER")[0]); */
            printBase64(subjectPublicKey.getKeyData("RSAPublicKeyBER")[0]);
            println("-----END RSA PUBLIC KEY-----", false);
            println();

            /* ADDED: Get attributes (at least the V3ExtensionsAttribute)
               from PKCS #10.  Copied from FulfillRequest.java
             */
            /* Convert any attributes in the certificate request to
             * extensions.  convertAttributesToExtensions() will convert
             * only the V3ExtensionsAttribute type.  Handling of other
             * attributes is not automatic, and needs to be handled by the
             * application. */
            println("Converting any attributes to extensions.");
            X509V3Extensions extensions = convertAttributesToExtensions
                (p10.getAttributes());
            println("Extension types found in request:");
            printExtensionTypes(extensions);

            /* If any extensions were converted from attributes, add those
             * to the certificate template now. */
            if (extensions != null) {
                template.setExtensions(extensions);
            }

            println();
            pause();
            println();

            /* Create the CMP IR request object.  This object will hold the
             * required data for a CMP IR message. */
            CMPInitRequestMessage request = new CMPInitRequestMessage(template);

            /* Prepare necessary information for signing the IR request:
             *
             * The request will be signed using signingAlgorithm.  A
             * password based MAC (PBM) will be generated using the
             * specified salt, sharedSecret and iteration
             * count.  The shared secret is determined by the client and
             * server out-of-band. */
            String signingAlgorithm = "MD5/RSA/PKCS1Block01Pad";
            char[] sharedSecret = "interop".toCharArray();
            int iterations = 0; // Specifying 0 will use the default iteration count
            byte[] salt = certJ.getRandomObject().generateRandomBytes(20);

            println("Collecting Proof-of-Possession info:");
            println("Signature algorithm:  " + signingAlgorithm);
            println("Shared secret:  " + new String(sharedSecret));
            println("Digest iterations:  " + iterations);
            println("Salt: ");
            printBuffer(salt);

            CMPPOPGenerationInfoSignature popGenerationInfo =
                new CMPPOPGenerationInfoSignature
                    (signingAlgorithm, sharedSecret, salt, iterations);
            println();

            /* ADDED/CHANGED: Get existing private key */
            JSAFE_PrivateKey privateKey = CertUtilities.loadPKCS8PrivateKey("pkcs8.bin", this);
            /* ADDED: Check that private key corresponds to the public key
               in the PKCS #10 cert request */
            /* ADDED: Compare moduli */
            if (!Arrays.equals(subjectPublicKey.getKeyData("RSAPublicKey")[0],
                               privateKey.getKeyData("RSAPrivateKeyCRT")[0])) {
              println("Modulus in private key does not match public key.");
            }

            /* ADDED: Compare public exponents */
            if (!Arrays.equals(subjectPublicKey.getKeyData("RSAPublicKey")[1],
                               privateKey.getKeyData("RSAPrivateKeyCRT")[1])) {
              println("Public exponent in private key does not match public key.");
            }

            /* Generate the proof of possesion for this IR request.  Proof
             * of possesion provides the server with the assurance that the
             * client requesting a certificate is the entity that controls
             * the private key corresponding to the public key in the
             * CertTemplate object. */
            println("Generating Proof-of-Possession.");
            cmpService.generateProofOfPossession
              /* CHANGED: Use existing private key */
              /* (request, keyPair.getPrivateKey(), popGenerationInfo); */
                (request, privateKey, popGenerationInfo);

            println();
            pause();
            println();

            /* Generate the protection info for the message.  This
             * information is used when the message is sent to the server.
             * The information specified here deteremines how the message is
             * packaged for transmission to the server.  See the JavaDoc for
             * the CMPProtectInfo class for more information on construction
             * parameters. */
            String protectAlgorithm = "PBE/HMAC/SHA1/PKIXPBE-1024";
            byte[] keyID = "interop".getBytes();

            println("Collecting message protection info:");
            println("Protection algorithm:  " + protectAlgorithm);
            println("Shared secret:  " + new String(sharedSecret));
            println("Key ID:  ");
            printBuffer(keyID);
            println("Recipient:");
            NameUtilities.printNameInfo(recipient.getSubjectName(), this);
            CMPProtectInfo protectInfo = new CMPProtectInfo
                (protectAlgorithm, sharedSecret, keyID, recipient,
                    caCerts, dbService);

            println();
            pause();
            println();

            /* Now, send the IR request message to the server.  When this
             * call completes, the CMPResponseCommon object will contain the
             * information that existed in the server's response message.
             * If the request was successful, this will include a client
             * certificate.  In this example, we won't specify a destination
             * database for any resulting certificates or CRLs. */
            print("Sending request...  ");
            CMPResponseCommon response =
                (CMPResponseCommon) cmpService.sendRequest
                (request, protectInfo, null);
            println("Done!");

            /* Check to see if an error response was received. */
            if (response instanceof CMPErrorMessage) {
                CMPErrorMessage error = (CMPErrorMessage) response;
                if (error.errorCodePresent()) {
                    println("Error code received was:  " + error.getErrorCode());
                    println("Error strings are:  ");
                    String[] errorStrings = error.getErrorDetails();
                    for (int i = 0; i < errorStrings.length; i++) {
                        println(". " + errorStrings[i]);
                    }
                }
                else {
                    println("Error received, but no error details.");
                    println("Checking PKIStatusInfo...");
                    PKIStatusInfo statusInfo = response.getStatusInfo();
                    println("Status code = " + statusInfo.getStatus());
                    String[] statusStrings = statusInfo.getStatusStrings();
                    for (int i = 0; i < statusStrings.length; i++) {
                        println(". " + statusStrings[i]);
                    }
                }
            }
            /* Should be a CMPCertResponseCommon because we submitted a
             * certificate request. */
            else {
                /* Get the issued client certificate. */
                print("Retrieving certificates");
                newCert = (X509Certificate) response.getCertificate();
                dbService.insertCertificate(newCert);
                /* CHANGED: Use private key
                   dbService.insertPrivateKeyByCertificate(newCert, keyPair.getPrivateKey()); */
                   dbService.insertPrivateKeyByCertificate(newCert, privateKey);

                /* Get the returned CA certificates.  This list may or may not
                 * duplicate entries specified earlier in the CMPProtectInfo
                 * constructor. */
                print(", CA certificates");
                Certificate[] newCACerts = response.getCACerts();
                caCerts = new X509Certificate[newCACerts.length];
                for (int i = 0; i < caCerts.length; i++) {
                    caCerts[i] = (X509Certificate) newCACerts[i];
                }
                println(".  Done!");

                /* Print out some information about the returned certificate. */
                println("Returned certificate data:");
                println("Issuer:");
                NameUtilities.printNameInfo(newCert.getIssuerName(), this);
                println();

                println("Subject:");
                NameUtilities.printNameInfo(newCert.getSubjectName(), this);
                println();

                println("Serial Number:");
                printBuffer(newCert.getSerialNumber());

                /* At this point, we would decide if we approved of this
                 * certificate or not.  In this example, we'll specify that we
                 * do approve and send that to the server. */
                println("Accepting certificate and sending confirmation.");
                int confirmationValue = PKIStatusInfo.PKI_STATUS_GRANTED;

                CMPCertConfirmMessage certConf = new CMPCertConfirmMessage
                    ((CMPCertResponseCommon) response,
                        new PKIStatusInfo(confirmationValue, 0, null, 0));

                CMPResponseCommon confirmation =
                    (CMPResponseCommon) cmpService.sendRequest
                    (certConf, protectInfo, null);
                println("Sent!");
            }
        }
        catch (Exception any) {
            any.printStackTrace();
            throw (any);
        }

        println();
        println("CMPRequest sample program finished.");
    }

    public X500Name createSubjectName(String commonName)
        throws NameException {
        X500Name name = new X500Name();
        RDN rdn = new RDN();

        rdn.addNameAVA(new AttributeValueAssertion
            (AttributeValueAssertion.COUNTRY_NAME, null,
                ASN1.PRINT_STRING, "US"));
        name.addRDN(rdn);

        rdn = new RDN();
        rdn.addNameAVA(new AttributeValueAssertion
            (AttributeValueAssertion.STATE_NAME, null,
                ASN1.PRINT_STRING, "Oregon"));
        name.addRDN(rdn);

        rdn = new RDN();
        rdn.addNameAVA(new AttributeValueAssertion
            (AttributeValueAssertion.ORGANIZATION_NAME, null,
                ASN1.PRINT_STRING, "TestCo, Inc."));
        name.addRDN(rdn);

        rdn = new RDN();
        rdn.addNameAVA(new AttributeValueAssertion
            (AttributeValueAssertion.LOCALITY_NAME, null,
                ASN1.PRINT_STRING, "Eugene"));
        name.addRDN(rdn);

        rdn = new RDN();
        rdn.addNameAVA(new AttributeValueAssertion
            (AttributeValueAssertion.STREET_ADDRESS, null,
                ASN1.PRINT_STRING, "82 W. 7th Ave"));
        name.addRDN(rdn);

        rdn = new RDN();
        rdn.addNameAVA(new AttributeValueAssertion
            (AttributeValueAssertion.COMMON_NAME, null,
                ASN1.PRINT_STRING, commonName));
        name.addRDN(rdn);

        return (name);
    }


    /* Generate a RSA key pair. */
    public JSAFE_KeyPair generateKeys()
        throws Exception {
        JSAFE_KeyPair keyPairGenerator = null;

        println("Generating RSA key pair.");

        try {
            /* Generate RSA keys. */
            keyPairGenerator = JSAFE_KeyPair.getInstance("RSA", "Java");
            int[] parameters = {
                1024, 65537
            };
            println("" + parameters[0] + "-bit modulus.");
            println("Public Exponent:  " + parameters[1]);

            /* Initialize the random number generator.  The following method
             * of seeding is insecure, and should not be used in a finished
             * application.  Refer to the Crypto-J documentation for more
             * information regarding how to properly seed a pseudo-random
             * number generator. */
            JSAFE_SecureRandom random = (JSAFE_SecureRandom)
                JSAFE_SecureRandom.getInstance("SHA1Random", "Java");
            random.seed((new Date()).toString().getBytes());

            keyPairGenerator.generateInit(null, parameters, random);

            println("Generating...");
            keyPairGenerator.generate();

            println("Done generating key pair.");

            return (keyPairGenerator);
        }
        catch (Exception anyException) {
            anyException.printStackTrace();
            println("Error during key pair generation.");
            throw (anyException);
        }
    }

  /* ADDED: Copied from FulfillRequest.java sample */

    /* This function will convert any extensions masquerading as
     * attributes in the certificate request to extensions for the
     * certificate.  Dealing with other attributes is defined by the
     * policy of the Certification Authority.  Dealing with such
     * attributes is not covered here.  */
    public X509V3Extensions convertAttributesToExtensions
        (X501Attributes attributes) {
        X509V3Extensions returnValue = null;
        println("  Processing attributes...");

        /* If no attributes were passed in, they cannot be converted. */
        if (attributes == null) {
            println("No attributes.");
            return (null);
        }

        /* Get the V3ExtensionAttribute object for the attributes,
         * if it exists. */
        V3ExtensionAttribute v3Extensions = null;
        v3Extensions = (V3ExtensionAttribute) attributes.getAttributeByType
            (X501Attribute.X509_V3_EXTENSION);

        if (v3Extensions != null) {
            /* Get the X509V3Extensions object containing the extensions. */
            returnValue = v3Extensions.getV3ExtensionAttribute();
        }
        else {
            returnValue = null;
        }

        println("Done processing attributes.");
        println();
        return (returnValue);
    }

    public void printExtensionTypes(X509V3Extensions extensions) {
        if (extensions != null) {
            int number = extensions.getExtensionCount();
            X509V3Extension ext = null;

            for (int i = 0; i < number; i++) {
                try {
                    ext = extensions.getExtensionByIndex(i);
                    println("  Extension " + (i + 1) + ":  " +
                        ext.getExtensionTypeString());
                }
                catch (CertificateException ce) {
                    println("Caught exception obtaining extension type info.");
                    ce.printStackTrace(new PrintStream(this.getOutputStream()));
                }
            }
        }
    }
}

Legacy Article IDa33915

Attachments

    Outcomes