com.alfaariss.oa.profile.aselect.ws.security.OACrypto.java Source code

Java tutorial

Introduction

Here is the source code for com.alfaariss.oa.profile.aselect.ws.security.OACrypto.java

Source

/*
 * Asimba Server
 * 
 * Copyright (C) 2012 Asimba
 * Copyright (C) 2007-2008 Alfa & Ariss B.V.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program 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 Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see www.gnu.org/licenses
 * 
 * Asimba - Serious Open Source SSO - More information on www.asimba.org
 * 
 */
package com.alfaariss.oa.profile.aselect.ws.security;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Vector;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ws.security.WSSecurityException;
import org.apache.ws.security.components.crypto.Crypto;

import com.alfaariss.oa.OAException;
import com.alfaariss.oa.SystemErrors;
import com.alfaariss.oa.engine.core.Engine;
import com.alfaariss.oa.engine.core.crypto.CryptoException;
import com.alfaariss.oa.engine.core.crypto.CryptoManager;
import com.alfaariss.oa.engine.core.crypto.factory.AbstractSigningFactory;

/**
 * OAS specific {@link Crypto} implementation which uses the 
 *  {@link CryptoManager}. 
 * 
 * <br><br><i>Partitially based on sources from the Apache Web Services project
 * (http://ws.apache.org/)</i>
 * 
 * @author EVB
 * @author Alfa & Ariss
 * @since 1.4
 */
public class OACrypto implements Crypto {
    private AbstractSigningFactory _factory;
    private static CertificateFactory _certFactory;
    private static Log _logger;

    /**
     * Create new {@link OACrypto} using the engine.
     * 
     * Requires an initialized {@link Engine} with signing enabled.
     * 
     * @throws OAException If retrieval of signing factory fails.
     */
    public OACrypto() throws OAException {
        _logger = LogFactory.getLog(OACrypto.class);
        try {
            CryptoManager manager = Engine.getInstance().getCryptoManager();
            if (manager == null) {
                _logger.warn("Could not create OACrypto, OAS cryptomanager not initialized");
                throw new OAException(SystemErrors.ERROR_CRYPTO_CREATE);
            }

            _factory = manager.getSigningFactory();
            if (_factory == null) {
                _logger.warn("Could not create OACrypto, OAS signing not enabled");
                throw new OAException(SystemErrors.ERROR_CRYPTO_CREATE);
            }
        } catch (OAException e) {
            throw e;
        } catch (Exception e) {
            _logger.error("Could not create OACrypto, due to internal error", e);
            throw new OAException(SystemErrors.ERROR_INTERNAL);
        }
    }

    /**
     * Used by WSS4J to initialize.
     * @param properties The crypto proprties (not used)
     * @throws OAException If ceration fails.
     */
    public OACrypto(Properties properties) throws OAException {
        this();
    }

    /**
     * Used by WSS4J to initialize.
     * @param properties The crypto proprties (not used)
     * @param loader The classloader to be used.
     * @throws OAException If ceration fails.
     */
    public OACrypto(Properties properties, ClassLoader loader) throws OAException {
        this();
    }

    /**
     * Create new {@link OACrypto} using the given factory.
     * @param factory The signing factory to be used.
     */
    public OACrypto(AbstractSigningFactory factory) {
        _logger = LogFactory.getLog(OACrypto.class);
        _factory = factory;
    }

    /**
     * @see Crypto#getAliasForX509Cert(java.security.cert.Certificate)
     */
    public String getAliasForX509Cert(Certificate cert) throws WSSecurityException {
        try {
            String alias = _factory.getCertificateAlias(cert);
            if (alias == null) {
                // Search
                Enumeration<String> aliases = _factory.getAliases();
                while (alias == null & aliases.hasMoreElements()) {
                    String tAlias = aliases.nextElement();
                    X509Certificate tempCert = (X509Certificate) _factory.getCertificate(alias);
                    if (tempCert.equals(cert)) {
                        alias = tAlias;
                    }
                }
            }
            return alias;
        } catch (CryptoException e) {
            _logger.error("Could not retrieve alias for X509 certificate", e);
            throw new WSSecurityException(WSSecurityException.FAILURE);
        }
    }

    /**
     * @see Crypto#getAliasForX509Cert(java.lang.String)
     */
    public String getAliasForX509Cert(String issuer) throws WSSecurityException {
        try {
            return _factory.getAliasForX509Cert(issuer, null);
        } catch (OAException e) {
            _logger.error("Could not retrieve alias for issuer", e);
            throw new WSSecurityException(WSSecurityException.FAILURE);
        }
    }

    /**
     * @see org.apache.ws.security.components.crypto.Crypto#getAliasForX509Cert(
     *  java.lang.String, java.math.BigInteger)
     */
    public String getAliasForX509Cert(String issuer, BigInteger serialNumber) throws WSSecurityException {
        try {
            return _factory.getAliasForX509Cert(issuer, serialNumber);
        } catch (OAException e) {
            _logger.error("Could not retrieve alias for issuer and serial number", e);
            throw new WSSecurityException(WSSecurityException.FAILURE);
        }
    }

    /**
     * Not supported by OAS.
     * @see org.apache.ws.security.components.crypto.Crypto#getAliasForX509Cert(byte[])
     */
    public String getAliasForX509Cert(byte[] skiBytes) throws WSSecurityException {
        //TODO EVB: implement getAliasForX509Cert
        _logger.error("Could not retrieve alias for SubjectKeyIdentifier, not supported");
        throw new WSSecurityException(WSSecurityException.FAILURE);
    }

    /**
     * Not supported by OAS.
     * @see org.apache.ws.security.components.crypto.Crypto#getAliasForX509CertThumb(byte[])
     */
    public String getAliasForX509CertThumb(byte[] arg0) throws WSSecurityException {
        //TODO EVB: implement getAliasForX509CertThumb
        _logger.error("Could not retrieve alias for Thumbprint, not supported");
        throw new WSSecurityException(WSSecurityException.FAILURE);
    }

    /**
     * Not supported by OAS.
     * @see org.apache.ws.security.components.crypto.Crypto#getAliasesForDN(java.lang.String)
     */
    public String[] getAliasesForDN(String arg0) throws WSSecurityException {
        //TODO EVB: implement getAliasesForDN
        _logger.error("Could not retrieve alias for Thumbprint, not supported");
        throw new WSSecurityException(WSSecurityException.FAILURE);
    }

    /**
     * Retrieve 
     * @see Crypto#getCertificateData(boolean, X509Certificate[])
     */
    public byte[] getCertificateData(boolean reverse, X509Certificate[] certs) throws WSSecurityException {

        try {
            Vector<X509Certificate> list = new Vector<X509Certificate>();
            for (int i = 0; i < certs.length; i++) {
                if (reverse) {
                    list.insertElementAt(certs[i], 0);
                } else {
                    list.add(certs[i]);
                }
            }
            CertPath path = getCertificateFactory().generateCertPath(list);
            return path.getEncoded();
        } catch (CertificateEncodingException e) {
            _logger.warn("Could not encode certificate path", e);
            throw new WSSecurityException(WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, "encodeError", null, e);
        } catch (CertificateException e) {
            _logger.warn("Could not generate certificate path", e);
            throw new WSSecurityException(WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, "parseError", null, e);
        }
    }

    /**
     * Retrieve the singleton instance of the certificate factory. 
     * @see org.apache.ws.security.components.crypto.Crypto#getCertificateFactory()
     */
    public CertificateFactory getCertificateFactory() throws WSSecurityException {
        if (_certFactory == null) {
            try {
                Provider provider = _factory.getKeyStore().getProvider();
                String sProvider = null;
                if (provider != null) {
                    sProvider = provider.getName();
                }
                if (sProvider == null || sProvider.length() == 0) {
                    _certFactory = CertificateFactory.getInstance("X.509");
                } else {
                    _certFactory = CertificateFactory.getInstance("X.509", provider);
                }
            } catch (CertificateException e) {
                throw new WSSecurityException(WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, "unsupportedCertType",
                        null, e);
            }
        }
        return _certFactory;
    }

    /**
     * Retrieve certificate from keystore or trustore.
     * @see org.apache.ws.security.components.crypto.Crypto#getCertificates(java.lang.String)
     */
    public X509Certificate[] getCertificates(String alias) throws WSSecurityException {
        try {
            //Check if own certificate is requested
            if (alias.equals(getDefaultX509Alias())) {
                return new X509Certificate[] { (X509Certificate) _factory.getCertificate() };
            }
            return new X509Certificate[] { (X509Certificate) _factory.getCertificate(alias) };
        } catch (OAException e) {
            _logger.warn("Could not retrieve certificate", e);
            throw new WSSecurityException(WSSecurityException.FAILURE);
        }
    }

    /**
     * Retrieve the default alias.
     * @see org.apache.ws.security.components.crypto.Crypto#getDefaultX509Alias()
     */
    public String getDefaultX509Alias() {
        return _factory.getAlias();
    }

    /**
     * @see org.apache.ws.security.components.crypto.Crypto#getKeyStore()
     */
    public KeyStore getKeyStore() {
        return _factory.getKeyStore();
    }

    /**
     * @see Crypto#getPrivateKey(java.lang.String, java.lang.String)
     */
    public PrivateKey getPrivateKey(String alias, String password) throws Exception {
        if (!alias.equals(getDefaultX509Alias())) {
            _logger.warn("Could not retrieve private key, alias invalid");
            throw new WSSecurityException(WSSecurityException.FAILURE);
        }
        if (!password.equals(_factory.getPrivateKeyPassword())) {
            _logger.warn("Could not retrieve private key, password invalid");
            throw new WSSecurityException(WSSecurityException.FAILURE);
        }
        return _factory.getPrivateKey();
    }

    /**
     * Not supported by OAS.
     * @see Crypto#getSKIBytesFromCert(java.security.cert.X509Certificate)
     */
    public byte[] getSKIBytesFromCert(X509Certificate arg0) throws WSSecurityException {
        //TODO EVB: implement getSKIBytesFromCert
        _logger.error("Could not retrieve SKIBytes certificate, not supported");
        throw new WSSecurityException(WSSecurityException.FAILURE);
    }

    /**
     * Construct an array of certificate's.
     * @see Crypto#getX509Certificates(byte[], boolean)
     */
    public X509Certificate[] getX509Certificates(byte[] data, boolean reverse) throws WSSecurityException {
        X509Certificate[] certs = null;
        try {
            InputStream in = new ByteArrayInputStream(data);
            CertPath path = getCertificateFactory().generateCertPath(in);
            List<? extends Certificate> certificates = path.getCertificates();
            certs = new X509Certificate[certificates.size()];

            int i = 0, size = certificates.size();
            for (X509Certificate cert : certs)
                certs[(reverse) ? (size - 1 - i) : i] = cert;
        } catch (CertificateException e) {
            throw new WSSecurityException(WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, "parseError", null, e);
        }
        return certs;
    }

    /**
     * Load a X509Certificate from the input stream.
     * @see Crypto#loadCertificate(java.io.InputStream)
     */
    public X509Certificate loadCertificate(InputStream in) throws WSSecurityException {
        X509Certificate cert = null;
        try {
            cert = (X509Certificate) getCertificateFactory().generateCertificate(in);
        } catch (CertificateException e) {
            throw new WSSecurityException(WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, "parseError", null, e);
        }
        return cert;
    }

    /**
     * Validate a given certificate chain.
     * @see Crypto#validateCertPath(java.security.cert.X509Certificate[])
     */
    public boolean validateCertPath(X509Certificate[] certs) throws WSSecurityException {
        boolean ok = false;
        try {
            // Generate cert path
            List<X509Certificate> certList = Arrays.asList(certs);
            CertPath path = this.getCertificateFactory().generateCertPath(certList);

            HashSet<TrustAnchor> set = new HashSet<TrustAnchor>();

            if (certs.length == 1) // Use factory certs
            {
                String alias = _factory.getAliasForX509Cert(certs[0].getIssuerDN().getName(),
                        certs[0].getSerialNumber());
                if (alias == null) {
                    _logger.debug("Certificate not trusted");
                    return false;
                }

                X509Certificate cert = (X509Certificate) _factory.getCertificate(alias);
                TrustAnchor anchor = new TrustAnchor(cert, cert.getExtensionValue("2.5.29.30"));
                set.add(anchor);
            } else {
                // Add certificates from the keystore
                Enumeration aliases = _factory.getAliases();
                while (aliases.hasMoreElements()) {
                    String alias = (String) aliases.nextElement();
                    X509Certificate cert = (X509Certificate) _factory.getCertificate(alias);
                    TrustAnchor anchor = new TrustAnchor(cert, cert.getExtensionValue("2.5.29.30"));
                    set.add(anchor);
                }
            }

            PKIXParameters param = new PKIXParameters(set);
            param.setRevocationEnabled(false);
            Provider provider = _factory.getKeyStore().getProvider();
            String sProvider = null;
            CertPathValidator certPathValidator = null;
            if (provider != null) {
                sProvider = provider.getName();
            }
            if (sProvider == null || sProvider.length() == 0) {
                certPathValidator = CertPathValidator.getInstance("PKIX");
            } else {
                certPathValidator = CertPathValidator.getInstance("PKIX", sProvider);
            }
            certPathValidator.validate(path, param);
            ok = true;
        } catch (NoSuchProviderException e) {
            _logger.warn("No such provider", e);
            throw new WSSecurityException(WSSecurityException.FAILURE, "certpath", new Object[] { e.getMessage() },
                    e);
        } catch (NoSuchAlgorithmException e) {
            _logger.warn("No such algorithm", e);
            throw new WSSecurityException(WSSecurityException.FAILURE, "certpath", new Object[] { e.getMessage() },
                    e);
        } catch (InvalidAlgorithmParameterException e) {
            _logger.warn("Invalid algorithm param", e);
            throw new WSSecurityException(WSSecurityException.FAILURE, "certpath", new Object[] { e.getMessage() },
                    e);
        } catch (CertificateException e) {
            _logger.warn("Invalid certificate", e);
            throw new WSSecurityException(WSSecurityException.FAILURE, "certpath", new Object[] { e.getMessage() },
                    e);
        } catch (ClassCastException e) {
            _logger.warn("Certificate is not an X509Certificate", e);
            throw new WSSecurityException(WSSecurityException.FAILURE, "certpath", new Object[] { e.getMessage() },
                    e);
        } catch (CertPathValidatorException e) {
            _logger.warn("Could not validate Cert Path", e);
            throw new WSSecurityException(WSSecurityException.FAILURE, "certpath", new Object[] { e.getMessage() },
                    e);
        } catch (CryptoException e) {
            throw new WSSecurityException(WSSecurityException.FAILURE, "certpath", new Object[] { e.getMessage() },
                    e);
        }
        return ok;
    }
}