eu.eidas.auth.engine.core.impl.SignP12.java Source code

Java tutorial

Introduction

Here is the source code for eu.eidas.auth.engine.core.impl.SignP12.java

Source

/* 
 * Licensed under the EUPL, Version 1.1 or  as soon they will be approved by
 * the European Commission - subsequent versions of the EUPL (the "Licence");
 * You may not use this work except in compliance with the Licence. You may
 * obtain a copy of the Licence at:
 * 
 * http://www.osor.eu/eupl/european-union-public-licence-eupl-v.1.1
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the Licence is distributed on an "AS IS" basis, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * Licence for the specific language governing permissions and limitations under
 * the Licence.
 */

package eu.eidas.auth.engine.core.impl;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.Provider;
import java.security.Security;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.InvalidPropertiesFormatException;
import java.util.List;
import java.util.Properties;

import eu.eidas.auth.engine.metadata.MetadataProcessorI;
import eu.eidas.engine.exceptions.SAMLEngineException;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.NotImplementedException;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.opensaml.Configuration;
import org.opensaml.common.SAMLObject;
import org.opensaml.common.SignableSAMLObject;
import org.opensaml.security.SAMLSignatureProfileValidator;
import org.opensaml.xml.io.MarshallingException;
import org.opensaml.xml.security.SecurityHelper;
import org.opensaml.xml.security.credential.Credential;
import org.opensaml.xml.security.trust.ExplicitKeyTrustEvaluator;
import org.opensaml.xml.security.x509.BasicX509Credential;
import org.opensaml.xml.signature.KeyInfo;
import org.opensaml.xml.signature.Signature;
import org.opensaml.xml.signature.SignatureException;
import org.opensaml.xml.signature.SignatureValidator;
import org.opensaml.xml.signature.Signer;
import org.opensaml.xml.util.Base64;
import org.opensaml.xml.validation.ValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The Class SignP12. Class responsible for signing and validating of messages
 * SAML with a certificate store software.
 * 
 * @author fjquevedo
 */
public final class SignP12 extends AbstractSigner {

    /** The logger. */
    private static final Logger LOG = LoggerFactory.getLogger(SignP12.class.getName());

    /** The p12 store. */
    private KeyStore p12Store = null;

    /** The trust store. */
    private KeyStore trustStore = null;

    /**
     * Gets the trust store.
     * 
     * @return the trust store
     */
    public KeyStore getTrustStore() {
        return trustStore;
    }

    /**
     * Sets the trust store.
     * 
     * @param newTrustStore the new trust store
     */
    public void setTrustStore(final KeyStore newTrustStore) {
        this.trustStore = newTrustStore;
    }

    /**
     * Gets the p12 store.
     * 
     * @return the p12 store
     */
    public KeyStore getP12Store() {
        return p12Store;
    }

    /**
     * Sets the p12 store.
     * 
     * @param newP12Store the new p12 store
     */
    public void setP12Store(final KeyStore newP12Store) {
        this.p12Store = newP12Store;
    }

    private InputStream loadFileProperties(String fileConf) throws IOException {
        InputStream fileProperties = null;
        try {
            LOG.trace("File to load " + fileConf);
            fileProperties = new FileInputStream(fileConf);
            getProperties().loadFromXML(fileProperties);
        } catch (Exception e) {
            LOG.info("Unable to load external file, trying in classpath");
            fileProperties = SignP12.class.getResourceAsStream("/" + fileConf);
            if (fileProperties == null) {
                fileProperties = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileConf);
                if (fileProperties == null) {
                    Enumeration<URL> files = ClassLoader.getSystemClassLoader().getResources(fileConf);
                    if (files != null && files.hasMoreElements()) {
                        LOG.debug("file loaded");
                        fileProperties = ClassLoader.getSystemClassLoader()
                                .getResourceAsStream(files.nextElement().getFile());
                    } else {
                        throw new IOException("ERROR : File not found" + fileConf, e);
                    }
                }
            }
        }
        return fileProperties;
    }

    /**
     * Initialize the file configuration.
     * 
     * @param fileConf name of the file configuration
     * 
     * @throws SAMLEngineException error at the load from  file configuration
     */
    public void init(final String fileConf) throws SAMLEngineException {
        InputStream fileProperties = null;
        setProperties(new Properties());
        try {
            fileProperties = loadFileProperties(fileConf);
            LOG.trace("Loaded " + fileProperties.available() + " bytes");
            getProperties().loadFromXML(fileProperties);

        } catch (InvalidPropertiesFormatException e) {
            LOG.info("Exception: invalid properties format.", e);
            throw new SAMLEngineException(e);
        } catch (IOException e) {
            LOG.info("Exception: invalid file: " + fileConf, e);
            throw new SAMLEngineException(e);
        } finally {
            IOUtils.closeQuietly(fileProperties);
        }
    }

    /**
     * Gets the certificate.
     * 
     * @return the X509Certificate
     *     
     */
    public X509Certificate getCertificate() {
        throw new NotImplementedException();
    }

    /**
     * Sign the token SAML.
     * 
     * @param tokenSaml token SAML
     * 
     * @return the X509Certificate signed.
     * 
     * @throws SAMLEngineException error at sign SAML token
     *
     */
    public SAMLObject sign(final SignableSAMLObject tokenSaml) throws SAMLEngineException {
        LOG.info("Start Sign process");
        try {
            Signature signature = computeSignature(p12Store);

            tokenSaml.setSignature(signature);

            LOG.info("Marshall samlToken.");
            Configuration.getMarshallerFactory().getMarshaller(tokenSaml).marshall(tokenSaml);

            LOG.info("Sign samlToken.");
            Signer.signObject(signature);

        } catch (MarshallingException e) {
            LOG.error("MarshallingException");
            throw new SAMLEngineException(e);
        } catch (SignatureException e) {
            LOG.error("Signature exception.");
            throw new SAMLEngineException(e);
        }

        return tokenSaml;
    }

    private void validateProfileSignature(final SignableSAMLObject tokenSaml) throws SAMLEngineException {
        final SAMLSignatureProfileValidator sigProfValidator = new SAMLSignatureProfileValidator();
        try {
            // Indicates signature id conform to SAML Signature profile
            sigProfValidator.validate(tokenSaml.getSignature());
        } catch (ValidationException e) {
            LOG.info("ValidationException: signature isn't conform to SAML Signature profile.");
            throw new SAMLEngineException(e);
        }

    }

    /**
     * Validate signature.
     * 
     * @param tokenSaml token SAML
     * 
     * @return the SAMLObject validated.
     * 
     * @throws SAMLEngineException error validate signature
     * 
     */
    public SAMLObject validateSignature(final SignableSAMLObject tokenSaml, String messageFormat)
            throws SAMLEngineException {
        LOG.info("Start signature validation.");
        try {

            // Validate structure signature
            validateProfileSignature(tokenSaml);

            String aliasCert = null;
            X509Certificate certificate;

            final List<Credential> trustCred = new ArrayList<Credential>();

            for (final Enumeration<String> e = trustStore.aliases(); e.hasMoreElements();) {
                aliasCert = e.nextElement();
                final BasicX509Credential credential = new BasicX509Credential();
                certificate = (X509Certificate) trustStore.getCertificate(aliasCert);
                credential.setEntityCertificate(certificate);
                trustCred.add(credential);
            }

            final KeyInfo keyInfo = tokenSaml.getSignature().getKeyInfo();

            final org.opensaml.xml.signature.X509Certificate xmlCert = keyInfo.getX509Datas().get(0)
                    .getX509Certificates().get(0);

            final CertificateFactory certFact = CertificateFactory.getInstance("X.509");
            final ByteArrayInputStream bis = new ByteArrayInputStream(Base64.decode(xmlCert.getValue()));
            final X509Certificate cert = (X509Certificate) certFact.generateCertificate(bis);

            // Exist only one certificate
            final BasicX509Credential entityX509Cred = new BasicX509Credential();
            entityX509Cred.setEntityCertificate(cert);

            // Validate trust certificates
            final ExplicitKeyTrustEvaluator keyTrustEvaluator = new ExplicitKeyTrustEvaluator();
            if (!keyTrustEvaluator.validate(entityX509Cred, trustCred)) {
                throw new SAMLEngineException("Certificate it is not trusted.");
            }

            // Validate signature
            final SignatureValidator sigValidator = new SignatureValidator(entityX509Cred);
            LOG.info("Key algorithm {}",
                    SecurityHelper.getKeyAlgorithmFromURI(tokenSaml.getSignature().getSignatureAlgorithm()));
            sigValidator.validate(tokenSaml.getSignature());

        } catch (ValidationException e) {
            LOG.info("ValidationException.");
            throw new SAMLEngineException(e);
        } catch (KeyStoreException e) {
            LOG.error("KeyStoreException.", e);
            throw new SAMLEngineException(e);
        } catch (GeneralSecurityException e) {
            LOG.error("GeneralSecurityException.", e);
            throw new SAMLEngineException(e);
        }
        return tokenSaml;
    }

    /**
     * Load cryptographic service provider.
     * 
     * @throws SAMLEngineException the SAML engine exception
     */
    public void loadCryptServiceProvider() throws SAMLEngineException {
        LOG.info("Load Cryptographic Service Provider");

        FileInputStream fis = null;
        FileInputStream fisTrustStore = null;

        try {
            // Dynamically register Bouncy Castle provider.
            boolean found = false;
            // Check if BouncyCastle is already registered as a provider
            final Provider[] providers = Security.getProviders();
            for (int i = 0; i < providers.length; i++) {
                if (providers[i].getName().equals(BouncyCastleProvider.PROVIDER_NAME)) {
                    found = true;
                }
            }

            // Register only if the provider has not been previously registered
            if (!found) {
                LOG.debug("SAMLCore: Register Bouncy Castle provider.");
                Security.insertProviderAt(new BouncyCastleProvider(), Security.getProviders().length);
            }

            p12Store = KeyStore.getInstance(getProperties().getProperty("keystoreType"));

            fis = new FileInputStream(getProperties().getProperty("keystorePath"));

            p12Store.load(fis, getProperties().getProperty("keyStorePassword").toCharArray());

            trustStore = KeyStore.getInstance(getProperties().getProperty("trustStoreType"));

            fisTrustStore = new FileInputStream(getProperties().getProperty("trustStorePath"));
            trustStore.load(fisTrustStore, getProperties().getProperty("trustStorePassword").toCharArray());

        } catch (Exception e) {
            throw new SAMLEngineException("Error loading CryptographicServiceProvider", e);
        } finally {
            IOUtils.closeQuietly(fis);
            IOUtils.closeQuietly(fisTrustStore);
        }
    }

    public void setMetadataProcessor(MetadataProcessorI processor) {
        //TODO
    }

    public SAMLObject signMetadata(SignableSAMLObject tokenSaml) throws SAMLEngineException {
        return sign(tokenSaml);
    }

}