org.globus.cog.security.certrequest.GridCertRequest.java Source code

Java tutorial

Introduction

Here is the source code for org.globus.cog.security.certrequest.GridCertRequest.java

Source

/*
 * Swift Parallel Scripting Language (http://swift-lang.org)
 * Code from Java CoG Kit Project (see notice below) with modifications.
 *
 * Copyright 2005-2014 University of Chicago
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// ----------------------------------------------------------------------
// This code is developed as part of the Java CoG Kit project
// The terms of the license can be found at http://www.cogkit.org/license
// This message may not be removed or altered.
// ----------------------------------------------------------------------

package org.globus.cog.security.certrequest;

import java.io.*;
import java.util.Vector;
import java.util.StringTokenizer;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;

import org.globus.common.CoGProperties;
import org.globus.gsi.CertUtil;
import org.globus.gsi.OpenSSLKey;
import org.globus.gsi.TrustedCertificates;
import org.globus.gsi.bc.BouncyCastleOpenSSLKey;
import org.globus.util.PEMUtils;
import org.globus.util.Util;

import COM.claymoresystems.cert.CertRequest;
import COM.claymoresystems.cert.X509Name;

import org.bouncycastle.asn1.DERConstructedSet;
//import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.jce.PKCS10CertificationRequest;
import org.bouncycastle.util.encoders.Base64;

import org.apache.log4j.Logger;

/**
 * CertRequest Command Line Client
 *
 * Things remaining to be done:
 * Support for host or service certificate request. Perhaps this should not be
 * part of this tool since the COG kit is mostly a client library.
 * @author Jean-Claude Cote
 * @author Vladimir Silva
 */
public final class GridCertRequest {
    private static Logger logger = Logger.getLogger(GridCertRequest.class.getName());

    public static final String usage = "\ngrid-cert-request [-help] [ options ...]" + "\n" + "\n  Example Usage:"
            + "\n" + "\n    Creating a user certifcate:" + "\n      grid-cert-request" + "\n"
            + "\n    Creating a host or gatekeeper certifcate:" + "\n      grid-cert-request -host [my.host.fqdn]"
            + "\n" + "\n    Creating a LDAP server certificate:"
            + "\n      grid-cert-request -service ldap -host [my.host.fqdn]" + "\n" + "\n  Options:" + "\n"
            + "\n    -version           : Display version" + "\n    -?, -h, -help,     : Display usage"
            + "\n    -usage" + "\n    -cn <name>,        : Common name of the user" + "\n    -commonname <name>"
            + "\n    -service <service> : Create certificate for a service. Requires"
            + "\n                         the -host option and implies that the generated"
            + "\n                         key will not be password protected (ie implies -nopw)."
            + "\n    -host <FQDN>       : Create certificate for a host named <FQDN>"
            + "\n    -dir <dir_name>    : Changes the directory the private key and certificate"
            + "\n                         request will be placed in. By default user"
            + "\n                         certificates are placed in /home/user/.globus, host"
            + "\n                         certificates are placed in /etc/grid-security and"
            + "\n                         service certificates are place in"
            + "\n                         /etc/grid-security/<service>."
            + "\n    -prefix <prefix>   : Causes the generated files to be named"
            + "\n                         <prefix>cert.pem, <prefix>key.pem and"
            + "\n                         <prefix>cert_request.pem"
            + "\n    -nopw,             : Create certificate without a passwd" + "\n    -nodes,"
            + "\n    -nopassphrase," + "\n    -verbose           : Don't clear the screen <<Not used>>"
            //            + "\n    -int[eractive]     : Prompt user for each component of the DN <<Not implemented yet>>"
            + "\n    -force             : Overwrites preexisting certifictes";

    private static String message = "\nA certificate request and private key will be created."
            + "\nYou will be asked to enter a PEM pass phrase."
            + "\nThis pass phrase is akin to your account password," + "\nand is used to protect your key file."
            + "\nIf you forget your pass phrase, you will need to" + "\nobtain a new certificate.\n";

    private static String cn = null;
    private static String service = null;
    private static String gatekeeper = null;
    private static String hostName = null;
    private static String certDir = null;
    private static String certFile = null;
    private static String keyFile = null;
    private static String reqFile = null;
    private static boolean noPswd = false;
    private static boolean interactive = false;
    private static boolean force = false;
    private static boolean resubmit = false;
    private static String version = "1.0";
    private static boolean verbose = false;
    private static String prefix = "user";

    /**
     * Main
     * @param args main prog args
     */
    public static void main(String[] args) {

        boolean bOk = parseCmdLine(args);

        String userCertFile = "";
        String userKeyFile = "";
        String userCertReqFile = "";

        if (bOk) {
            // Get default location of cert.
            CoGProperties props = CoGProperties.getDefault();

            // If no alternate directory specified.
            if (certDir == null) {
                userCertFile = props.getUserCertFile();
                userKeyFile = props.getUserKeyFile();
                // Get root dir of default cert location.
                int pos = userKeyFile.lastIndexOf(File.separator);
                certDir = userKeyFile.substring(0, pos + 1);
            } else {
                // If alternate directory specified set cert locations.
                if (certDir.endsWith(File.separator) == false) {
                    certDir += File.separator;
                }
                userCertFile = certDir + prefix + "cert.pem";
                userKeyFile = certDir + prefix + "key.pem";
            }

            // Cert request file name.
            userCertReqFile = userCertFile.substring(0, userCertFile.length() - 4) + "_request.pem";
        } else {
            System.exit(-1);
        }

        File fDir = null;
        fDir = new File(certDir);

        // Create dir if does not exists.
        if (!fDir.exists()) {
            fDir.mkdir();
        }

        // Make sure directory exists.
        if (!fDir.exists() || !fDir.isDirectory()) {
            System.out.println("The directory " + certDir + " does not exists.");
            bOk = false;
        }

        // Make sure we can write to it.
        if (!fDir.canWrite()) {
            System.out.println("Can't write to " + certDir);
            bOk = false;
        }

        // Check not to overwrite any of these files.
        if (force == false) {
            boolean bFileExists = false;
            File f = new File(userKeyFile);
            if (f.exists()) {
                System.out.println(userKeyFile + " exists");
                bFileExists = true;
            }
            f = new File(userCertFile);
            if (f.exists()) {
                System.out.println(userCertFile + " exists");
                bFileExists = true;
            }
            f = new File(userCertReqFile);
            if (f.exists()) {
                System.out.println(userCertReqFile + " exists");
                bFileExists = true;
            }

            if (bFileExists) {
                System.out.println("If you wish to overwrite, run the script again with -force.");
                System.exit(-1);
            }
        }

        String password = "";

        // host cert?
        if (service != null && service.length() != 0) {
            noPswd = true;
            cn = "host/" + hostName;
        }

        if (!noPswd) {
            // Get password from user.
            bOk = false;
            int attempts = 0;

            System.out.println(message);

            while (bOk == false && attempts < 3) {
                password = Util.getInput("Enter PEM pass phrase: ");
                String password2 = Util.getInput("Verify password Enter PEM pass phrase: ");
                if (password.compareTo(password2) != 0) {
                    System.out.println("Verify failure");
                } else {
                    if (password.length() < 4) {
                        System.out.println("phrase is too short, needs to be at least 4 chars");
                    } else {
                        bOk = true;
                    }
                }
                attempts++;
            }
        }

        // Generate cert request.
        try {
            System.out.println("Writing new request to " + userCertFile);
            System.out.println("Writing new private key to " + userKeyFile);

            makeCertificateRequest(cn, password, userKeyFile, userCertFile, userCertReqFile);
        } catch (Exception e) {
            System.out.println("error: " + e);
            e.printStackTrace();
        }
    }

    protected static boolean parseCmdLine(String[] args) {
        boolean bOk = true;
        if (args.length == 0) {
            System.out.println(usage);
            bOk = false;
        } else {
            for (int i = 0; i < args.length && bOk; i++) {
                if (args[i].equalsIgnoreCase("-version")) {
                    System.out.println(version);
                } else if (args[i].equalsIgnoreCase("-help") || args[i].equalsIgnoreCase("-h")
                        || args[i].equalsIgnoreCase("-?")) {
                    System.out.println(usage);
                    bOk = false;
                } else if (args[i].equalsIgnoreCase("-cn") || args[i].equalsIgnoreCase("-commonname")) {
                    // common name specified
                    cn = args[++i];
                } else if (args[i].equalsIgnoreCase("-service")) {
                    // user certificate directory specified
                    service = args[++i];
                }

                else if (args[i].equalsIgnoreCase("-host")) {
                    // host name specified
                    service = prefix = "host";
                    hostName = args[++i];
                } else if (args[i].equalsIgnoreCase("-dir")) {
                    // user certificate directory specified
                    certDir = args[++i];
                } else if (args[i].equalsIgnoreCase("-prefix")) {
                    prefix = args[++i];
                } else if (args[i].equalsIgnoreCase("-nopw") || args[i].equalsIgnoreCase("-nodes")
                        || args[i].equalsIgnoreCase("-nopassphrase")) {
                    // no password
                    noPswd = true;
                } else if (args[i].equalsIgnoreCase("-verbose")) {
                    verbose = true;
                }
                /*
                else if ((args[i].equalsIgnoreCase("-int")) || (args[i].equalsIgnoreCase("-interactive"))) {
                // interactive mode
                interactive = true;
                }
                */
                else if (args[i].equalsIgnoreCase("-force")) {
                    // overwrite existing credentials
                    force = true;
                } else {
                    System.out.println("Error: argument #" + i + "(" + args[i] + ") : unknown");
                }
            }
        }
        return bOk;
    }

    /**
     * Generates a encrypted private key and certificate request.
     * @param dname common name
     * @param password CSR password
     * @param privKeyLoc Full path to priv key
     * @param certLoc Full path to user cert (Will be empty)
     * @param certReqLoc Full path to CSR
     * @throws java.lang.Exception
     */
    static public void makeCertificateRequest(String dname, String password, String privKeyLoc, String certLoc,
            String certReqLoc) throws Exception {
        logger.debug("genCertificateRequest cn=" + dname + " pwd=" + password + " Priv key=" + privKeyLoc
                + " Cert loc=" + certLoc + " CSR loc=" + certReqLoc);
        makeCertificateRequest(dname, password, new FileOutputStream(privKeyLoc), new FileOutputStream(certReqLoc));

        // set read only permissions
        Util.setFilePermissions(privKeyLoc, 600);

        // Create an empty cert file.
        File f = new File(certLoc);
        f.createNewFile();
    }

    /**
     * Certficate generation main function
     * @param dname Distinguished name (e.g John Doe)
     * @param password CSR password
     * @param outKey Out stream to the private key
     * @param outCertReq CSR out stream
     * @throws java.lang.Exception if error
     */
    static public void makeCertificateRequest(String dname, String password, OutputStream outKey,
            OutputStream outCertReq) throws Exception {
        String sigAlgName = "MD5WithRSA";
        String keyAlgName = "RSA";

        CertUtil.init();

        // load CA certs and grab the DN for the request (every thing but the CN part)
        TrustedCertificates tcerts = TrustedCertificates.getDefaultTrustedCertificates();
        String CADN = "";

        if (tcerts != null) {
            X509Certificate[] caCerts = tcerts.getCertificates();
            if (caCerts == null) {
                System.out.println("Warning: No trusted certificates found.");
            } else {
                CADN = (((caCerts.length == 0) || (caCerts[0] == null)) ? ""
                        : (caCerts[0].getSubjectDN().toString()));
            }
        } else {
            System.out.println("Warning: No trusted certificates found.");
        }
        if (CADN == null) {
            CADN = "";
        }

        // replace CA dn's CN elem with the user's CN
        if (CADN.indexOf("CN") != -1)
            CADN = CADN.substring(0, CADN.indexOf("CN"));
        if (CADN.indexOf("cn") != -1)
            CADN = CADN.substring(0, CADN.indexOf("cn"));

        dname = CADN + "CN=" + dname;

        logger.debug("Using DN=" + dname);
        KeyPair kp = null;
        byte[] data = null;

        // pure TLS can only create encrypted CSRs, OpenSSLKey gives an exception when encrypting
        if (password.length() != 0) {
            StringWriter sw = new StringWriter(); // will contain the priv key PEM
            BufferedWriter bw = new BufferedWriter(sw);

            kp = CertRequest.generateKey(keyAlgName, 1024, password, bw, true); // gen pub/priv keys
            data = CertRequest.makePKCS10Request(kp, makePTLSX509Name(dname));

            // save encrypted private key
            outKey.write(sw.toString().getBytes());

        } else {
            // OpenSSLKey gives an exception when encrypting, thus
            // use for unenc CSRs only...until fixed
            kp = KeyPairGenerator.getInstance(keyAlgName).generateKeyPair();
            data = new PKCS10CertificationRequest(sigAlgName, new org.bouncycastle.asn1.x509.X509Name(dname),
                    kp.getPublic(), new DERConstructedSet(), kp.getPrivate()).getEncoded();

            // save unencrypted priv key
            OpenSSLKey key = new BouncyCastleOpenSSLKey(kp.getPrivate());
            key.writeTo(outKey);
        }

        // Save the certificate request to a .pem file.
        PrintStream ps = new PrintStream(outCertReq);
        ps.println(makeRequestInfoHeader(dname));
        ps.print(toPEM(data));
        ps.close();

    }

    /**
     * Converts to PEM encoding.
     * @param data cert bytes
     * @return pem enoded CSR
     */
    static private String toPEM(byte[] data) {
        byte[] enc_data = Base64.encode(data);
        String header = "-----BEGIN CERTIFICATE REQUEST-----";
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            PEMUtils.writeBase64(out, header, enc_data, "-----END CERTIFICATE REQUEST-----");
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("Unexpected error: " + e.getMessage());
        }
        return new String(out.toByteArray());
    }

    /* Cert RQ info header */
    static private String makeRequestInfoHeader(String subject) {
        StringBuffer buff = new StringBuffer(
                "This is a Certificate Request file:\nIt should be mailed to to a CA for signature");
        buff.append("\n===============================================================");
        buff.append("\nCertificate Subject:\n\t");
        buff.append(subject);
        buff.append("\n\n");
        return buff.toString();
    }

    /*
     * makeCertDN: Creates an X509 Identity based on a string subject
     * e.g:  "C=US,O=Grid,OU=OGSA,OU=Foo,CN=John Doe"
     */
    private static X509Name makePTLSX509Name(String subject) throws Exception {
        Vector tdn = new Vector();
        Vector elems = new Vector();
        StringTokenizer st = new StringTokenizer(subject, ",");

        for (; st.hasMoreTokens();) {
            String s = st.nextToken(); // [key=value]
            if (s.indexOf("=") == -1)
                throw new Exception("Invalid subject format: " + subject + " Offending value: " + s);

            String key = s.substring(0, s.indexOf("=")).trim();
            String val = s.substring(s.indexOf("=") + 1).trim();

            if (val == null || val.equals(""))
                throw new Exception("Invalid subject format: " + subject + " Offending value: " + s);

            //logger.debug(key + "=" + val);
            String[] temp = { key, val };
            tdn.addElement(temp);
        }
        // COM.claymoresystems.cert (puretls.jar)
        return CertRequest.makeSimpleDN(tdn);
    }
}