org.alfresco.extension.countersign.signature.RepositoryManagedSignatureProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.extension.countersign.signature.RepositoryManagedSignatureProvider.java

Source

/*
 * Copyright 2012-2013 Alfresco Software Limited.
 * 
 * Licensed under the GNU Affero General Public License, Version 3.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.gnu.org/licenses/agpl-3.0.html
 * 
 * If you do not wish to be bound to the terms of the AGPL v3.0, 
 * A commercial license may be obtained by contacting the author.
 * 
 * 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.
 * 
 * This file is part of an unsupported extension to Alfresco.
 * 
 */
package org.alfresco.extension.countersign.signature;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.imageio.ImageIO;
import javax.security.auth.x500.X500Principal;

import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.extension.countersign.model.CounterSignSignatureModel;
import org.alfresco.extension.countersign.service.CounterSignService;
import org.alfresco.model.ContentModel;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.openssl.PEMReader;
import org.bouncycastle.openssl.PEMWriter;
import org.bouncycastle.x509.X509V3CertificateGenerator;
import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;

public class RepositoryManagedSignatureProvider implements SignatureProvider {
    private static Log logger = LogFactory.getLog(RepositoryManagedSignatureProvider.class);
    private ServiceRegistry serviceRegistry;
    private String user;
    private Properties config;
    private CounterSignService counterSignService;

    public RepositoryManagedSignatureProvider(ServiceRegistry serviceRegistry,
            CounterSignService counterSignService, String user, Properties config) {
        this.config = config;
        this.serviceRegistry = serviceRegistry;
        this.user = user;
        this.counterSignService = counterSignService;

        // check to see if this user has the signer aspect.  If not, add it.
        // this is temporary until I get the management interface sorted out.
        if (!signatureAvailable()) {
            NodeRef person = serviceRegistry.getPersonService().getPerson(user);
            if (person != null) {
                serviceRegistry.getNodeService().addAspect(person, CounterSignSignatureModel.ASPECT_SIGNER, null);
            }
        }

    }

    @Override
    public KeyStore getUserKeyStore(String storePassword) {

        try {
            NodeRef person = serviceRegistry.getPersonService().getPerson(user);

            if (person == null) {
                return null;
            }

            KeyStore keystore = KeyStore.getInstance("pkcs12");

            // check to see if a keystore exists for this user
            NodeRef keystoreNode = counterSignService.getSignatureArtifact(person,
                    CounterSignSignatureModel.ASSOC_SIGNERKEYSTORE);

            // if no keystore, create one, persist it and associate it with the user
            if (keystoreNode == null) {
                keystore = createUserKeyStore(person, storePassword);
            } else {
                // open the reader to the key and load it
                ContentReader keyReader = serviceRegistry.getContentService().getReader(keystoreNode,
                        ContentModel.PROP_CONTENT);
                keystore.load(keyReader.getContentInputStream(), storePassword.toCharArray());
            }

            // return the keystore
            return keystore;
        } catch (KeyStoreException kse) {
            throw new AlfrescoRuntimeException(kse.getMessage());
        } catch (java.security.cert.CertificateException ce) {
            throw new AlfrescoRuntimeException(ce.getMessage());
        } catch (NoSuchAlgorithmException nsaex) {
            throw new AlfrescoRuntimeException(nsaex.getMessage());
        } catch (IOException ioex) {
            throw new AlfrescoRuntimeException(ioex.getMessage());
        } catch (NoSuchProviderException nspex) {
            throw new AlfrescoRuntimeException(nspex.getMessage());
        }
    }

    @Override
    public BufferedImage getSignatureImage() {
        NodeRef person = serviceRegistry.getPersonService().getPerson(user);
        if (person == null) {
            return null;
        }

        NodeRef sigImage = counterSignService.getSignatureArtifact(person,
                CounterSignSignatureModel.ASSOC_SIGNERSIGNATUREIMAGE);

        if (sigImage != null) {
            ContentReader imageReader = serviceRegistry.getContentService().getReader(sigImage,
                    ContentModel.PROP_CONTENT);

            try {
                return ImageIO.read(imageReader.getContentInputStream());
            } catch (IOException ioex) {
                logger.warn("Could not retrieve signature image as a child of person: " + ioex);
                // generate a default image?
            }
        }

        return null;
    }

    @Override
    public String getSignatureSource() {
        NodeRef person = serviceRegistry.getPersonService().getPerson(user);
        if (person == null) {
            return null;
        }

        NodeRef sigImage = counterSignService.getSignatureArtifact(person,
                CounterSignSignatureModel.ASSOC_SIGNERSIGNATUREIMAGE);

        if (sigImage != null) {
            return String.valueOf(serviceRegistry.getNodeService().getProperty(sigImage,
                    CounterSignSignatureModel.PROP_SIGNATUREJSON));
        }

        return null;
    }

    @Override
    public void saveSignatureImage(BufferedImage image, String source) {

        // save the signature image as a child of the person
        NodeRef person = serviceRegistry.getPersonService().getPerson(user);

        if (person != null) {

            NodeRef sigNode = counterSignService.getSignatureArtifact(person,
                    CounterSignSignatureModel.ASSOC_SIGNERSIGNATUREIMAGE);

            if (sigNode == null) {
                // set up JSON source as property
                Map<QName, Serializable> sigProps = new HashMap<QName, Serializable>();
                sigProps.put(CounterSignSignatureModel.PROP_SIGNATUREJSON, source);

                QName assocQName = QName.createQName(CounterSignSignatureModel.COUNTERSIGN_SIGNATURE_MODEL_1_0_URI,
                        QName.createValidLocalName(user + "-signatureimage"));

                ChildAssociationRef sigChildRef = serviceRegistry.getNodeService().createNode(person,
                        CounterSignSignatureModel.ASSOC_SIGNERSIGNATUREIMAGE, assocQName,
                        CounterSignSignatureModel.TYPE_SIGNATUREIMAGE, sigProps);

                sigNode = sigChildRef.getChildRef();
            } else {
                serviceRegistry.getNodeService().setProperty(sigNode, CounterSignSignatureModel.PROP_SIGNATUREJSON,
                        source);
            }

            // get a writer, store the image content
            ContentWriter writer = serviceRegistry.getContentService().getWriter(sigNode, ContentModel.PROP_CONTENT,
                    true);

            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ImageIO.write(image, "png", baos);
                writer.putContent(new ByteArrayInputStream(baos.toByteArray()));

                PermissionService ps = serviceRegistry.getPermissionService();
                ps.clearPermission(sigNode, PermissionService.ALL_AUTHORITIES);
                ps.setInheritParentPermissions(sigNode, false);
            } catch (IOException ioex) {
                logger.warn("Could not save signature image as child of person: " + ioex);
            }
        }
    }

    @Override
    public boolean signatureAvailable() {
        // signature is only available if this user has the "signer" aspect applied.
        NodeRef person = serviceRegistry.getPersonService().getPerson(user);
        if (person != null
                && serviceRegistry.getNodeService().hasAspect(person, CounterSignSignatureModel.ASPECT_SIGNER)) {
            // now check to see if the user has a keystore available.  If so, then
            // they can sign without creating a new keystore.
            NodeRef keystoreNode = counterSignService.getSignatureArtifact(person,
                    CounterSignSignatureModel.ASSOC_SIGNERKEYSTORE);
            if (keystoreNode != null && serviceRegistry.getNodeService().exists(keystoreNode)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Get the user's public key
     * 
     */
    public PublicKey getPublicKey() {
        NodeRef person = serviceRegistry.getPersonService().getPerson(user);
        NodeRef keyNode = counterSignService.getSignatureArtifact(person,
                CounterSignSignatureModel.ASSOC_SIGNERPUBLICKEY);

        if (keyNode != null) {
            PEMReader parser = null;

            try {
                ContentReader keyReader = serviceRegistry.getContentService().getReader(keyNode,
                        ContentModel.PROP_CONTENT);
                parser = new PEMReader(new InputStreamReader(keyReader.getContentInputStream()));
                PublicKey key = (PublicKey) parser.readObject();
                parser.close();
                return key;
            } catch (Exception ioex) {
                logger.warn("Error reading user public key: " + ioex.getLocalizedMessage());
            } finally {
                try {
                    if (parser != null)
                        parser.close();
                } catch (IOException ioex) {
                    logger.warn("Error closing PEMReader");
                }
            }
        }

        return null;
    }

    @Override
    public boolean validateSignature(byte[] sig, byte[] hash) {
        String alg = config.getProperty(RepositoryManagedSignatureProviderFactory.SIGNATURE_ALGORITHM);
        String prov = config.getProperty(RepositoryManagedSignatureProviderFactory.JAVA_SIGNATURE_PROVIDER);

        boolean valid = false;

        try {
            Signature validate = Signature.getInstance(alg, prov);
            validate.initVerify(getPublicKey());
            validate.update(hash);
            valid = validate.verify(sig);
        } catch (NoSuchProviderException nspe) {
            throw new AlfrescoRuntimeException("Provider: " + prov + " was not found: " + nspe.getMessage());
        } catch (NoSuchAlgorithmException nsae) {
            throw new AlfrescoRuntimeException("Algorithm: " + alg + " is not available: " + nsae.getMessage());
        } catch (SignatureException se) {
            valid = false;
        } catch (InvalidKeyException ike) {
            valid = false;
        }

        return valid;
    }

    /**
     * Sign a hash using the user's private key
     * 
     * @param hash
     * @param key
     * @return
     * @throws Exception
     */
    public byte[] signHash(byte[] hash, String password) throws Exception {

        String alg = config.getProperty(RepositoryManagedSignatureProviderFactory.SIGNATURE_ALGORITHM);
        String prov = config.getProperty(RepositoryManagedSignatureProviderFactory.JAVA_SIGNATURE_PROVIDER);
        String alias = config.getProperty(RepositoryManagedSignatureProviderFactory.ALIAS);

        KeyStore ks = getUserKeyStore(password);
        PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());
        Signature signer = Signature.getInstance(alg, prov);
        signer.initSign(key);
        signer.update(hash);
        return signer.sign();
    }

    /**
     * Compute a hash of the input stream using the configured algorithm
     * 
     * @param contentSteam
     * @return a hash of the content stream
     */
    public byte[] computeHash(InputStream contentStream) {

        String alg = config.getProperty(RepositoryManagedSignatureProviderFactory.HASH_ALGORITHM);

        MessageDigest messageDigest = null;
        try {
            messageDigest = MessageDigest.getInstance(alg);
        } catch (NoSuchAlgorithmException e) {
            logger.error("Unable to process algorithm type: " + alg);
            return null;
        }
        messageDigest.reset();
        byte[] buffer = new byte[1024];
        int bytesRead = -1;
        try {
            while ((bytesRead = contentStream.read(buffer)) > -1) {
                messageDigest.update(buffer, 0, bytesRead);
            }
        } catch (IOException e) {
            logger.error("Unable to read content stream.", e);
            return null;
        } finally {
            try {
                contentStream.close();
            } catch (IOException e) {
            }
        }
        byte[] digest = messageDigest.digest();
        return convertByteArrayToHex(digest).getBytes();
    }

    private String convertByteArrayToHex(byte[] array) {
        StringBuffer hashValue = new StringBuffer();
        for (int i = 0; i < array.length; i++) {
            String hex = Integer.toHexString(0xFF & array[i]);
            if (hex.length() == 1) {
                hashValue.append('0');
            }
            hashValue.append(hex);
        }
        return hashValue.toString().toUpperCase();
    }

    /**
     * Save the user's public key, associated as a child with the person
     * that owns it.  Make this world-readable, as anybody should be able
     * to use it for validating signatures.
     * 
     * @param person
     * @param publicKey
     */
    private void saveUserPublicKey(NodeRef person, PublicKey publicKey) {
        NodeRef keyNode = counterSignService.getSignatureArtifact(person,
                CounterSignSignatureModel.ASSOC_SIGNERPUBLICKEY);

        if (keyNode == null) {
            QName assocQName = QName.createQName(CounterSignSignatureModel.COUNTERSIGN_SIGNATURE_MODEL_1_0_URI,
                    QName.createValidLocalName(user + "-publickey"));

            ChildAssociationRef keyChildRef = serviceRegistry.getNodeService().createNode(person,
                    CounterSignSignatureModel.ASSOC_SIGNERPUBLICKEY, assocQName,
                    CounterSignSignatureModel.TYPE_PUBLICKEY);

            keyNode = keyChildRef.getChildRef();
        }

        // get a writer, store the public key
        ContentWriter writer = serviceRegistry.getContentService().getWriter(keyNode, ContentModel.PROP_CONTENT,
                true);
        PEMWriter pem = null;

        try {
            //set the encoding and write out the key
            serviceRegistry.getNodeService().setProperty(keyNode, CounterSignSignatureModel.PROP_KEYENCODING,
                    "PEM");
            pem = new PEMWriter(new OutputStreamWriter(writer.getContentOutputStream()));
            pem.writeObject(publicKey);
        } catch (IOException ioex) {
            logger.error("Error writing public key to PEMWriter: " + ioex);
        } finally {
            if (pem != null)
                try {
                    pem.close();
                } catch (Exception ex) {
                    logger.error("Error closing PEMWriter");
                }
        }

        // Ensure that the cert is readable by anybody, this is important
        // for later validation of signed content.  Can't validate without the
        // public key
        PermissionService ps = serviceRegistry.getPermissionService();
        ps.setPermission(keyNode, PermissionService.ALL_AUTHORITIES, PermissionService.READ, true);
        ps.setInheritParentPermissions(keyNode, false);
    }

    /**
     * Save the Java KeyStore, associated as a child with the person that owns this keystore
     * 
     * @param person
     * @param keystore
     * @throws IOException 
     * @throws CertificateException 
     * @throws NoSuchAlgorithmException 
     * @throws KeyStoreException 
     */
    private void saveUserKeyStore(NodeRef person, KeyStore keystore, String password)
            throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {

        NodeRef keystoreNode = counterSignService.getSignatureArtifact(person,
                CounterSignSignatureModel.ASSOC_SIGNERKEYSTORE);

        if (keystoreNode == null) {
            QName assocQName = QName.createQName(CounterSignSignatureModel.COUNTERSIGN_SIGNATURE_MODEL_1_0_URI,
                    QName.createValidLocalName(user + "-signaturekeystore"));

            ChildAssociationRef keyChildRef = serviceRegistry.getNodeService().createNode(person,
                    CounterSignSignatureModel.ASSOC_SIGNERKEYSTORE, assocQName,
                    CounterSignSignatureModel.TYPE_PKCS12);

            keystoreNode = keyChildRef.getChildRef();
        }

        // get a writer, store the user keystore
        ContentWriter writer = serviceRegistry.getContentService().getWriter(keystoreNode,
                ContentModel.PROP_CONTENT, true);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        keystore.store(baos, password.toCharArray());
        writer.putContent(new ByteArrayInputStream(baos.toByteArray()));

        // now set the permissions on this node so that only the person that owns it
        // can access it.  PermissionService.ALL_AUTHORITIES = GROUP_EVERYONE
        // this should leave the owner permissions intact
        PermissionService ps = serviceRegistry.getPermissionService();
        ps.clearPermission(keystoreNode, PermissionService.ALL_AUTHORITIES);
        ps.setInheritParentPermissions(keystoreNode, false);
    }

    /**
     * Create a keystore for this user to be used for document signing, store it associated with the user's
     * person node
     * 
     * @param person
     * @param password
     * 
     * @return a Java KeyStore object suitable for document signing
     * @throws NoSuchAlgorithmException 
     * @throws NoSuchProviderException 
     * @throws KeyStoreException 
     * @throws IOException 
     * @throws CertificateException 
     */
    private KeyStore createUserKeyStore(NodeRef person, String password) throws NoSuchAlgorithmException,
            NoSuchProviderException, KeyStoreException, CertificateException, IOException {

        // get the alias from the configuration
        String alias = config.getProperty(RepositoryManagedSignatureProviderFactory.ALIAS);

        // initialize key generator
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
        keyGen.initialize(2048, random);

        // generate a keypair
        KeyPair pair = keyGen.generateKeyPair();
        PrivateKey priv = pair.getPrivate();
        PublicKey pub = pair.getPublic();

        // generate the user certificate
        Certificate cert = generateCertificate(pair, person);

        // get the ca cert used to sign and create cert chain
        KeyStore trustedKs = getTrustedKeyStore();
        Certificate[] caChain = getCaCertChain(trustedKs);
        Certificate[] certChain = new Certificate[caChain.length + 1];
        certChain[0] = cert;
        for (int i = 0; i < caChain.length; i++) {
            certChain[i + 1] = caChain[i];
        }

        // create keystore, adding private key and cert chain
        KeyStore ks = KeyStore.getInstance("pkcs12");
        ks.load(null, password.toCharArray());
        ks.setKeyEntry(alias, priv, password.toCharArray(), certChain);

        // save the keystore
        saveUserKeyStore(person, ks, password);

        // also save the public key separately, will need it 
        // for later validaiton activities
        saveUserPublicKey(person, pub);

        // return the generated keystore
        return ks;

    }

    /**
     * Generate an X509 cert for use as the keystore cert chain
     * 
     * @param keyPair
     * @return
     */
    private X509Certificate generateCertificate(KeyPair keyPair, NodeRef person) {

        X509Certificate cert = null;
        int validDuration = Integer
                .parseInt(config.getProperty(RepositoryManagedSignatureProviderFactory.VALID_DURATION));

        // get user's first and last name
        Map<QName, Serializable> props = serviceRegistry.getNodeService().getProperties(person);
        String firstName = String.valueOf(props.get(ContentModel.PROP_FIRSTNAME));
        String lastName = String.valueOf(props.get(ContentModel.PROP_LASTNAME));

        // backdate the start date by a day
        Calendar start = Calendar.getInstance();
        start.add(Calendar.DATE, -1);
        java.util.Date startDate = start.getTime();

        // what is the end date for this cert's validity?
        Calendar end = Calendar.getInstance();
        end.add(Calendar.DATE, validDuration);
        java.util.Date endDate = end.getTime();

        try {
            // This code works with newer versions of the BouncyCastle libraries, but not
            // the (severely outdated) version that ships with Alfresco
            /*X509v1CertificateBuilder certBuilder = new JcaX509v1CertificateBuilder(
                new X500Principal("CN=" + firstName + " " + lastName), 
                BigInteger.ONE, 
                startDate, cal.getTime(), 
                new X500Principal("CN=" + firstName + " " + lastName), 
                keyPair.getPublic());
                
             AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA");
             AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
             AsymmetricKeyParameter keyParam = PrivateKeyFactory.createKey(keyPair.getPrivate().getEncoded());
            ContentSigner sigGen = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(keyParam);
            X509CertificateHolder certHolder = certBuilder.build(sigGen);
                
            // now lets convert this thing back to a regular old java cert
            CertificateFactory cf = CertificateFactory.getInstance("X.509");  
             InputStream certIs = new ByteArrayInputStream(certHolder.getEncoded()); 
             cert = (X509Certificate) cf.generateCertificate(certIs); 
             certIs.close();*/

            X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
            X500Principal subjectName = new X500Principal("CN=" + firstName + " " + lastName);

            certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
            certGen.setNotBefore(startDate);
            certGen.setNotAfter(endDate);
            certGen.setSubjectDN(subjectName);
            certGen.setPublicKey(keyPair.getPublic());
            certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");

            // if we are actually generating a trusted cert, the action is a little different
            boolean generateTrusted = Boolean.parseBoolean(
                    config.getProperty(RepositoryManagedSignatureProviderFactory.ENABLE_TRUSTED_CERTS));
            if (generateTrusted) {
                KeyStore trustedKs = getTrustedKeyStore();

                PrivateKey caKey = getCaKey(trustedKs);
                X509Certificate caCert = getCaCert(trustedKs);

                // set the issuer of the generated cert to the subject of the ca cert
                X500Principal caSubject = caCert.getSubjectX500Principal();
                certGen.setIssuerDN(caSubject);

                //add the required extensions for the new cert
                certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
                        new AuthorityKeyIdentifierStructure(caCert));
                certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false,
                        new SubjectKeyIdentifierStructure(keyPair.getPublic()));

                cert = certGen.generate(caKey, "BC");

                //verify the cert
                cert.verify(caCert.getPublicKey());
            } else {
                certGen.setIssuerDN(subjectName);
                cert = certGen.generate(keyPair.getPrivate(), "BC");
            }
        } catch (CertificateException ce) {
            logger.error("CertificateException creating or validating X509 certificate for user: " + ce);
            throw new AlfrescoRuntimeException(ce.getMessage());
        } catch (Exception ex) {
            logger.error("Unknown exception creating or validating X509 certificate for user : " + ex);
            ex.printStackTrace();
        }

        return cert;
    }

    /**
     * Get the Certificate Authority private key
     * 
     * @return
     */
    private PrivateKey getCaKey(KeyStore trustedKs) {
        PrivateKey caKey = null;
        String keyAlias = config.getProperty(RepositoryManagedSignatureProviderFactory.TRUSTED_KEY_ALIAS);
        String keyPassword = config.getProperty(RepositoryManagedSignatureProviderFactory.TRUSTED_KEY_PASSWORD);

        try {
            caKey = (PrivateKey) trustedKs.getKey(keyAlias, keyPassword.toCharArray());
        } catch (KeyStoreException kse) {
            throw new AlfrescoRuntimeException(kse.getMessage());
        } catch (UnrecoverableKeyException uke) {
            throw new AlfrescoRuntimeException(uke.getMessage());
        } catch (NoSuchAlgorithmException nsae) {
            throw new AlfrescoRuntimeException(nsae.getMessage());
        }

        return caKey;
    }

    /**
     * Get the Certificate Authority public key certificate
     * 
     * @return
     */
    private X509Certificate getCaCert(KeyStore trustedKs) {
        X509Certificate caCert = null;
        String certAlias = config.getProperty(RepositoryManagedSignatureProviderFactory.TRUSTED_CERT_ALIAS);

        try {
            caCert = (X509Certificate) trustedKs.getCertificate(certAlias);
        } catch (KeyStoreException kse) {
            throw new AlfrescoRuntimeException(kse.getMessage());
        }

        return caCert;
    }

    /**
     * Get the certificate chain for the CA certificate
     * 
     * @param trustedKs
     * @return
     */
    private Certificate[] getCaCertChain(KeyStore trustedKs) {
        Certificate[] caCertChain = null;
        String certAlias = config.getProperty(RepositoryManagedSignatureProviderFactory.TRUSTED_CERT_ALIAS);

        try {
            caCertChain = trustedKs.getCertificateChain(certAlias);
        } catch (KeyStoreException kse) {
            throw new AlfrescoRuntimeException(kse.getMessage());
        }

        return caCertChain;
    }

    /**
     * Get the trusted keystore as configured in the extension properties.
     * 
     * @return
     */
    private KeyStore getTrustedKeyStore() {
        try {
            String keystorePassword = config
                    .getProperty(RepositoryManagedSignatureProviderFactory.TRUSTED_KEYSTORE_PASSWORD);
            String keystorePath = config
                    .getProperty(RepositoryManagedSignatureProviderFactory.TRUSTED_KEYSTORE_PATH);
            KeyStore keystore = KeyStore.getInstance("pkcs12");
            FileInputStream keyStream = new FileInputStream(keystorePath);
            keystore.load(keyStream, keystorePassword.toCharArray());

            // return the keystore
            return keystore;
        } catch (KeyStoreException kse) {
            throw new AlfrescoRuntimeException(kse.getMessage());
        } catch (java.security.cert.CertificateException ce) {
            throw new AlfrescoRuntimeException(ce.getMessage());
        } catch (NoSuchAlgorithmException nsaex) {
            throw new AlfrescoRuntimeException(nsaex.getMessage());
        } catch (IOException ioex) {
            throw new AlfrescoRuntimeException(ioex.getMessage());
        }
    }

    public void setServiceRegistry(ServiceRegistry serviceRegistry) {
        this.serviceRegistry = serviceRegistry;
    }
}