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