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.IOException;
024import java.io.InputStream;
025import java.security.KeyStore;
026import java.security.KeyStoreException;
027import java.security.NoSuchAlgorithmException;
028import java.security.cert.CertificateEncodingException;
029import java.security.cert.CertificateException;
030import java.security.cert.X509Certificate;
031import java.util.ArrayList;
032import java.util.List;
033
034import org.nuxeo.ecm.platform.signature.api.exception.CertException;
035import org.nuxeo.ecm.platform.signature.api.pki.RootService;
036import org.nuxeo.runtime.model.ComponentContext;
037import org.nuxeo.runtime.model.ComponentInstance;
038import org.nuxeo.runtime.model.DefaultComponent;
039
040/**
041 * @author <a href="mailto:ws@nuxeo.com">Wojciech Sulejman</a>
042 */
043public class RootServiceImpl extends DefaultComponent implements RootService {
044
045    private KeyStore rootKeyStore;
046
047    private String rootKeystoreFilePath;
048
049    private String rootKeystorePassword;
050
051    private String rootCertificateAlias;
052
053    private String rootKeyAlias;
054
055    private String rootKeyPassword;
056
057    protected List<RootDescriptor> config;
058
059    private static final String KEYSTORE_TYPE = "JKS";
060
061    @Override
062    public void activate(ComponentContext context) {
063        config = new ArrayList<>();
064    }
065
066    @Override
067    public KeyStore getRootKeyStore() {
068        return rootKeyStore;
069    }
070
071    @Override
072    public void setRootKeyStore(KeyStore rootKeyStore) {
073        this.rootKeyStore = rootKeyStore;
074    }
075
076    @Override
077    public String getRootKeystoreFilePath() {
078        return rootKeystoreFilePath;
079    }
080
081    @Override
082    public void setRootKeystoreFilePath(String rootKeystoreFilePath) {
083        this.rootKeystoreFilePath = rootKeystoreFilePath;
084    }
085
086    @Override
087    public String getRootKeystorePassword() {
088        return rootKeystorePassword;
089    }
090
091    @Override
092    public void setRootKeystorePassword(String rootKeystorePassword) {
093        this.rootKeystorePassword = rootKeystorePassword;
094    }
095
096    @Override
097    public String getRootCertificateAlias() {
098        return rootCertificateAlias;
099    }
100
101    @Override
102    public void setRootCertificateAlias(String rootCertificateAlias) {
103        this.rootCertificateAlias = rootCertificateAlias;
104    }
105
106    @Override
107    public String getRootKeyAlias() {
108        return rootKeyAlias;
109    }
110
111    @Override
112    public void setRootKeyAlias(String rootKeyAlias) {
113        this.rootKeyAlias = rootKeyAlias;
114    }
115
116    @Override
117    public String getRootKeyPassword() {
118        return rootKeyPassword;
119    }
120
121    @Override
122    public void setRootKeyPassword(String rootKeyPassword) {
123        this.rootKeyPassword = rootKeyPassword;
124    }
125
126    @Override
127    public boolean isRootSetup() {
128        boolean rootIsSetup = false;
129        if (rootKeyStore != null && rootKeystorePassword != null && rootCertificateAlias != null
130                && rootKeyAlias != null && rootKeyPassword != null) {
131            rootIsSetup = true;
132        }
133        return rootIsSetup;
134    }
135
136    protected void initializeRoot() throws CertException {
137        for (RootDescriptor certDescriptor : config) {
138            if (certDescriptor.getRootKeystoreFilePath() != null) {
139                setRootKeystoreFilePath(certDescriptor.getRootKeystoreFilePath());
140            } else if (getRootKeyStore() == null) {
141                throw new CertException("Keystore path is missing");
142            }
143            if (certDescriptor.getRootCertificateAlias() != null) {
144                setRootCertificateAlias(certDescriptor.getRootCertificateAlias());
145            } else {
146                throw new CertException("You have to provide root certificate alias");
147            }
148            if (certDescriptor.getRootKeystorePassword() != null) {
149                setRootKeystorePassword(certDescriptor.getRootKeystorePassword());
150            } else {
151                throw new CertException("You have to provide root keystore password");
152            }
153            if (certDescriptor.getRootKeyAlias() != null) {
154                setRootKeyAlias(certDescriptor.getRootKeyAlias());
155            } else {
156                throw new CertException("You have to provide root key alias");
157            }
158            if (certDescriptor.getRootKeyPassword() != null) {
159                setRootKeyPassword(certDescriptor.getRootKeyPassword());
160            } else {
161                throw new CertException("You have to provide root key password");
162            }
163        }
164        KeyStore keystore = getKeyStore(getRootKeystoreIS(), getRootKeystorePassword());
165        setRootKeyStore(keystore);
166    }
167
168    public KeyStore getKeyStore(InputStream keystoreIS, String password) throws CertException {
169        KeyStore ks;
170        try {
171            ks = java.security.KeyStore.getInstance(KEYSTORE_TYPE);
172            ks.load(keystoreIS, password.toCharArray());
173        } catch (KeyStoreException e) {
174            throw new CertException(e);
175        } catch (NoSuchAlgorithmException e) {
176            throw new CertException(e);
177        } catch (CertificateException e) {
178            throw new CertException(e);
179        } catch (IOException e) {
180            throw new CertException(e);
181        }
182        return ks;
183    }
184
185    @Override
186    public InputStream getRootKeystoreIS() throws CertException {
187        InputStream keystoreIS = null;
188        File rootKeystoreFile = null;
189        try {
190            rootKeystoreFile = new File(getRootKeystoreFilePath());
191            if (rootKeystoreFile.exists()) {
192                keystoreIS = new FileInputStream(rootKeystoreFile);
193            } else {// try a temporary resource keystore instead of a
194                // configurable one
195                keystoreIS = Thread.currentThread().getContextClassLoader().getResourceAsStream(
196                        getRootKeystoreFilePath());
197            }
198        } catch (IOException e) {
199            // try local path
200            throw new CertException("Certificate not found at" + rootKeystoreFile.getAbsolutePath());
201        }
202        return keystoreIS;
203    }
204
205    /**
206     * Public certificate for the CA root. Encoded as an ASN.1 DER ("anybody there?") formatted byte array.
207     */
208    @Override
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}