All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jolokia.jvmagent.security.KeyStoreUtil Maven / Gradle / Ivy

There is a newer version: 1.7.2
Show newest version
package org.jolokia.jvmagent.security;/*
 * 
 * Copyright 2015 Roland Huss
 *
 * 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.
 */

import java.io.*;
import java.security.*;
import java.security.cert.*;
import java.security.cert.Certificate;
import java.security.spec.*;
import java.util.Date;

import org.jolokia.Version;
import org.jolokia.util.Base64Util;
import org.jolokia.util.ClassUtil;
import sun.security.x509.X500Name;

/**
 * Utility class for handling keystores
 *
 * @author roland
 * @since 30/09/15
 */
public class KeyStoreUtil {

    private final static String KEYGEN_CLASS_JDK8 = "sun.security.tools.keytool.CertAndKeyGen";
    private final static String KEYGEN_CLASS_JDK7 = "sun.security.x509.CertAndKeyGen";

    private KeyStoreUtil() {
    }

    /**
     * Update a keystore with a CA certificate
     *
     * @param pTrustStore the keystore to update
     * @param pCaCert     CA cert as PEM used for the trust store
     */
    public static void updateWithCaPem(KeyStore pTrustStore, File pCaCert)
            throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
        InputStream is = new FileInputStream(pCaCert);
        try {
            CertificateFactory certFactory = CertificateFactory.getInstance("X509");
            X509Certificate cert = (X509Certificate) certFactory.generateCertificate(is);

            String alias = cert.getSubjectX500Principal().getName();
            pTrustStore.setCertificateEntry(alias, cert);
        } finally {
            is.close();
        }
    }

    /**
     * Update a key store with the keys found in a server PEM and its key file.
     *
     * @param pKeyStore   keystore to update
     * @param pServerCert server certificate
     * @param pServerKey  server key
     * @param pKeyAlgo    algorithm used in the keystore (e.g. "RSA")
     * @param pPassword   password to use for the key file. must not be null, use char[0]
     *                    for an empty password.
     */
    public static void updateWithServerPems(KeyStore pKeyStore, File pServerCert, File pServerKey, String pKeyAlgo, char[] pPassword)
            throws IOException, CertificateException, NoSuchAlgorithmException, InvalidKeySpecException, KeyStoreException {
        InputStream is = new FileInputStream(pServerCert);
        try {
            CertificateFactory certFactory = CertificateFactory.getInstance("X509");
            X509Certificate cert = (X509Certificate) certFactory.generateCertificate(is);

            byte[] keyBytes = decodePem(pServerKey);
            PrivateKey privateKey;

            KeyFactory keyFactory = KeyFactory.getInstance(pKeyAlgo);
            try {
                // First let's try PKCS8
                privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
            } catch (InvalidKeySpecException e) {
                // Otherwise try PKCS1
                RSAPrivateCrtKeySpec keySpec = PKCS1Util.decodePKCS1(keyBytes);
                privateKey = keyFactory.generatePrivate(keySpec);
            }

            String alias = cert.getSubjectX500Principal().getName();
            pKeyStore.setKeyEntry(alias, privateKey, pPassword, new Certificate[]{cert});
        } finally {
            is.close();
        }
    }

    /**
     * Update the given keystore with a self signed server certificate. This can be used if no
     * server certificate is provided from the outside and no SSL verification is used by the client.
     *
     * @param pKeyStore keystore to update
     */
    public static void updateWithSelfSignedServerCertificate(KeyStore pKeyStore)
            throws NoSuchProviderException, NoSuchAlgorithmException, IOException,
                   InvalidKeyException, CertificateException, SignatureException, KeyStoreException {

        X500Name x500Name =
            new X500Name(
                    "Jolokia Agent " + Version.getAgentVersion(), // CN
                    "JVM",                                        // OU
                    "jolokia.org",                                // O
                    "Pegnitz",                                    // L
                    "Franconia",                                  // ST
                    "DE"                                          // C
            );

        // Need to do it via reflection because Java8 moved class to a different package
        Object keypair = createKeyPair();
        PrivateKey privKey = getPrivateKey(keypair);

        X509Certificate[] chain = new X509Certificate[1];
        chain[0] = getSelfCertificate(keypair,x500Name, new Date(), (long) 3650 * 24 * 60 * 60);
        pKeyStore.setKeyEntry("jolokia-agent", privKey, new char[0], chain);
    }

    // =============================================================================================
    // Reflection based access to KeyGen classes:

    private static Object createKeyPair() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException {
        Class keyGenClass = lookupKeyGenClass();
        Object keypair = ClassUtil.newInstance(keyGenClass, "RSA", "SHA1WithRSA");
        ClassUtil.applyMethod(keypair, "generate", 2048);
        return keypair;
    }

    private static X509Certificate getSelfCertificate(Object keypair, X500Name x500Name, Date date, long l) {
        return (X509Certificate) ClassUtil.applyMethod(keypair, "getSelfCertificate", x500Name, date, l);
    }

    private static PrivateKey getPrivateKey(Object keypair) {
        return (PrivateKey) ClassUtil.applyMethod(keypair, "getPrivateKey");
    }

    private static Class lookupKeyGenClass() {
        Class keyGenClass = ClassUtil.classForName(KEYGEN_CLASS_JDK8);
        if (keyGenClass == null) {
            keyGenClass = ClassUtil.classForName(KEYGEN_CLASS_JDK7);
        }
        if (keyGenClass == null) {
            throw new IllegalStateException("Cannot lookup key-generator class: Neither Java8's " + KEYGEN_CLASS_JDK8 +
                                            " nor " + KEYGEN_CLASS_JDK7 + " can be looked up with the context class loader");

        }
        return keyGenClass;
    }

    // This method is inspired and partly taken over from
    // http://oauth.googlecode.com/svn/code/java/
    // All credits to belong to them.
    private static byte[] decodePem(File pemFile) throws IOException {
        BufferedReader reader = new BufferedReader(new FileReader(pemFile));
        try {
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.contains("-----BEGIN ")) {
                    return readBytes(pemFile, reader, line.trim().replace("BEGIN", "END"));
                }
            }
            throw new IOException("PEM " + pemFile + " is invalid: no begin marker");
        } finally {
            reader.close();
        }
    }

    private static byte[] readBytes(File pemFile, BufferedReader reader, String endMarker) throws IOException {
        String line;
        StringBuffer buf = new StringBuffer();

        while ((line = reader.readLine()) != null) {
            if (line.indexOf(endMarker) != -1) {
                return Base64Util.decode(buf.toString());
            }
            buf.append(line.trim());
        }
        throw new IOException(pemFile + " is invalid : No end marker");
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy