Package org.ejbca.core.protocol.ocsp

Source Code of org.ejbca.core.protocol.ocsp.OCSPUtil

/*************************************************************************
*                                                                       *
*  EJBCA: The OpenSource Certificate Authority                          *
*                                                                       *
*  This software is free software; you can redistribute it and/or       *
*  modify it under the terms of the GNU Lesser General Public           *
*  License as published by the Free Software Foundation; either         *
*  version 2.1 of the License, or any later version.                    *
*                                                                       *
*  See terms of license at gnu.org.                                     *
*                                                                       *
*************************************************************************/
package org.ejbca.core.protocol.ocsp;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.ocsp.BasicOCSPResp;
import org.bouncycastle.ocsp.BasicOCSPRespGenerator;
import org.bouncycastle.ocsp.OCSPException;
import org.bouncycastle.ocsp.OCSPReq;
import org.bouncycastle.ocsp.RespID;
import org.ejbca.config.OcspConfiguration;
import org.ejbca.core.model.InternalResources;
import org.ejbca.core.model.ca.NotSupportedException;
import org.ejbca.core.model.ca.SignRequestException;
import org.ejbca.core.model.ca.SignRequestSignatureException;
import org.ejbca.core.model.ca.caadmin.extendedcaservices.ExtendedCAServiceRequestException;
import org.ejbca.core.model.ca.caadmin.extendedcaservices.IllegalExtendedCAServiceRequestException;
import org.ejbca.core.model.ca.caadmin.extendedcaservices.OCSPCAServiceRequest;
import org.ejbca.core.model.ca.caadmin.extendedcaservices.OCSPCAServiceResponse;
import org.ejbca.core.model.util.AlgorithmTools;
import org.ejbca.core.protocol.certificatestore.HashID;
import org.ejbca.core.protocol.certificatestore.ICertificateCache;
import org.ejbca.util.CertTools;
import org.ejbca.util.FileTools;

/** Class with common methods used by both Internal and External OCSP responders
*
* @author tomas
* @version $Id: OCSPUtil.java 11154 2011-01-12 09:56:23Z jeklund $
*
*/
public class OCSPUtil {

  private static final Logger m_log = Logger.getLogger(OCSPUtil.class);
    /** Internal localization of logs and errors */
    private static final InternalResources intres = InternalResources.getInstance();


    public static BasicOCSPRespGenerator createOCSPResponse(OCSPReq req, X509Certificate respondercert, int respIdType) throws OCSPException, NotSupportedException {
        if (null == req) {
            throw new IllegalArgumentException();
        }
        BasicOCSPRespGenerator res = null;
        if (respIdType == OcspConfiguration.RESPONDERIDTYPE_NAME) {
          res = new BasicOCSPRespGenerator(new RespID(respondercert.getSubjectX500Principal()));
        } else {
          res = new BasicOCSPRespGenerator(respondercert.getPublicKey());
        }
        X509Extensions reqexts = req.getRequestExtensions();
        if (reqexts != null) {
          X509Extension ext = reqexts.getExtension(OCSPObjectIdentifiers.id_pkix_ocsp_response);
            if (null != ext) {
                //m_log.debug("Found extension AcceptableResponses");
                ASN1OctetString oct = ext.getValue();
                try {
                    ASN1Sequence seq = ASN1Sequence.getInstance(new ASN1InputStream(new ByteArrayInputStream(oct.getOctets())).readObject());
                    Enumeration en = seq.getObjects();
                    boolean supportsResponseType = false;
                    while (en.hasMoreElements()) {
                        DERObjectIdentifier oid = (DERObjectIdentifier) en.nextElement();
                        //m_log.debug("Found oid: "+oid.getId());
                        if (oid.equals(OCSPObjectIdentifiers.id_pkix_ocsp_basic)) {
                            // This is the response type we support, so we are happy! Break the loop.
                            supportsResponseType = true;
                            m_log.debug("Response type supported: " + oid.getId());
                            continue;
                        }
                    }
                    if (!supportsResponseType) {
                        throw new NotSupportedException("Required response type not supported, this responder only supports id-pkix-ocsp-basic.");
                    }
                } catch (IOException e) {
                }
            }
        }
        return res;
    }
   
    public static BasicOCSPResp generateBasicOCSPResp(OCSPCAServiceRequest serviceReq, String sigAlg, X509Certificate signerCert, PrivateKey signerKey, String provider, X509Certificate[] chain, int respIdType)
    throws NotSupportedException, OCSPException, NoSuchProviderException, IllegalArgumentException {
      BasicOCSPResp returnval = null;
      BasicOCSPRespGenerator basicRes = null;
      basicRes = OCSPUtil.createOCSPResponse(serviceReq.getOCSPrequest(), signerCert, respIdType);
      ArrayList responses = serviceReq.getResponseList();
      if (responses != null) {
        Iterator iter = responses.iterator();
        while (iter.hasNext()) {
            OCSPResponseItem item = (OCSPResponseItem)iter.next();
              basicRes.addResponse(item.getCertID(), item.getCertStatus(), item.getThisUpdate(), item.getNextUpdate(), null);         
        }
      }
      X509Extensions exts = serviceReq.getExtensions();
      if (exts != null) {
        Enumeration oids = exts.oids();
        if (oids.hasMoreElements()) {
            basicRes.setResponseExtensions(exts);         
        }
      }

      returnval = basicRes.generate(sigAlg, signerKey, chain, new Date(), provider );
      if (m_log.isDebugEnabled()) {
        m_log.debug("Signing OCSP response with OCSP signer cert: " + signerCert.getSubjectDN().getName());
        RespID respId = null;
        if (respIdType == OcspConfiguration.RESPONDERIDTYPE_NAME) {
        respId = new RespID(signerCert.getSubjectX500Principal());         
        } else {
        respId = new RespID(signerCert.getPublicKey());         
        }
        if (!returnval.getResponderId().equals(respId)) {
          m_log.error("Response responderId does not match signer certificate responderId!");
        }
        boolean verify = returnval.verify(signerCert.getPublicKey(), "BC");
        if (verify) {
            m_log.debug("The OCSP response is verifying.");
        } else {
          m_log.error("The response is NOT verifying!");
        }
      }
      return returnval;
    }

    /**
     * Checks if a certificate is valid
     * Does also print a WARN if the certificate is about to expire.
     * @param signerCert the certificate to be tested
     * @return true if the certificate is valid
     */
    public static boolean isCertificateValid( X509Certificate signerCert ) {
      try {
        signerCert.checkValidity();
      } catch (CertificateExpiredException e) {
        m_log.error(intres.getLocalizedMessage("ocsp.errorcerthasexpired",
                                               signerCert.getSerialNumber(), signerCert.getIssuerDN()));
        return false;
      } catch (CertificateNotYetValidException e) {
        m_log.error(intres.getLocalizedMessage("ocsp.errornotyetvalid",
                                               signerCert.getSerialNumber(), signerCert.getIssuerDN()));
        return false;
      }
      final long warnBeforeExpirationTime = OcspConfiguration.getWarningBeforeExpirationTime();
      if ( warnBeforeExpirationTime<1 ) {
        return true;
      }
      final Date warnDate = new Date(new Date().getTime()+warnBeforeExpirationTime);
      try {
        signerCert.checkValidity( warnDate );
      } catch (CertificateExpiredException e) {
        m_log.warn(intres.getLocalizedMessage("ocsp.warncertwillexpire", signerCert.getSerialNumber(),
                                              signerCert.getIssuerDN(), signerCert.getNotAfter()));
      } catch (CertificateNotYetValidException e) {
        throw new Error("This should never happen.", e);
      }
    if ( !m_log.isDebugEnabled() ) {
      return true;
    }
    m_log.debug("Time for \"certificate will soon expire\" not yet reached. You will be warned after: "+
                new Date(signerCert.getNotAfter().getTime()-warnBeforeExpirationTime));
      return true;
    }
    /**
     * Method generates an ExtendedCAServiceResponse which is a OCSPCAServiceResponse wrapping the BasicOCSPRespfor usage
     * internally in EJBCA.
     * 
     * @param ocspServiceReq OCSPCAServiceRequest
     * @param privKey PrivateKey used to sign the OCSP response
     * @param providerName Provider for the private key, can be on HSM
     * @param certChain Certificate chain for signing the OCSP response
     * @return OCSPCAServiceResponse
     * @throws IllegalExtendedCAServiceRequestException
     * @throws ExtendedCAServiceRequestException
     */
    public static OCSPCAServiceResponse createOCSPCAServiceResponse(OCSPCAServiceRequest ocspServiceReq, PrivateKey privKey, String providerName, X509Certificate[] certChain)
    throws IllegalExtendedCAServiceRequestException, ExtendedCAServiceRequestException {
      final X509Certificate signerCert = certChain[0];
      final String sigAlgs = ocspServiceReq.getSigAlg();
      final PublicKey pk = signerCert.getPublicKey();
      final String sigAlg = OCSPUtil.getSigningAlgFromAlgSelection(sigAlgs, pk);
      m_log.debug("Signing algorithm: "+sigAlg);
      final boolean includeChain = ocspServiceReq.includeChain();
      m_log.debug("Include chain: "+includeChain);
      final X509Certificate[] chain;
      if (includeChain) {
        chain = certChain;
      } else {
        chain = new X509Certificate[1];
        chain[0] = signerCert;
      }
      try {
        final int respIdType = ocspServiceReq.getRespIdType();
        final BasicOCSPResp ocspresp = OCSPUtil.generateBasicOCSPResp(ocspServiceReq, sigAlg, signerCert, privKey, providerName, chain, respIdType);
        final OCSPCAServiceResponse result = new OCSPCAServiceResponse(ocspresp, Arrays.asList(chain));
        isCertificateValid(signerCert);
        return result;
      } catch (OCSPException ocspe) {
        throw new ExtendedCAServiceRequestException(ocspe);
      } catch (NoSuchProviderException nspe) {
        throw new ExtendedCAServiceRequestException(nspe);           
      } catch (NotSupportedException e) {
        m_log.info("OCSP Request type not supported: ", e);
        throw new IllegalExtendedCAServiceRequestException(e);
      } catch (IllegalArgumentException e) {
        m_log.error("IllegalArgumentException: ", e);
        throw new IllegalExtendedCAServiceRequestException(e);
      }
    } // createOCSPCAServiceResponse


    /**
     * Returns a signing algorithm to use selecting from a list of possible algorithms.
     *
     * @param sigalgs the list of possible algorithms, ;-separated. Example "SHA1WithRSA;SHA1WithECDSA".
     * @param pk public key of signer, so we can choose between RSA, DSA and ECDSA algorithms
     * @return A single algorithm to use Example: SHA1WithRSA, SHA1WithDSA or SHA1WithECDSA
     */
    public static String getSigningAlgFromAlgSelection(String sigalgs, PublicKey pk) {
      String sigAlg = null;
      String[] algs = StringUtils.split(sigalgs, ';');
      for(int i = 0; i < algs.length; i++) {
        if ( AlgorithmTools.isCompatibleSigAlg(pk, algs[i]) ) {
          sigAlg = algs[i];
          break;
        }
      }
        m_log.debug("Using signature algorithm for response: "+sigAlg);
        return sigAlg;
    }

    /** Checks the signature on an OCSP request and checks that it is signed by an allowed CA.
     * Does not check for revocation of the signer certificate
     *
     * @param clientRemoteAddr The ip address or hostname of the remote client that sent the request, can be null.
     * @param req The signed OCSPReq
     * @param cacerts a CertificateCache of Certificates, the authorized CA-certificates. The signer certificate must be issued by one of these.
     * @return X509Certificate which is the certificate that signed the OCSP request
     * @throws SignRequestSignatureException if signature verification fail, or if the signing certificate is not authorized
     * @throws SignRequestException if there is no signature on the OCSPReq
     * @throws OCSPException if the request can not be parsed to retrieve certificates
     * @throws NoSuchProviderException if the BC provider is not installed
     * @throws CertificateException if the certificate can not be parsed
     * @throws NoSuchAlgorithmException if the certificate contains an unsupported algorithm
     * @throws InvalidKeyException if the certificate, or CA key is invalid
     */
    public static X509Certificate checkRequestSignature(String clientRemoteAddr, OCSPReq req, ICertificateCache cacerts)
    throws SignRequestException, OCSPException,
    NoSuchProviderException, CertificateException,
    NoSuchAlgorithmException, InvalidKeyException,
    SignRequestSignatureException {
     
      X509Certificate signercert = null;
     
      if (!req.isSigned()) {
        String infoMsg = intres.getLocalizedMessage("ocsp.errorunsignedreq", clientRemoteAddr);
        m_log.info(infoMsg);
        throw new SignRequestException(infoMsg);
      }
      // Get all certificates embedded in the request (probably a certificate chain)
      X509Certificate[] certs = req.getCerts("BC");
      // Set, as a try, the signer to be the first certificate, so we have a name to log...
      String signer = null;
      if (certs.length > 0) {
        signer = CertTools.getSubjectDN(certs[0]);
      }
     
        // We must find a cert to verify the signature with...
      boolean verifyOK = false;
      for (int i = 0; i < certs.length; i++) {
        if (req.verify(certs[i].getPublicKey(), "BC") == true) {
          signercert = certs[i];
            signer = CertTools.getSubjectDN(signercert);
            Date now = new Date();
          String signerissuer = CertTools.getIssuerDN(signercert);
          String infoMsg = intres.getLocalizedMessage("ocsp.infosigner", signer);
          m_log.info(infoMsg);
          verifyOK = true;
          // Also check that the signer certificate can be verified by one of the CA-certificates
          // that we answer for
          X509Certificate signerca = cacerts.findLatestBySubjectDN(HashID.getFromIssuerDN(certs[i]));
          String subject = signer;
          String issuer = signerissuer;
          if (signerca != null) {
            try {
              signercert.verify(signerca.getPublicKey());
                  if (m_log.isDebugEnabled()) {
                      m_log.debug("Checking validity. Now: "+now+", signerNotAfter: "+signercert.getNotAfter());             
                  }
                  CertTools.checkValidity(signercert, now);
                  // Move the error message string to the CA cert
                subject = CertTools.getSubjectDN(signerca);
                issuer = CertTools.getIssuerDN(signerca);
                  CertTools.checkValidity(signerca, now);
            } catch (SignatureException e) {
              infoMsg = intres.getLocalizedMessage("ocsp.infosigner.invalidcertsignature", subject, issuer, e.getMessage());
              m_log.info(infoMsg);
              verifyOK = false;
            } catch (InvalidKeyException e) {
              infoMsg = intres.getLocalizedMessage("ocsp.infosigner.invalidcertsignature", subject, issuer, e.getMessage());
              m_log.info(infoMsg);
              verifyOK = false;
            } catch (CertificateNotYetValidException e) {
              infoMsg = intres.getLocalizedMessage("ocsp.infosigner.certnotyetvalid", subject, issuer, e.getMessage());
              m_log.info(infoMsg);
              verifyOK = false;
            } catch (CertificateExpiredException e) {
              infoMsg = intres.getLocalizedMessage("ocsp.infosigner.certexpired", subject, issuer, e.getMessage());
              m_log.info(infoMsg);
              verifyOK = false;
            }                             
          } else {
            infoMsg = intres.getLocalizedMessage("ocsp.infosigner.nocacert", signer, signerissuer);
            m_log.info(infoMsg);
            verifyOK = false;
          }
          break;
        }
      }
      if (!verifyOK) {
        String errMsg = intres.getLocalizedMessage("ocsp.errorinvalidsignature", signer);
        m_log.info(errMsg);
        throw new SignRequestSignatureException(errMsg);
      }
     
      return signercert;
    }

    /** returns an HashTable of responseExtensions to be added to the BacisOCSPResponseGenerator with
     * <code>
     * X509Extensions exts = new X509Extensions(table);
     * basicRes.setResponseExtensions(responseExtensions);
     * </code>
     *
     * @param req OCSPReq
     * @return a Hashtable, can be empty nut not null
     */
    public static Hashtable getStandardResponseExtensions(OCSPReq req) {
        X509Extensions reqexts = req.getRequestExtensions();
        Hashtable table = new Hashtable();
        if (reqexts != null) {
          // Table of extensions to include in the response
            X509Extension ext = reqexts.getExtension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce);
            if (null != ext) {
                //m_log.debug("Found extension Nonce");
                table.put(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, ext);
            }
        }
      return table;
    }
   
    public static Hashtable getCertificatesFromDirectory(String certificateDir) throws IOException {
      // read all files from trustDir, expect that they are PEM formatted certificates
      CertTools.installBCProvider();
      File dir = new File(certificateDir);
      Hashtable trustedCerts  = new Hashtable();
      if (dir == null || dir.isDirectory() == false) {
        m_log.error(dir.getCanonicalPath()+ " is not a directory.");
        throw new IllegalArgumentException(dir.getCanonicalPath()+ " is not a directory.");               
      }
      File files[] = dir.listFiles();
      if (files == null || files.length == 0) {
        String errMsg = intres.getLocalizedMessage("ocsp.errornotrustfiles", dir.getCanonicalPath());
        m_log.error(errMsg);               
      }
      for ( int i=0; i<files.length; i++ ) {
        final String fileName = files[i].getCanonicalPath();
        // Read the file, don't stop completely if one file has errors in it
        try {
          byte[] bytes = FileTools.getBytesFromPEM(FileTools.readFiletoBuffer(fileName),
              CertTools.BEGIN_CERTIFICATE, CertTools.END_CERTIFICATE);
          X509Certificate  cert = (X509Certificate) CertTools.getCertfromByteArray(bytes);
          String key =  cert.getIssuerDN()+";"+cert.getSerialNumber().toString(16);
          trustedCerts.put(key,cert);
        } catch (CertificateException e) {
          String errMsg = intres.getLocalizedMessage("ocsp.errorreadingfile", fileName, "trustDir", e.getMessage());
          m_log.error(errMsg, e);
        } catch (IOException e) {
          String errMsg = intres.getLocalizedMessage("ocsp.errorreadingfile", fileName, "trustDir", e.getMessage());
          m_log.error(errMsg, e);
        }
      }
      return trustedCerts;
    }
   
    /**
     * Checks to see if a certificate is in a list of certificate.
     * Comparison is made on SerialNumber
     * @param cert the certificate to look for
     * @param trustedCerts the list (Hashtable) to look in
     * @return true if cert is in trustedCerts, false otherwise
     */
    public static boolean checkCertInList(X509Certificate cert, Hashtable trustedCerts) {
      //String key = CertTools.getIssuerDN(cert)+";"+cert.getSerialNumber().toString(16);
      String key =  cert.getIssuerDN()+";"+cert.getSerialNumber().toString(16);
      Object found = trustedCerts.get(key);
        if (found != null) {
            return true;
        }
        return false;
    }
}
TOP

Related Classes of org.ejbca.core.protocol.ocsp.OCSPUtil

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.