com.vmware.certificate.VMCAClient.java Source code

Java tutorial

Introduction

Here is the source code for com.vmware.certificate.VMCAClient.java

Source

/*
 * Copyright  2012-2015 VMware, Inc.  All Rights Reserved.
 *
 * 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.
 */

package com.vmware.certificate;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.NoSuchElementException;

import org.apache.commons.codec.binary.Base64;

import com.sun.jna.Pointer;

/**
 * @author Anu Engineer
 *
 */
public class VMCAClient implements Iterable<X509Certificate> {
    public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----\n";
    public static final String END_CERT = "\n-----END CERTIFICATE-----";

    private String _username;
    private String _domain;
    private String _password;
    private String _serverName;

    public enum certFilters {
        ACTIVE_CERTIFICATES, REVOKED_CERTIFICATES, EXPIRED_CERTIFICATES, ALL_CERTIFICATES
    }

    /**
     * Creates a Client class that allows you to communicate to a VMCA Server
     *
     * @param ServerName
     *            - Name of the VMCA Server
     */
    public VMCAClient(String username, String domain, String password, String server_name) {
        _username = username;
        _domain = domain;
        _password = password;
        _serverName = server_name;
        setEnumFilter(certFilters.ACTIVE_CERTIFICATES);
    }

    /**
     * Creates a Certificate from a PEM encoded String
     *
     * @param certificateString
     * @return
     * @throws Exception
     */
    public static X509Certificate getCertificateFromString(String certificateString) throws Exception {
        InputStream is = new ByteArrayInputStream(certificateString.getBytes());
        CertificateFactory cf = CertificateFactory.getInstance("X509");
        X509Certificate c = (X509Certificate) cf.generateCertificate(is);
        return c;
    }

    /**
     * returns a PEM Encoded String from a X509Certificate
     *
     * @param certificate
     * @return
     * @throws Exception
     */
    private String getEncodedStringFromCertificate(X509Certificate certificate) throws Exception {
        if (certificate == null) {
            throw new IllegalStateException("Invalid Certificate, certificate cannot be null");
        }

        String encoded = new String(Base64.encodeBase64(certificate.getEncoded()));
        StringBuffer pemencode = new StringBuffer();
        for (int x = 0; x < encoded.length(); x++) {

            if ((x > 0) && (x % 64 == 0)) {
                pemencode.append("\n");
                pemencode.append(encoded.charAt(x));
            } else {
                pemencode.append(encoded.charAt(x));

            }
        }

        return BEGIN_CERT + pemencode.toString() + END_CERT;
    }

    private String encodeX509CertificatesToString(X509Certificate[] certs) throws Exception {
        if (certs == null || certs.length == 0) {
            return null;
        }

        int stringBuilderSize = certs.length * 1024; // approximate string builder
                                                     // size
        StringBuilder sb = new StringBuilder(stringBuilderSize);
        for (X509Certificate cert : certs) {
            String pem = getEncodedStringFromCertificate(cert);
            sb.append(pem);
            sb.append('\n');
        }
        if (sb.length() > 0) {
            sb.deleteCharAt(sb.length() - 1);
        }

        return sb.toString();
    }

    private String encodePrivateKeyToString(PrivateKey key) throws UnsupportedEncodingException {
        if (key == null) {
            return null;
        }
        byte[] privBytes = key.getEncoded();
        String encoded = new String(Base64.encodeBase64(privBytes));
        StringBuffer pemencode = new StringBuffer();
        for (int x = 0; x < encoded.length(); x++) {

            if ((x > 0) && (x % 64 == 0)) {
                pemencode.append("\n");
                pemencode.append(encoded.charAt(x));
            } else {
                pemencode.append(encoded.charAt(x));

            }
        }
        return "-----BEGIN PRIVATE KEY-----\n" + pemencode.toString() + "\n" + "-----END PRIVATE KEY-----";
    }

    /**
     * Gets root CA Certificate Array from VMCA
     *
     * @return X509Certificate Root CA Certificate
     * @throws Exception
     */
    public X509Certificate[] getRootCertificates() throws Exception {
        try (VMCAServerContext context = getServerContext()) {
            return this.getRootCertificates(context);
        }
    }

    /**
     * Gets root CA Certificate Array from VMCA
     *
     * @return X509Certificate Root CA Certificate
     * @throws Exception
     */
    protected X509Certificate[] getRootCertificates(VMCAServerContext context) throws Exception {
        ArrayList<X509Certificate> trustedRoots = new ArrayList<X509Certificate>();
        trustedRoots.add(VMCAClient.getCertificateFromString(VMCAAdapter2.GetRootCertificate(context)));
        return trustedRoots.toArray(new X509Certificate[trustedRoots.size()]);
    }

    /**
     * Adding new root CA Certificate chain to VMCA
     *
     * @param certificateChain
     *           Chain of certificates in case VMCA becomes a subordinate CA. Remember that the leaf certificate goes first (at index 0).
     * @param key
     * @throws Exception
     */
    public void addRootCertificate(X509Certificate[] certificateChain, PrivateKey key) throws Exception {
        try (VMCAServerContext context = getServerContext()) {
            VMCAAdapter2.AddRootCertificate(context, encodeX509CertificatesToString(certificateChain),
                    encodePrivateKeyToString(key));
        }
    }

    /**
     * Gets root CA Certificate from VMCA
     *
     * @return X509Certificate Root CA Certificate
     * @throws Exception
     */
    public X509Certificate getRootCertificate() throws Exception {
        try (VMCAServerContext context = getServerContext()) {
            return this.getRootCertificate(context);
        }
    }

    /**
     * Gets root CA Certificate from VMCA
     *
     * @return X509Certificate Root CA Certificate
     * @throws Exception
     */
    protected X509Certificate getRootCertificate(VMCAServerContext context) throws Exception {
        return VMCAClient.getCertificateFromString(VMCAAdapter2.GetRootCertificate(context));
    }

    /*
     * Returns a Signed Certificate from the Server
     *
     * @param certificateRequest -- A PKCS 10 Certificate request
     *
     * @param notBefore - Start date of the Certificate
     *
     * @param notAfter - End Date of the Certificate
     *
     * @return X509Certificate
     *
     * @throws Exception
     */

    public X509Certificate getCertificate(String certificateRequest, Date notBefore, Date notAfter)
            throws Exception {

        try (VMCAServerContext context = getServerContext()) {
            return this.getCertificate(context, certificateRequest, notBefore, notAfter);
        }
    }

    /*
     * Returns a Signed Certificate from the Server
     *
     * @param certificateRequest -- A PKCS 10 Certificate request
     *
     * @param notBefore - Start date of the Certificate
     *
     * @param notAfter - End Date of the Certificate
     *
     * @return X509Certificate
     *
     * @throws Exception
     */

    protected X509Certificate getCertificate(VMCAServerContext context, String certificateRequest, Date notBefore,
            Date notAfter) throws Exception {

        long epochNotBefore = notBefore.getTime();
        long epochNotAfter = notAfter.getTime();

        epochNotBefore = epochNotBefore / 1000;
        epochNotAfter = epochNotAfter / 1000;

        return getCertificateFromString(VMCAAdapter2.VMCAGetSignedCertificateFromCSR(context, certificateRequest,
                epochNotBefore, epochNotAfter));
    }

    /*
     * Returns a Signed Certificate from the Server
     *
     * @param certificateRequest -- A PKCS 10 Certificate request
     *
     * @param hostName -- Host name, CSR will be validated against hostname
     *
     * @param ipAddress -- optional -- ipAddress to validate against CSR
     *
     * @param notBefore - Start date of the Certificate
     *
     * @param notAfter - End Date of the Certificate
     *
     * @return X509Certificate
     *
     * @throws Exception
     */
    protected X509Certificate getCertificateForHost(VMCAServerContext context, String hostName, String ipAddress,
            String certificateRequest, Date notBefore, Date notAfter) throws Exception {

        long epochNotBefore = notBefore.getTime();
        long epochNotAfter = notAfter.getTime();

        epochNotBefore = epochNotBefore / 1000;
        epochNotAfter = epochNotAfter / 1000;

        return getCertificateFromString(VMCAAdapter2.VMCAGetSignedCertificateForHost(context, hostName, ipAddress,
                certificateRequest, epochNotBefore, epochNotAfter));
    }

    private String getPEMEncodedKey(KeyPair Keys) {
        byte[] privBytes = Keys.getPrivate().getEncoded();
        String encoded = new String(Base64.encodeBase64(privBytes));
        StringBuffer pemencode = new StringBuffer();
        for (int x = 0; x < encoded.length(); x++) {

            if ((x > 0) && (x % 64 == 0)) {
                pemencode.append("\n");
                pemencode.append(encoded.charAt(x));
            } else {
                pemencode.append(encoded.charAt(x));

            }
        }
        return "-----BEGIN PRIVATE KEY-----\n" + pemencode.toString() + "\n" + "-----END PRIVATE KEY-----";
    }

    /**
     * Returns a Signed Certificate from the Server
     *
     * @param Req
     *            -- A Request Object
     * @param Keys
     *            - A Java Key Pair Object
     * @param notBefore
     *            - Start Date for the Certificate
     * @param notAfter
     *            - End Date for the validity of the Certificate
     * @return X509Certificate that is signed by VMCA
     */
    public X509Certificate getCertificate(Request req, KeyPair Keys, Date notBefore, Date notAfter)
            throws Exception {

        try (VMCAServerContext context = getServerContext()) {
            return this.getCertificate(context, req, Keys, notBefore, notAfter);
        }
    }

    /**
     * Returns a Signed Certificate from the Server
     *
     * @param Req
     *            -- A Request Object
     * @param Keys
     *            - A Java Key Pair Object
     * @param notBefore
     *            - Start Date for the Certificate
     * @param notAfter
     *            - End Date for the validity of the Certificate
     * @return X509Certificate that is signed by VMCA
     */
    protected X509Certificate getCertificate(VMCAServerContext context, Request req, KeyPair Keys, Date notBefore,
            Date notAfter) throws Exception {

        long epochNotBefore = notBefore.getTime();
        long epochNotAfter = notAfter.getTime();

        epochNotBefore = epochNotBefore / 1000;
        epochNotAfter = epochNotAfter / 1000;
        String certString = VMCAAdapter2.VMCAJavaGenCert(context, req.getName(), req.getCountry(),
                req.getLocality(), req.getState(), req.getOrganization(), req.getOrgunit(), req.getDnsname(),
                req.getUri(), req.getEmail(), req.getIpaddress(), req.getKeyusage(), 0, getPEMEncodedKey(Keys),
                epochNotBefore, epochNotAfter);

        return getCertificateFromString(certString);

    }

    /**
     * Revokes a Certificate
     *
     * @param certificate
     * @throws Exception
     */
    public void revokeCertificate(X509Certificate certificate) throws Exception {
        try (VMCAServerContext context = getServerContext()) {
            this.revokeCertificate(context, certificate);
        }
    }

    /**
     * Revokes a Certificate
     *
     * @param certificate
     * @throws Exception
     */
    protected void revokeCertificate(VMCAServerContext context, X509Certificate certificate) throws Exception {
        VMCAAdapter2.RevokeCertificate(context, getEncodedStringFromCertificate(certificate));
    }

    /**
     * Gets the VMCA Server Version
     *
     * @return Version String
     * @throws Exception
     */
    public String getServerVersion() throws Exception {
        try (VMCAServerContext context = getServerContext()) {
            return this.getServerVersion(context);
        }
    }

    /**
     * Gets the VMCA Server Version
     *
     * @return Version String
     * @throws Exception
     */
    protected String getServerVersion(VMCAServerContext context) throws Exception {
        return VMCAAdapter2.getServerVersion(context);
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Iterable#iterator()
     */
    @Override
    public Iterator<X509Certificate> iterator() {
        try {
            VMCAServerContext context = getServerContext();
            return new VMCACertIterator(context, filterToInteger(getEnumFilter()));
        } catch (VMCAException e) {
            throw new RuntimeException(e.getMessage() + " Error Code: [" + e.getErrorCode() + "]");
        }

    }

    /**
     * @return the enumFilter
     */
    public certFilters getEnumFilter() {
        return enumFilter;
    }

    /**
     * @param enumFilter
     *            the enumFilter to set
     */
    public void setEnumFilter(certFilters enumFilter) {
        this.enumFilter = enumFilter;
    }

    private certFilters enumFilter;

    private VMCAServerContext getServerContext() throws VMCAException {
        return VMCAAdapter2.getServerContext(_serverName, _username, _domain, _password);
    }

    /**
     * filter to Integer, since I am using ENUM java Enum to Integer is not
     * direct
     *
     * @param filter
     * @return
     */
    private static int filterToInteger(final certFilters filter) {
        switch (filter) {
        case ACTIVE_CERTIFICATES:
            return 0;
        case REVOKED_CERTIFICATES:
            return 1;
        case EXPIRED_CERTIFICATES:
            return 2;
        case ALL_CERTIFICATES:
            return 4;
        }
        return 0;
    }

    private class VMCACertIterator implements Iterator<X509Certificate>, AutoCloseable {
        private VMCAServerContext serverContext;
        private Pointer enumContext;
        private String nextCert;
        private int certFilter;

        /**
         * Ctor for Iterator
         *
         * @param serverName
         * @param certFilter
         */
        public VMCACertIterator(VMCAServerContext context, int certFilter) throws VMCAException {
            this.certFilter = certFilter;
            this.serverContext = context;
            enumContext = VMCAAdapter2.VMCAOpenEnumContext(context, this.certFilter);
        }

        @Override
        public boolean hasNext() {
            try {
                if (enumContext != null) {
                    nextCert = VMCAAdapter2.VMCAGetNextCertificate(enumContext);
                    if (nextCert != null) {
                        return true;
                    } else {
                        close();
                    }
                }
            } catch (Exception e) {
                close();
            }
            return false;
        }

        @Override
        public X509Certificate next() {
            try {
                return VMCAClient.getCertificateFromString(nextCert);
            } catch (Exception e) {
                throw new NoSuchElementException(e.getMessage());
            }
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException(
                    "To Remove or Revoke a Certificate, Please use Revoke Certificate");

        }

        @Override
        public void close() {
            dispose();
        }

        @Override
        protected void finalize() throws Throwable {
            try {
                dispose();
            } finally {
                super.finalize();
            }
        }

        public void dispose() {
            if (enumContext != null) {
                VMCAAdapter2.VMCACloseEnumContext(enumContext);
                enumContext = null;
            }

            if (serverContext != null) {
                serverContext.dispose();
                serverContext = null;
            }
        }
    }

    public void getCRL(String existingCRL, String value) throws VMCAException, IOException {
        try (VMCAServerContext context = getServerContext()) {
            this.getCRL(context, existingCRL, value);
        }
    }

    protected void getCRL(VMCAServerContext context, String existingCRL, String value) throws VMCAException {
        @SuppressWarnings("unused")
        String returnedCRL = VMCAAdapter2.VMCAGetCRL(context, existingCRL, value);
    }

    public void publishRoots() throws VMCAException, Exception {
        try (VMCAServerContext context = getServerContext()) {
            this.publishRoots(context);
        }
    }

    protected void publishRoots(VMCAServerContext context) throws VMCAException, Exception {
        VMCAAdapter2.VMCAPublishRoots(context);
    }

}