it.cnr.icar.eric.server.security.authentication.CertificateAuthority.java Source code

Java tutorial

Introduction

Here is the source code for it.cnr.icar.eric.server.security.authentication.CertificateAuthority.java

Source

/*
 * ====================================================================
 * This file is part of the ebXML Registry by Icar Cnr v3.2 
 * ("eRICv32" in the following disclaimer).
 *
 * "eRICv32" is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * "eRICv32" is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License Version 3
 * along with "eRICv32".  If not, see <http://www.gnu.org/licenses/>.
 *
 * eRICv32 is a forked, derivative work, based on:
 *    - freebXML Registry, a royalty-free, open source implementation of the ebXML Registry standard,
 *      which was published under the "freebxml License, Version 1.1";
 *   - ebXML OMAR v3.2 Edition, published under the GNU GPL v3 by S. Krushe & P. Arwanitis.
 * 
 * All derivative software changes and additions are made under
 *
 * Copyright (C) 2013 Ing. Antonio Messina <messina@pa.icar.cnr.it>
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the freebxml Software Foundation.  For more
 * information on the freebxml Software Foundation, please see
 * "http://www.freebxml.org/".
 *
 * This product includes software developed by the Apache Software
 * Foundation (http://www.apache.org/).
 *
 * ====================================================================
 */
package it.cnr.icar.eric.server.security.authentication;

import it.cnr.icar.eric.common.BindingUtility;
import it.cnr.icar.eric.common.RegistryResponseHolder;
import it.cnr.icar.eric.common.RepositoryItem;
import it.cnr.icar.eric.common.RepositoryItemImpl;
import it.cnr.icar.eric.common.exceptions.InvalidContentException;
import it.cnr.icar.eric.common.exceptions.MissingRepositoryItemException;
import it.cnr.icar.eric.common.security.KeyTool;
import it.cnr.icar.eric.server.common.ServerRequestContext;
import it.cnr.icar.eric.server.util.ServerResourceBundle;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.security.auth.x500.X500Principal;
import javax.xml.registry.JAXRException;
import javax.xml.registry.RegistryException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.oasis.ebxml.registry.bindings.rim.PersonNameType;
import org.oasis.ebxml.registry.bindings.rim.PostalAddressType;
import org.oasis.ebxml.registry.bindings.rim.UserType;
import org.oasis.ebxml.registry.bindings.rs.RegistryRequestType;
import org.oasis.ebxml.registry.bindings.rs.RegistryResponseType;

import sun.security.x509.CertificateIssuerName;
import sun.security.x509.CertificateSubjectName;
import sun.security.x509.X500Name;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;

/**
 * CA that generates user certs signed by the RegistryOperator private key
 *
 * @author <a href="mailto:Farrukh.Najmi@Sun.COM">Farrukh S. Najmi</a>
 */
public class CertificateAuthority {
    /*# private CertificateAuthority _certificateAuthority; */
    private static CertificateAuthority instance = null;
    @SuppressWarnings("unused")
    private static final Log log = LogFactory.getLog(CertificateAuthority.class);

    private static AuthenticationServiceImpl ac = null;
    private static BindingUtility bu = BindingUtility.getInstance();

    private Certificate caCertificate = null;

    protected CertificateAuthority() {
    }

    public synchronized static CertificateAuthority getInstance() {
        if (instance == null) {
            instance = new CertificateAuthority();
            ac = AuthenticationServiceImpl.getInstance();
        }

        return instance;
    }

    @SuppressWarnings("static-access")
    private Certificate getCACertificate() throws RegistryException {
        if (caCertificate == null) {
            caCertificate = ac.getCertificate(ac.ALIAS_REGISTRY_OPERATOR);
        }
        return caCertificate;
    }

    /** Extension request to sign specified cert and return the signed cert. */
    @SuppressWarnings("static-access")
    public RegistryResponseHolder signCertificateRequest(UserType user, RegistryRequestType req,
            Map<?, ?> idToRepositoryItemMap) throws RegistryException {

        RegistryResponseHolder respHolder = null;
        RegistryResponseType ebRegistryResponseType = null;
        ServerRequestContext context = null;

        try {
            context = new ServerRequestContext("CertificateAUthority.signCertificateRequest", req);
            context.setUser(user);

            if (idToRepositoryItemMap.keySet().size() == 0) {
                throw new MissingRepositoryItemException(
                        ServerResourceBundle.getInstance().getString("message.KSRepItemNotFound"));
            }

            String id = (String) idToRepositoryItemMap.keySet().iterator().next();

            Object obj = idToRepositoryItemMap.get(id);
            if (!(obj instanceof RepositoryItem)) {
                throw new InvalidContentException();
            }
            RepositoryItem ri = (RepositoryItem) obj; //This is the JKS keystore containing cert to be signed            

            //Read original cert from keystore
            InputStream is = ri.getDataHandler().getInputStream();
            KeyStore keyStore = KeyStore.getInstance("JKS");
            keyStore.load(is, bu.FREEBXML_REGISTRY_KS_PASS_REQ.toCharArray());
            is.close();
            X509Certificate cert = (X509Certificate) keyStore
                    .getCertificate(bu.FREEBXML_REGISTRY_USERCERT_ALIAS_REQ);

            //Sign the cert
            cert = signCertificate(cert);

            //Replace cert with signed cert in keystore
            keyStore.deleteEntry(bu.FREEBXML_REGISTRY_USERCERT_ALIAS_REQ);
            keyStore.setCertificateEntry(bu.FREEBXML_REGISTRY_USERCERT_ALIAS_RESP, cert);

            //Add CA root cert (RegistryOPerator's cert) to keystore.
            keyStore.setCertificateEntry(bu.FREEBXML_REGISTRY_CACERT_ALIAS, getCACertificate());

            Certificate[] certChain = new Certificate[2];
            certChain[0] = cert;
            certChain[1] = getCACertificate();
            validateChain(certChain);

            File repositoryItemFile = File.createTempFile(".eric-ca-resp", ".jks");
            repositoryItemFile.deleteOnExit();
            FileOutputStream fos = new java.io.FileOutputStream(repositoryItemFile);
            keyStore.store(fos, bu.FREEBXML_REGISTRY_KS_PASS_RESP.toCharArray());
            fos.flush();
            fos.close();

            DataHandler dh = new DataHandler(new FileDataSource(repositoryItemFile));
            RepositoryItemImpl riNew = new RepositoryItemImpl(id, dh);

            ebRegistryResponseType = bu.rsFac.createRegistryResponseType();
            ebRegistryResponseType.setStatus(BindingUtility.CANONICAL_RESPONSE_STATUS_TYPE_ID_Success);

            HashMap<String, Object> respIdToRepositoryItemMap = new HashMap<String, Object>();
            respIdToRepositoryItemMap.put(id, riNew);

            respHolder = new RegistryResponseHolder(ebRegistryResponseType, respIdToRepositoryItemMap);

        } catch (RegistryException e) {
            context.rollback();
            throw e;
        } catch (Exception e) {
            context.rollback();
            throw new RegistryException(e);
        }

        context.commit();
        return respHolder;
    }

    /**
     * Signed specified cert using the private key of RegistryOperator.
     * Warning this uses Sun's JDK impl specific classes and will not work
     * with other JDK impls.
     *
     */
    @SuppressWarnings("static-access")
    X509Certificate signCertificate(X509Certificate inCert) throws RegistryException {
        X509CertImpl signedCert = null;

        try {
            X509CertImpl caCert = (X509CertImpl) getCACertificate();
            X509CertInfo caCertInfo = new X509CertInfo(caCert.getTBSCertificate());
            X509CertInfo inCertInfo = new X509CertInfo(inCert.getTBSCertificate());

            // Use catch (certs subject name as signed cert's issuer name
            CertificateSubjectName caCertSubjectName = (CertificateSubjectName) caCertInfo
                    .get(X509CertInfo.SUBJECT);
            CertificateIssuerName signedCertIssuerName = new CertificateIssuerName(
                    (X500Name) caCertSubjectName.get(CertificateSubjectName.DN_NAME));

            inCertInfo.set(X509CertInfo.ISSUER, signedCertIssuerName);
            signedCert = new X509CertImpl(inCertInfo);

            //TODO: Need to remove hardcoding below and instead somehow use info.algId => algName
            //            signedCert.sign(ac.getPrivateKey(ac.ALIAS_REGISTRY_OPERATOR, ac.ALIAS_REGISTRY_OPERATOR), "MD5WithRSA"); // JDK6
            //            signedCert.sign(ac.getPrivateKey(ac.ALIAS_REGISTRY_OPERATOR, ac.ALIAS_REGISTRY_OPERATOR), "SHA256withRSA"); // JDK7

            // removed hardcoding
            signedCert.sign(ac.getPrivateKey(ac.ALIAS_REGISTRY_OPERATOR, ac.ALIAS_REGISTRY_OPERATOR),
                    inCert.getSigAlgName());

        } catch (java.security.GeneralSecurityException e) {
            throw new RegistryException(
                    ServerResourceBundle.getInstance().getString("message.ErrorSigningRegIssuedCert"), e);
        } catch (java.io.IOException e) {
            throw new RegistryException(
                    ServerResourceBundle.getInstance().getString("message.ErrorSigningRegIssuedCert"), e);
        }

        return signedCert;
    }

    private boolean validateChain(Certificate[] certChain) {
        for (int i = 0; i < certChain.length - 1; i++) {
            X500Principal issuerDN = ((X509Certificate) certChain[i]).getIssuerX500Principal();
            X500Principal subjectDN = ((X509Certificate) certChain[i + 1]).getSubjectX500Principal();
            if (!(issuerDN.equals(subjectDN)))
                return false;
        }
        return true;
    }

    /** 
      * Generate a registry issued certificate signed by private key of RegistryOperator.
      */
    public X509Certificate generateRegistryIssuedCertificate(String dname) throws RegistryException {
        X509Certificate cert = null;

        File ksFile = null;
        try {
            String keystoreFileName = System.getProperty("java.io.tmpdir") + "/eric-temp-ks.jks";
            String keystoreType = "JKS";
            String alias = "ebxmlrr";
            String storePassStr = "ebxmlrr";
            String keyPassStr = "ebxmlrr";
            String keyAlg = "RSA"; //XWSS does not support DSA which is default is KeyTool. Hmm. Weird.

            String[] args = { "-genkey", "-keyAlg", keyAlg, "-alias", alias, "-keypass", keyPassStr, "-keystore",
                    keystoreFileName, "-storepass", storePassStr, "-storetype", keystoreType, "-dname", dname };

            KeyTool keytool = new KeyTool();
            keytool.run(args, System.out);

            ksFile = new File(keystoreFileName);

            //Now load the KeyStore and get the cert
            FileInputStream fis = new java.io.FileInputStream(ksFile);

            KeyStore keyStore = KeyStore.getInstance(keystoreType);
            keyStore.load(fis, storePassStr.toCharArray());

            cert = (X509Certificate) keyStore.getCertificate(alias);
            cert = signCertificate(cert);

        } catch (Exception e) {
            throw new RegistryException(ServerResourceBundle.getInstance().getString("message.CertGenError"), e);
        } finally {
            if (ksFile != null) {
                try {
                    ksFile.delete();
                } catch (Exception e) {
                    ksFile = null;
                }
            }
        }

        return cert;
    }

    /**
     * Gets the DN for specified User object.
     *
     * @param user DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     *
     * @throws JAXRException DOCUMENT ME!
     */
    @SuppressWarnings("unused")
    private static String getDNameFromUser(UserType user) throws RegistryException {
        String dname = "CN=";

        List<PostalAddressType> addresses = user.getAddress();
        PersonNameType ebPersonNameType = user.getPersonName();

        //CN=Farrukh Najmi, OU=freebxml.org, O=ebxmlrr, L=Islamabad, ST=Punjab, C=PK
        if (ebPersonNameType == null) {
            ebPersonNameType = bu.rimFac.createPersonNameType();
            ebPersonNameType.setFirstName(user.getId());
        }

        PostalAddressType ebPostalAddressType = null;
        if ((addresses != null) && (addresses.size() > 0)) {
            ebPostalAddressType = (addresses.iterator().next());
        } else {
            ebPostalAddressType = bu.rimFac.createPostalAddressType();
        }

        String city = ebPostalAddressType.getCity();

        if ((city == null) || (city.length() == 0)) {
            city = "Unknown";
        }

        String state = ebPostalAddressType.getStateOrProvince();

        if ((state == null) || (state.length() == 0)) {
            state = "Unknown";
        }

        String country = ebPostalAddressType.getCountry();

        if ((country == null) || (country.length() == 0)) {
            country = "Unknown";
        }

        if (country.length() > 0) {
            country = country.substring(0, 2);
        }

        dname += (ebPersonNameType.getFirstName() + " " + ebPersonNameType.getMiddleName() + " "
                + ebPersonNameType.getLastName() + ", OU=Unknown, O=Unknown, L=" + city + ", ST=" + state + ", C="
                + country);

        return dname;
    }

}