org.globus.cog.security.cert.request.GridCertRequest.java Source code

Java tutorial

Introduction

Here is the source code for org.globus.cog.security.cert.request.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.
// ----------------------------------------------------------------------

/**
 * Copyright (c) 2003, National Research Council of Canada
 * All rights reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this 
 * software and associated documentation files (the "Software"), to deal in the Software 
 * without restriction, including without limitation the rights to use, copy, modify, merge, 
 * publish, distribute, and/or sell copies of the Software, and to permit persons to whom the 
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice(s) and this licence appear in all copies of the Software or 
 * substantial portions of the Software, and that both the above copyright notice(s) and this 
 * license appear in supporting documentation.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE 
 * COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE 
 * FOR ANY CLAIM, OR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL 
 * DAMAGES, OR ANY DAMAGES WHATSOEVER (INCLUDING, BUT NOT 
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWSOEVER 
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
 * OTHERWISE) ARISING IN ANY WAY OUT OF OR IN CONNECTION WITH THE 
 * SOFTWARE OR THE USE OF THE SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * Except as contained in this notice, the name of a copyright holder shall NOT be used in 
 * advertising or otherwise to promote the sale, use or other dealings in this Software 
 * without specific prior written authorization.  Title to copyright in this software and any 
 * associated documentation will at all times remain with copyright holders.
 */

package org.globus.cog.security.cert.request;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import org.bouncycastle.asn1.DERConstructedSet;
import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.jce.PKCS10CertificationRequest;
import org.bouncycastle.util.encoders.Base64;
import org.globus.common.CoGProperties;
import org.globus.gsi.CertUtil;
import org.globus.cog.security.cert.request.OpenSSLKey;
import org.globus.cog.security.cert.request.BouncyCastleOpenSSLKey;
import org.globus.util.PEMUtils;
import org.globus.util.Util;

import java.util.StringTokenizer;

/**
 * GridCertRequest 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.
 * Prompt user for each component of the DN. (Interactive mode)
 * @author Jean-Claude Cote
 */
public final class GridCertRequest {

    public static final String usage = "\n" + "\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). <<Not implemented yet>>"
            + "\n    -host <FQDN>       : Create certificate for a host named <FQDN> <<Not implemented yet>>"
            + "\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";

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

        File fDir = null;
        fDir = new File(certDir);
        if (bOk) {
            // 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 (bOk) {
            if (!fDir.canWrite()) {
                System.out.println("Can't write to " + certDir);
                bOk = false;
            }
        }

        // Check not to overwrite any of these files.
        if (bOk) {
            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.");
                    bOk = false;
                }
            }
        }

        String password = "";
        if (bOk && !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.            
        if (bOk) {

            try {
                System.out.println("writing new private key to " + userKeyFile);
                genCertificateRequest(cn, "ca@gridcanada.ca", 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 = "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.
     */
    static public void genCertificateRequest(String dname, String emailAddressOfCA, String password,
            String privKeyLoc, String certLoc, String certReqLoc) throws Exception {

        String sigAlgName = "MD5WithRSA";
        String keyAlgName = "RSA";

        CertUtil.init();

        // Generate a new key pair.
        KeyPairGenerator keygen = KeyPairGenerator.getInstance(keyAlgName);
        KeyPair keyPair = keygen.genKeyPair();
        PrivateKey privKey = keyPair.getPrivate();
        PublicKey pubKey = keyPair.getPublic();

        // Generate the certificate request.        
        X509Name name = new X509Name(dname);
        DERConstructedSet derSet = new DERConstructedSet();
        PKCS10CertificationRequest request = new PKCS10CertificationRequest(sigAlgName, name, pubKey, derSet,
                privKey);

        // Save the certificate request to a .pem file.
        byte[] data = request.getEncoded();
        PrintStream ps = new PrintStream(new FileOutputStream(certReqLoc));

        // build / delimited name.        
        String certSubject = "";
        StringTokenizer tokens = new StringTokenizer(dname, ",");
        while (tokens.hasMoreTokens()) {
            certSubject = certSubject + "/" + tokens.nextToken();
        }

        ps.print("\n\n" + "Please mail the following certificate request to " + emailAddressOfCA + "\n" + "\n"
                + "==================================================================\n" + "\n"
                + "Certificate Subject:\n" + "\n" + certSubject + "\n" + "\n"
                + "The above string is known as your user certificate subject, and it \n"
                + "uniquely identifies this user.\n" + "\n"
                + "To install this user certificate, please save this e-mail message\n"
                + "into the following file.\n" + "\n" + "\n" + certLoc + "\n" + "\n" + "\n"
                + "      You need not edit this message in any way. Simply \n"
                + "      save this e-mail message to the file.\n" + "\n" + "\n"
                + "If you have any questions about the certificate contact\n" + "the Certificate Authority at "
                + emailAddressOfCA + "\n" + "\n");
        ps.print(toPEM(data));
        ps.close();

        // Save private key to a .pem file.
        OpenSSLKey key = new BouncyCastleOpenSSLKey(privKey);
        if (password.length() != 0) {
            key.encrypt(password);
        }
        key.writeTo(new File(privKeyLoc).getAbsolutePath());
        // set read only permissions
        Util.setFilePermissions(privKeyLoc, 600);

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

    /**
     * Converts to PEM encoding.
     */
    static public 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) {
            throw new RuntimeException("Unexpected error: " + e.getMessage());
        }
        return new String(out.toByteArray());
    }

}