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}