001/*
002 * (C) Copyright 2011 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *    Wojciech Sulejman
016 */
017package org.nuxeo.ecm.platform.signature.core.pki;
018
019import java.io.File;
020import java.io.FileInputStream;
021import java.io.FileNotFoundException;
022import java.io.IOException;
023import java.io.InputStream;
024import java.security.KeyStore;
025import java.security.KeyStoreException;
026import java.security.NoSuchAlgorithmException;
027import java.security.cert.CertificateEncodingException;
028import java.security.cert.CertificateException;
029import java.security.cert.X509Certificate;
030import java.util.ArrayList;
031import java.util.List;
032
033import org.nuxeo.ecm.platform.signature.api.exception.CertException;
034import org.nuxeo.ecm.platform.signature.api.pki.RootService;
035import org.nuxeo.runtime.model.ComponentContext;
036import org.nuxeo.runtime.model.ComponentInstance;
037import org.nuxeo.runtime.model.DefaultComponent;
038
039/**
040 * @author <a href="mailto:ws@nuxeo.com">Wojciech Sulejman</a>
041 */
042public class RootServiceImpl extends DefaultComponent implements RootService {
043
044    private KeyStore rootKeyStore;
045
046    private String rootKeystoreFilePath;
047
048    private String rootKeystorePassword;
049
050    private String rootCertificateAlias;
051
052    private String rootKeyAlias;
053
054    private String rootKeyPassword;
055
056    protected List<RootDescriptor> config;
057
058    private static final String KEYSTORE_TYPE = "JKS";
059
060    @Override
061    public void activate(ComponentContext context) {
062        config = new ArrayList<RootDescriptor>();
063    }
064
065    @Override
066    public KeyStore getRootKeyStore() {
067        return rootKeyStore;
068    }
069
070    @Override
071    public void setRootKeyStore(KeyStore rootKeyStore) {
072        this.rootKeyStore = rootKeyStore;
073    }
074
075    @Override
076    public String getRootKeystoreFilePath() {
077        return rootKeystoreFilePath;
078    }
079
080    @Override
081    public void setRootKeystoreFilePath(String rootKeystoreFilePath) {
082        this.rootKeystoreFilePath = rootKeystoreFilePath;
083    }
084
085    @Override
086    public String getRootKeystorePassword() {
087        return rootKeystorePassword;
088    }
089
090    @Override
091    public void setRootKeystorePassword(String rootKeystorePassword) {
092        this.rootKeystorePassword = rootKeystorePassword;
093    }
094
095    @Override
096    public String getRootCertificateAlias() {
097        return rootCertificateAlias;
098    }
099
100    @Override
101    public void setRootCertificateAlias(String rootCertificateAlias) {
102        this.rootCertificateAlias = rootCertificateAlias;
103    }
104
105    @Override
106    public String getRootKeyAlias() {
107        return rootKeyAlias;
108    }
109
110    @Override
111    public void setRootKeyAlias(String rootKeyAlias) {
112        this.rootKeyAlias = rootKeyAlias;
113    }
114
115    @Override
116    public String getRootKeyPassword() {
117        return rootKeyPassword;
118    }
119
120    @Override
121    public void setRootKeyPassword(String rootKeyPassword) {
122        this.rootKeyPassword = rootKeyPassword;
123    }
124
125    @Override
126    public boolean isRootSetup() {
127        boolean rootIsSetup = false;
128        if (rootKeyStore != null && rootKeystorePassword != null && rootCertificateAlias != null
129                && rootKeyAlias != null && rootKeyPassword != null) {
130            rootIsSetup = true;
131        }
132        return rootIsSetup;
133    }
134
135    protected void initializeRoot() throws CertException {
136        for (RootDescriptor certDescriptor : config) {
137            if (certDescriptor.getRootKeystoreFilePath() != null) {
138                setRootKeystoreFilePath(certDescriptor.getRootKeystoreFilePath());
139            } else if (getRootKeyStore() == null) {
140                throw new CertException("Keystore path is missing");
141            }
142            if (certDescriptor.getRootCertificateAlias() != null) {
143                setRootCertificateAlias(certDescriptor.getRootCertificateAlias());
144            } else {
145                throw new CertException("You have to provide root certificate alias");
146            }
147            if (certDescriptor.getRootKeystorePassword() != null) {
148                setRootKeystorePassword(certDescriptor.getRootKeystorePassword());
149            } else {
150                throw new CertException("You have to provide root keystore password");
151            }
152            if (certDescriptor.getRootKeyAlias() != null) {
153                setRootKeyAlias(certDescriptor.getRootKeyAlias());
154            } else {
155                throw new CertException("You have to provide root key alias");
156            }
157            if (certDescriptor.getRootKeyPassword() != null) {
158                setRootKeyPassword(certDescriptor.getRootKeyPassword());
159            } else {
160                throw new CertException("You have to provide root key password");
161            }
162        }
163        KeyStore keystore = getKeyStore(getRootKeystoreIS(), getRootKeystorePassword());
164        setRootKeyStore(keystore);
165    }
166
167    public KeyStore getKeyStore(InputStream keystoreIS, String password) throws CertException {
168        KeyStore ks;
169        try {
170            ks = java.security.KeyStore.getInstance(KEYSTORE_TYPE);
171            ks.load(keystoreIS, password.toCharArray());
172        } catch (KeyStoreException e) {
173            throw new CertException(e);
174        } catch (NoSuchAlgorithmException e) {
175            throw new CertException(e);
176        } catch (CertificateException e) {
177            throw new CertException(e);
178        } catch (IOException e) {
179            throw new CertException(e);
180        }
181        return ks;
182    }
183
184    @Override
185    public InputStream getRootKeystoreIS() throws CertException {
186        InputStream keystoreIS = null;
187        File rootKeystoreFile = null;
188        try {
189            rootKeystoreFile = new File(getRootKeystoreFilePath());
190            if (rootKeystoreFile.exists()) {
191                keystoreIS = new FileInputStream(rootKeystoreFile);
192            } else {// try a temporary resource keystore instead of a
193                // configurable one
194                keystoreIS = Thread.currentThread().getContextClassLoader().getResourceAsStream(
195                        getRootKeystoreFilePath());
196            }
197        } catch (IOException e) {
198            // try local path
199            throw new CertException("Certificate not found at" + rootKeystoreFile.getAbsolutePath());
200        }
201        return keystoreIS;
202    }
203
204    /**
205     * Public certificate for the CA root. Encoded as an ASN.1 DER ("anybody there?") formatted byte array.
206     */
207    public byte[] getRootPublicCertificate() throws CertException {
208        X509Certificate certificate;
209        try {
210            certificate = getCertificate(getRootKeyStore(), getRootCertificateAlias());
211            return certificate.getEncoded();
212        } catch (CertificateEncodingException e) {
213            throw new CertException(e);
214        }
215    }
216
217    // custom certificate type for the root certificate
218    protected X509Certificate getCertificate(KeyStore ks, String certificateAlias) throws CertException {
219        X509Certificate certificate = null;
220        try {
221
222            if (ks == null) {
223                throw new CertException("Keystore missing for " + certificateAlias);
224            }
225            if (ks.containsAlias(certificateAlias)) {
226                certificate = (X509Certificate) ks.getCertificate(certificateAlias);
227            } else {
228                throw new CertException("Certificate not found");
229            }
230        } catch (KeyStoreException e) {
231            throw new CertException(e);
232        }
233        return certificate;
234    }
235
236    @Override
237    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor)
238            throws CertException {
239        config.add((RootDescriptor) contribution);
240        initializeRoot();
241        if (!isRootSetup()) {
242            throw new CertException("Root keystore was not set up correctly");
243        }
244    }
245
246    @Override
247    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
248        config.remove(contribution);
249    }
250
251}