1 13 14 package org.ejbca.ui.web.protocol; 15 16 import java.io.BufferedInputStream ; 17 import java.io.File ; 18 import java.io.FileInputStream ; 19 import java.io.IOException ; 20 import java.io.PrintWriter ; 21 import java.io.StringWriter ; 22 import java.math.BigInteger ; 23 import java.security.KeyStore ; 24 import java.security.PrivateKey ; 25 import java.security.PublicKey ; 26 import java.security.cert.Certificate ; 27 import java.security.cert.CertificateFactory ; 28 import java.security.cert.X509Certificate ; 29 import java.security.interfaces.RSAPublicKey ; 30 import java.util.ArrayList ; 31 import java.util.Arrays ; 32 import java.util.Collection ; 33 import java.util.Enumeration ; 34 import java.util.HashMap ; 35 import java.util.Iterator ; 36 import java.util.List ; 37 import java.util.Map ; 38 39 import javax.ejb.EJBException ; 40 import javax.servlet.ServletConfig ; 41 import javax.servlet.ServletException ; 42 43 import org.apache.log4j.Logger; 44 import org.bouncycastle.ocsp.BasicOCSPResp; 45 import org.ejbca.core.ejb.ServiceLocator; 46 import org.ejbca.core.ejb.ca.store.ICertificateStoreOnlyDataSessionLocal; 47 import org.ejbca.core.ejb.ca.store.ICertificateStoreOnlyDataSessionLocalHome; 48 import org.ejbca.core.model.InternalResources; 49 import org.ejbca.core.model.ca.caadmin.extendedcaservices.ExtendedCAServiceNotActiveException; 50 import org.ejbca.core.model.ca.caadmin.extendedcaservices.ExtendedCAServiceRequestException; 51 import org.ejbca.core.model.ca.caadmin.extendedcaservices.OCSPCAServiceRequest; 52 import org.ejbca.core.model.ca.caadmin.extendedcaservices.OCSPCAServiceResponse; 53 import org.ejbca.core.model.ca.crl.RevokedCertInfo; 54 import org.ejbca.core.model.log.Admin; 55 import org.ejbca.core.protocol.ocsp.OCSPUtil; 56 import org.ejbca.ui.web.pub.cluster.ExtOCSPHealthCheck; 57 58 104 public class OCSPServletStandAlone extends OCSPServletBase implements IHealtChecker { 105 106 static final private Logger m_log = Logger.getLogger(OCSPServletStandAlone.class); 107 108 private static final InternalResources intres = InternalResources.getInstance(); 109 110 private String mKeystoreDirectoryName; 111 private char mKeyPassword[]; 112 private char mStorePassword[]; 113 private CardKeys mHardTokenObject; 114 private final Map mSignEntity; 115 private ICertificateStoreOnlyDataSessionLocal m_certStore = null; 116 117 public OCSPServletStandAlone() { 118 super(); 119 mSignEntity = new HashMap (); 120 } 121 public void init(ServletConfig config) throws ServletException { 122 super.init(config); 123 try { 124 { 125 final String keyPassword = config.getInitParameter("keyPassword"); 126 mKeyPassword = keyPassword!=null ? keyPassword.toCharArray() : null; 127 } 128 if ( mKeyPassword==null || mKeyPassword.length==0 ) 129 throw new ServletException ("no keystore password given"); 130 { 131 final String storePassword = config.getInitParameter("storePassword"); 132 mStorePassword = storePassword!=null ? storePassword.toCharArray() : null; 133 } 134 if ( mHardTokenObject==null ) { 135 final String hardTokenClassName = config.getInitParameter("hardTokenClassName"); 136 if ( hardTokenClassName!=null && hardTokenClassName.length()>0 ) { 137 String sCardPassword = config.getInitParameter("cardPassword"); 138 sCardPassword = sCardPassword!=null ? sCardPassword.trim() : null; 139 if ( sCardPassword!=null && sCardPassword.length()>0 ) { 140 try { 141 mHardTokenObject = (CardKeys)OCSPServletStandAlone.class.getClassLoader().loadClass(hardTokenClassName).newInstance(); 142 mHardTokenObject.autenticate(sCardPassword); 143 } catch( ClassNotFoundException e) { 144 String iMsg = intres.getLocalizedMessage("ocsp.classnotfound", hardTokenClassName); 145 m_log.info(iMsg); 146 } 147 } else { 148 String iMsg = intres.getLocalizedMessage("ocsp.nocardpwd"); 149 m_log.info(iMsg); 150 } 151 } else { 152 String iMsg = intres.getLocalizedMessage("ocsp.nohwsigningclass"); 153 m_log.info(iMsg); 154 } 155 } 156 if ( mStorePassword==null || mStorePassword.length==0 ) 157 mStorePassword = mKeyPassword; 158 mKeystoreDirectoryName = config.getInitParameter("softKeyDirectoryName"); 159 if ( mKeystoreDirectoryName!=null && mKeystoreDirectoryName.length()>0 ) { 160 ExtOCSPHealthCheck.setHealtChecker(this); 161 return; 162 } else { 163 String errMsg = intres.getLocalizedMessage("ocsp.errornovalidkeys"); 164 throw new ServletException (errMsg); 165 } 166 } catch( ServletException e ) { 167 throw e; 168 } catch (Exception e) { 169 String errMsg = intres.getLocalizedMessage("ocsp.errorinitialize"); 170 m_log.error(errMsg, e); 171 throw new ServletException (e); 172 } 173 } 174 175 178 private synchronized ICertificateStoreOnlyDataSessionLocal getStoreSessionOnlyData(){ 179 if(m_certStore == null){ 180 try { 181 ServiceLocator locator = ServiceLocator.getInstance(); 182 ICertificateStoreOnlyDataSessionLocalHome castorehome = 183 (ICertificateStoreOnlyDataSessionLocalHome)locator.getLocalHome(ICertificateStoreOnlyDataSessionLocalHome.COMP_NAME); 184 m_certStore = castorehome.create(); 185 }catch(Exception e){ 186 throw new EJBException (e); 187 } 188 } 189 return m_certStore; 190 } 191 192 private X509Certificate [] getCertificateChain(X509Certificate cert, Admin adm) { 193 RevokedCertInfo revokedInfo = isRevoked(adm, cert.getIssuerDN().getName(), 194 cert.getSerialNumber()); 195 String wMsg = intres.getLocalizedMessage("ocsp.signcertnotindb", cert.getSerialNumber(), cert.getIssuerDN()); 196 if ( revokedInfo==null ) { 197 m_log.warn(wMsg); 198 return null; 199 } 200 if ( revokedInfo.getReason()!=RevokedCertInfo.NOT_REVOKED ) { 201 wMsg = intres.getLocalizedMessage("ocsp.signcertrevoked", cert.getSerialNumber(), cert.getIssuerDN()); 202 m_log.warn(wMsg); 203 return null; 204 } 205 X509Certificate chain[] = null; 206 final List list = new ArrayList (); 207 X509Certificate current = cert; 208 while( true ) { 209 list.add(current); 210 if ( current.getIssuerX500Principal().equals(current.getSubjectX500Principal()) ) { 211 chain = (X509Certificate [])list.toArray(new X509Certificate [0]); 212 break; 213 } 214 Iterator j = m_cacerts.iterator(); 215 boolean isNotFound = true; 216 while( isNotFound && j.hasNext() ) { 217 X509Certificate target = (X509Certificate )j.next(); 218 if (m_log.isDebugEnabled()) { 219 m_log.debug( "current issuer '" + current.getIssuerX500Principal() + 220 "'. target subject: '" + target.getSubjectX500Principal() + "'."); 221 } 222 if ( current.getIssuerX500Principal().equals(target.getSubjectX500Principal()) ) { 223 current = target; 224 isNotFound = false; 225 } 226 } 227 if ( isNotFound ) 228 break; 229 } 230 if ( chain==null ) { 231 wMsg = intres.getLocalizedMessage("ocsp.signcerthasnochain", cert.getSerialNumber(), cert.getIssuerDN()); 232 m_log.warn(wMsg); 233 } 234 return chain; 235 } 236 private boolean loadFromKeyStore(Admin adm, String fileName) { 237 final Enumeration eAlias; 238 final KeyStore keyStore; 239 try { 240 KeyStore tmpKeyStore; 241 try { 242 tmpKeyStore = KeyStore.getInstance("JKS"); 243 tmpKeyStore.load(new FileInputStream (fileName), mStorePassword); 244 } catch( IOException e ) { 245 tmpKeyStore = KeyStore.getInstance("PKCS12", "BC"); 246 tmpKeyStore.load(new FileInputStream (fileName), mStorePassword); 247 } 248 keyStore = tmpKeyStore; 249 eAlias = keyStore.aliases(); 250 } catch( Exception e ) { 251 m_log.debug("Unable to load key file "+fileName+". Exception: "+e.getMessage()); 252 return false; 253 } 254 while( eAlias.hasMoreElements() ) { 255 final String alias = (String )eAlias.nextElement(); 256 try { 257 final PrivateKey key = (PrivateKey )keyStore.getKey(alias, mKeyPassword); 258 final X509Certificate cert = (X509Certificate )keyStore.getCertificate(alias); 259 if ( key!=null && cert!=null ) 260 putSignEntity(new PrivateKeyFactorySW(key), cert, adm, "BC"); 261 } catch (Exception e) { 262 String errMsg = intres.getLocalizedMessage("ocsp.errorgetalias", alias, fileName); 263 m_log.error(errMsg, e); 264 } 265 } 266 return true; 267 } 268 private boolean putSignEntity( PrivateKeyFactory keyFactory, X509Certificate cert, Admin adm, String providerName ) { 269 if ( keyFactory!=null && cert!=null ) { 270 X509Certificate [] chain = getCertificateChain(cert, adm); 271 if ( chain!=null ) { 272 int caid = getCaid(chain[1]); 273 m_log.debug("CA with ID "+caid+" now has a OCSP signing key."); 274 SigningEntity oldSigningEntity = (SigningEntity)mSignEntity.get(new Integer (caid)); 275 if ( oldSigningEntity!=null && !oldSigningEntity.getCertificateChain().equals(chain) ) { 276 String wMsg = intres.getLocalizedMessage("ocsp.newsigningkey", chain[1].getSubjectDN(), chain[0].getSubjectDN()); 277 m_log.warn(wMsg); 278 } 279 mSignEntity.put( new Integer (caid), new SigningEntity(chain, keyFactory, providerName) ); 280 } 281 return true; 282 } 283 return false; 284 } 285 public String healtCheck() { 286 StringWriter sw = new StringWriter (); 287 PrintWriter pw = new PrintWriter (sw); 288 try { 289 loadCertificates(); 290 Iterator i = mSignEntity.values().iterator(); 291 while ( i.hasNext() ) { 292 SigningEntity signingEntity = (SigningEntity)i.next(); 293 if ( !signingEntity.isOK() ) { 294 pw.println(); 295 String errMsg = intres.getLocalizedMessage("ocsp.errorocspkeynotusable", signingEntity.getCertificateChain()[1].getSubjectDN(), signingEntity.getCertificateChain()[0].getSerialNumber().toString(16)); 296 pw.print(errMsg); 297 m_log.error(errMsg); 298 } 299 } 300 } catch (Exception e) { 301 String errMsg = intres.getLocalizedMessage("ocsp.errorloadsigningcerts"); 302 m_log.error(errMsg, e); 303 pw.print(errMsg + ": "+e.getMessage()); 304 } 305 pw.flush(); 306 return sw.toString(); 307 } 308 interface PrivateKeyFactory { 309 PrivateKey getKey() throws Exception ; 310 311 boolean isOK(); 312 } 313 private class PrivateKeyFactorySW implements PrivateKeyFactory { 314 final private PrivateKey privateKey; 315 PrivateKeyFactorySW( PrivateKey key) { 316 privateKey = key; 317 } 318 public PrivateKey getKey() throws Exception { 319 return privateKey; 320 } 321 public boolean isOK() { 322 return privateKey!=null; 324 } 325 } 326 private class PrivateKeyFactoryHW implements PrivateKeyFactory { 327 final private RSAPublicKey publicKey; 328 PrivateKeyFactoryHW( RSAPublicKey key) { 329 publicKey = key; 330 } 331 public PrivateKey getKey() throws Exception { 332 return mHardTokenObject.getPrivateKey(publicKey); 333 } 334 public boolean isOK() { 335 return mHardTokenObject.isOK(publicKey); 336 } 337 } 338 private boolean putSignEntityHW( Object obj, Admin adm ) { 339 if ( obj!=null && obj instanceof X509Certificate ) { 340 X509Certificate cert = (X509Certificate )obj; 341 PrivateKeyFactory keyFactory = new PrivateKeyFactoryHW((RSAPublicKey )cert.getPublicKey()); 342 putSignEntity( keyFactory, cert, adm, "PrimeKey" ); 343 m_log.debug("HW key added. Serial number: "+cert.getSerialNumber().toString(0x10)); 344 return true; 345 } else 346 return false; 347 } 348 private void loadFromKeyCards(Admin adm, String fileName) { 349 final CertificateFactory cf; 350 try { 351 cf = CertificateFactory.getInstance("X.509"); 352 } catch (java.security.cert.CertificateException e) { 353 throw new Error (e); 354 } 355 String fileType = null; 356 try { final Collection c = cf.generateCertificates(new FileInputStream (fileName)); 358 if ( c!=null && !c.isEmpty() ) { 359 Iterator i = c.iterator(); 360 while (i.hasNext()) { 361 if ( putSignEntityHW(i.next(), adm) ) 362 fileType = "PKCS#7"; 363 } 364 } 365 } catch( Exception e) { 366 } 367 if ( fileType==null ) { 368 try { BufferedInputStream bis = new BufferedInputStream (new FileInputStream (fileName)); 370 while (bis.available() > 0) { 371 if ( putSignEntityHW(cf.generateCertificate(bis), adm) ) 372 fileType="PEM"; 373 } 374 } catch(Exception e){ 375 } 376 } 377 if ( fileType!=null ) 378 m_log.debug("Certificate(s) found in file "+fileName+" of "+fileType+"."); 379 else 380 m_log.debug("File "+fileName+" has no cert."); 381 } 382 protected void loadPrivateKeys(Admin adm) throws ServletException , IOException { 383 mSignEntity.clear(); 384 File dir = new File (mKeystoreDirectoryName); 385 if ( dir==null || dir.isDirectory()==false ) 386 throw new ServletException (dir.getCanonicalPath() + " is not a directory."); 387 File files[] = dir.listFiles(); 388 if ( files==null || files.length==0 ) 389 throw new ServletException ("No files in soft key directory: " + dir.getCanonicalPath()); 390 for ( int i=0; i<files.length; i++ ) { 391 final String fileName = files[i].getCanonicalPath(); 392 if ( !loadFromKeyStore(adm, fileName) ) 393 loadFromKeyCards(adm, fileName); 394 } 395 if ( mSignEntity.size()==0 ) 396 throw new ServletException ("No valid keys in directory " + dir.getCanonicalPath()); 397 } 398 private class SigningEntity { 399 final private X509Certificate mChain[]; 400 final private PrivateKeyFactory mKeyFactory; 401 final private String providerName; 402 SigningEntity(X509Certificate c[], PrivateKeyFactory f, String sName) { 403 mChain = c; 404 mKeyFactory = f; 405 providerName = sName; 406 } 407 OCSPCAServiceResponse sign( OCSPCAServiceRequest request) throws ExtendedCAServiceRequestException { 408 X509Certificate signerCert = mChain[0]; 409 final String sigAlgs = request.getSigAlg(); 410 PublicKey pk = signerCert.getPublicKey(); 411 String sigAlg = OCSPUtil.getSigningAlgFromAlgSelection(sigAlgs, pk); 412 m_log.debug("Signing algorithm: "+sigAlg); 413 final X509Certificate [] chain = request.includeChain() ? mChain : null; 414 try { 415 BasicOCSPResp ocspresp = OCSPUtil.generateBasicOCSPResp(request, sigAlg, signerCert, mKeyFactory.getKey(), providerName, chain); 416 return new OCSPCAServiceResponse(ocspresp, chain == null ? null : Arrays.asList(chain)); 417 } catch (Exception e) { 418 throw new ExtendedCAServiceRequestException(e); 419 } 420 } 421 boolean isOK() { 422 try { 423 return mKeyFactory.isOK(); 424 } catch (Exception e) { 425 m_log.debug("Exception thrown when accessing the private key", e); 426 return false; 427 } 428 } 429 X509Certificate [] getCertificateChain() { 430 return mChain; 431 } 432 } 433 434 protected Collection findCertificatesByType(Admin adm, int type, String issuerDN) { 435 return getStoreSessionOnlyData().findCertificatesByType(adm, type, issuerDN); 436 } 437 438 protected Certificate findCertificateByIssuerAndSerno(Admin adm, String issuer, BigInteger serno) { 439 return getStoreSessionOnlyData().findCertificateByIssuerAndSerno(adm, issuer, serno); 440 } 441 442 protected OCSPCAServiceResponse extendedService(Admin adm, int caid, OCSPCAServiceRequest request) throws ExtendedCAServiceRequestException, 443 ExtendedCAServiceNotActiveException { 444 SigningEntity se =(SigningEntity)mSignEntity.get(new Integer (caid)); 445 if ( se!=null ) { 446 return se.sign(request); 447 } 448 throw new ExtendedCAServiceNotActiveException("No ocsp signing key for caid "+caid); 449 } 450 451 protected RevokedCertInfo isRevoked(Admin adm, String name, BigInteger serialNumber) { 452 return getStoreSessionOnlyData().isRevoked(adm, name, serialNumber); 453 } 454 } 455 | Popular Tags |