net.java.bd.tools.security.OnlineKeytool.java Source code

Java tutorial

Introduction

Here is the source code for net.java.bd.tools.security.OnlineKeytool.java

Source

/*
 * Copyright (c) 2009, Sun Microsystems, Inc.
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *  * Neither the name of Sun Microsystems nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *  Note:  In order to comply with the binary form redistribution
 *         requirement in the above license, the licensee may include
 *         a URL reference to a copy of the required copyright notice,
 *         the list of conditions and the disclaimer in a human readable
 *         file with the binary form of the code that is subject to the
 *         above license.  For example, such file could be put on a
 *         Blu-ray disc containing the binary form of the code or could
 *         be put in a JAR file that is broadcast via a digital television
 *         broadcast medium.  In any event, you must include in any end
 *         user licenses governing any code that includes the code subject
 *         to the above license (in source and/or binary form) a disclaimer
 *         that is at least as protective of Sun as the disclaimers in the
 *         above license.
 *
 *         A copy of the required copyright notice, the list of conditions and
 *         the disclaimer will be maintained at
 *         https://hdcookbook.dev.java.net/misc/license.html .
 *         Thus, licensees may comply with the binary form redistribution
 *         requirement with a text file that contains the following text:
 *
 *             A copy of the license(s) governing this code is located
 *             at https://hdcookbook.dev.java.net/misc/license.html
 */

package net.java.bd.tools.security;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import java.math.BigInteger;

import java.net.URL;

import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.KeyStore;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.security.spec.RSAPublicKeySpec;

import java.util.Calendar;
import java.util.Date;

import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.x509.X509V3CertificateGenerator;

//import sun.misc.HexDumpEncoder;

/**
 * OnlineKeyTool is used for importing the private and
 * public keys from BDA provided files into a Java Keystore.
 */
public class OnlineKeytool {

    String keyFile;
    String crtFile;
    X509Certificate cert;
    PrivateKey privateKey;

    // Used by the command line tool
    static String keystoreFile = SecurityUtil.DEF_KEYSTORE_FILE;
    static String keystorePassword = SecurityUtil.DEF_KEYSTORE_PASSWORD;
    static String alias = SecurityUtil.DEF_ONLINE_CERT_ALIAS;
    static String keyPass = SecurityUtil.DEF_ONLINEKEY_PASSWORD;
    static KeyStore ks;
    public boolean debug = false;

    /**
     * Creates an OnlineKeytool instance that reads private and
     * public keys from the given files
     * <p>
     * @param kfile Private key file.
     * @param cfile online.crt file
     * @param debug
     */
    OnlineKeytool(String kfile, String cfile, boolean debug) {
        keyFile = kfile;
        crtFile = cfile;
        this.debug = debug;
    }

    /** Adds a new KeyEntry to the given keystore
     *  The private key and the certificate are created using the binary files.
     * <p>
     * @param store
     * @param alias
     * @param keypass
     * @return
     * @throws java.lang.Exception
     */
    public KeyStore importKeys(KeyStore store, String alias, String keypass) throws Exception {
        createPrivateKey();
        createSelfSignedCert();
        store.setKeyEntry(alias, this.privateKey, keypass.toCharArray(), new X509Certificate[] { this.cert });
        return store;
    }

    /**
     *  Creates a private key from the key CRT parameters
     * <p>
     * @throws java.lang.Exception
     */
    public void createPrivateKey() throws Exception {
        FileInputStream fin = new FileInputStream(keyFile);
        OnlinePrivateKeyReader kr = new OnlinePrivateKeyReader(fin);
        BigInteger m = kr.readModulus().abs();
        BigInteger pbe = kr.readPublicExponent();
        BigInteger pre = kr.readPrivateExponent();
        BigInteger p = kr.readPrimeP();
        BigInteger q = kr.readPrimeQ();
        BigInteger pe = kr.readPrimeExponentP();
        BigInteger qe = kr.readPrimeExponentQ();
        BigInteger coeff = kr.readCrtCoefficient();
        fin.close();
        /*if (debug) {
        HexDumpEncoder hexDump = new HexDumpEncoder();
        System.out.println("PrimeP:" + hexDump.encodeBuffer(p.toByteArray()));
        System.out.println();
        System.out.println("PrimeQ:" + hexDump.encodeBuffer(q.toByteArray()));
        System.out.println();
        System.out.println("Modulus:" + hexDump.encodeBuffer(m.toByteArray()));
        System.out.println();
        System.out.println("Private e:" + hexDump.encodeBuffer(pre.toByteArray()));
        System.out.println();
        System.out.println("Exponent e:" + hexDump.encodeBuffer(pe.toByteArray()));
        System.out.println();
        System.out.println("Exponent Q:" + hexDump.encodeBuffer(qe.toByteArray()));
        System.out.println();
        System.out.println("CRT coeff :" + hexDump.encodeBuffer(coeff.toByteArray()));
        System.out.println();
        }*/
        RSAPrivateCrtKeySpec privateSpec = new RSAPrivateCrtKeySpec(m, pbe, pre, p, q, pe, qe, coeff);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        this.privateKey = factory.generatePrivate(privateSpec);
    }

    /**
     * Creates a self signed certificate using the public key
     * from the "online.crt" file. See BD-ROM specification part 3.2,
     * version 2.2 Annex DD.
     * The certificate fields other than the key pair do not matter.
     * The certificate is just a place holder in the keystore.
     * The private key is created from the BDA binary file.
     * <p>
     * @throws java.lang.Exception
     */
    public void createSelfSignedCert() throws Exception {
        DataInputStream dis = new DataInputStream(new FileInputStream(crtFile));

        String typeIndicator = SecurityUtil.readISO646String(dis, 4);
        if (!typeIndicator.equals("OCRT")) {
            throw new RuntimeException("Invalid TypeIndicator: " + typeIndicator + " in " + crtFile);
        }
        String versionNo = SecurityUtil.readISO646String(dis, 4);
        if (!versionNo.equals("0200")) {
            throw new RuntimeException("Invalid Version No:" + versionNo + " in " + crtFile);
        }
        // reserved bytes
        dis.skipBytes(32);
        int certVerNo = dis.readInt();
        if (certVerNo != 0) {
            throw new RuntimeException("Invalid Certificate Version No:" + certVerNo);
        }
        int contentOwnerId = dis.readInt();
        if (debug) {
            System.out.println("Content owner ID:" + contentOwnerId);
        }
        // reserved bytes
        dis.skipBytes(32);
        String contentOwnerName = SecurityUtil.readISO646String(dis, 256);
        if (debug) {
            System.out.println("Content owner Name:" + contentOwnerName);
        }
        String dn = "cn=" + contentOwnerName; // used in cert

        // read the public key modulus 'n'
        byte[] buf = new byte[256];
        int read = dis.read(buf);
        if (read == -1) {
            throw new IOException("Reached end of file before finished reading the file:" + crtFile);
        }
        BigInteger modulus = new BigInteger(1, buf);
        /*HexDumpEncoder hexDump = null;
        if (debug) {
        System.out.println("public modulus:");
        hexDump = new HexDumpEncoder();
        System.out.println(hexDump.encodeBuffer(modulus.toByteArray()));
        }*/
        BigInteger exponent = BigInteger.valueOf(65537);
        RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = factory.generatePublic(spec);

        // create a self signed cert for importing into the keystore.
        X509V3CertificateGenerator cg = new X509V3CertificateGenerator();
        cg.reset();
        Calendar calendar = Calendar.getInstance();
        calendar.set(0000, 1, 1);
        Date validFrom = calendar.getTime();
        calendar.clear();
        calendar.set(9999, 1, 1);
        Date validTo = calendar.getTime();
        cg.setNotBefore(validFrom);
        cg.setNotAfter(validTo);
        cg.setPublicKey(publicKey);
        cg.setSignatureAlgorithm("SHA1WITHRSA");
        cg.setSubjectDN(new X509Name(dn));

        cg.setIssuerDN(new X509Name(dn));
        SecureRandom prng = SecureRandom.getInstance("SHA1PRNG");
        BigInteger serNo = new BigInteger(32, prng);
        cg.setSerialNumber(serNo);
        this.cert = cg.generate(this.privateKey);

        // read the BDA signature; not currently used
        //int read = dis.read(buf);
        //if (read == -1) {
        //    throw new IOException("Reached end of file before finished reading the file:" +
        //            crtFile);
        //}
        dis.close();
    }

    static void initKeyStore() throws Exception {

        char[] password = keystorePassword.toCharArray();
        ks = KeyStore.getInstance(KeyStore.getDefaultType());
        File kfile = new File(keystoreFile);
        if (!kfile.exists()) {
            ks.load(null, password);
            FileOutputStream fout = new FileOutputStream(kfile);
            ks.store(fout, password);
            fout.close();
        }
        URL url = new URL("file:" + kfile.getCanonicalPath());
        InputStream is = url.openStream();
        ks.load(is, password);
        is.close();
    }

    public static void main(String args[]) throws Exception {
        String keyFile = null;
        String crtFile = null;
        boolean keyFileExpected = true;
        boolean debug = false;

        for (int i = 0; i < args.length; i++) {
            String opt = args[i];
            if (opt.equals("-keystore")) {
                if (++i == args.length) {
                    errorNeedArgument(opt);
                }
                keystoreFile = args[i];
            } else if (opt.equals("-storepass")) {
                if (++i == args.length) {
                    errorNeedArgument(opt);
                }
                keystorePassword = args[i];
            } else if (opt.equals("-alias")) {
                if (++i == args.length) {
                    errorNeedArgument(opt);
                }
                alias = args[i];
            } else if (opt.equals("-keypass")) {
                if (++i == args.length) {
                    errorNeedArgument(opt);
                }
                keyPass = args[i];
            } else if (opt.equals("-help")) {
                printUsageAndExit("");
            } else if (opt.equals("-debug")) {
                debug = true;
            } else {
                if (keyFileExpected) {
                    keyFile = args[i];
                    keyFileExpected = false;
                } else {
                    crtFile = args[i];
                }
            }
        }
        checkFile(keyFile);
        checkFile(crtFile);
        initKeyStore();
        OnlineKeytool tool = new OnlineKeytool(keyFile, crtFile, debug);
        ks = tool.importKeys(ks, alias, keyPass);

        // make the updated keystore persistent
        FileOutputStream fos = new FileOutputStream(keystoreFile);
        ks.store(fos, keystorePassword.toCharArray());
        fos.close();
    }

    static void checkFile(String file) {
        if ((file == null) || (!(new File(file)).exists())) {
            printUsageAndExit("File Not Found:" + file);
        }
    }

    static private void tinyHelp() {
        System.err.println("Try OnlineKeytool -help");

        // do not drown user with the help lines.
        System.exit(1);
    }

    static private void errorNeedArgument(String flag) {
        System.err.println("Command option " + flag + " needs an argument.");
        tinyHelp();
    }

    private static void printUsageAndExit(String reason) {
        if (!reason.isEmpty()) {
            System.err.println("\nFailed: " + reason);
        }
        System.err.println("\n/**");
        System.err.println(" This is a tool for importing online credentials" + " into a Keystore");
        System.err.println(" This tool imports the private and public online keys obtained");
        System.err.println(" from BDA into the keystore.");
        System.err.println("**/\n");
        System.err.println("usage: OnlineKeytool [options] <private key file> <online.crt file>\n");
        System.err.println("Valid Options:");
        System.err.println(" -keystore filename  \t:Keystore where the keys get stored");
        System.err.println(
                "                     \t In the absense of this option, a default store:\"keystore.store\"");
        System.out.println("                     \t is used from the current working directory.");
        System.err.println(" -storepass password \t:Keystore password");
        System.err.println(" -alias alias        \t:Alias for the online keys");
        System.err.println(" -keypass password   \t:Password for online keys");
        System.err.println(" -help               \t:Prints this message");
        System.err.println("\nExample: java net.java.bd.tools.security.OnlineKeytool owner.bin online.crt\n");
        System.exit(1);
    }
}