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}