OCSPValidator.java

  1. /*
  2.  * GovWay - A customizable API Gateway
  3.  * https://govway.org
  4.  *
  5.  * Copyright (c) 2005-2025 Link.it srl (https://link.it).
  6.  *
  7.  * This program is free software: you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License version 3, as published by
  9.  * the Free Software Foundation.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  *
  19.  */
  20. package org.openspcoop2.utils.certificate.ocsp;

  21. import java.math.BigInteger;
  22. import java.security.cert.CRLReason;
  23. import java.security.cert.CertificateExpiredException;
  24. import java.security.cert.CertificateNotYetValidException;
  25. import java.security.cert.X509Certificate;
  26. import java.util.ArrayList;
  27. import java.util.Arrays;
  28. import java.util.Date;
  29. import java.util.HashMap;
  30. import java.util.List;
  31. import java.util.Map;

  32. import org.apache.commons.lang.StringUtils;
  33. import org.bouncycastle.asn1.DEROctetString;
  34. import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
  35. import org.bouncycastle.asn1.ocsp.ResponderID;
  36. import org.bouncycastle.asn1.x500.X500Name;
  37. import org.bouncycastle.asn1.x509.Extension;
  38. import org.bouncycastle.asn1.x509.Extensions;
  39. import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
  40. import org.bouncycastle.cert.X509CertificateHolder;
  41. import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
  42. import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
  43. import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
  44. import org.bouncycastle.cert.ocsp.BasicOCSPResp;
  45. import org.bouncycastle.cert.ocsp.CertificateID;
  46. import org.bouncycastle.cert.ocsp.OCSPReq;
  47. import org.bouncycastle.cert.ocsp.OCSPReqBuilder;
  48. import org.bouncycastle.cert.ocsp.OCSPResp;
  49. import org.bouncycastle.cert.ocsp.RevokedStatus;
  50. import org.bouncycastle.cert.ocsp.SingleResp;
  51. import org.bouncycastle.cert.ocsp.UnknownStatus;
  52. import org.bouncycastle.cert.ocsp.jcajce.JcaCertificateID;
  53. import org.bouncycastle.operator.ContentVerifierProvider;
  54. import org.bouncycastle.operator.DigestCalculator;
  55. import org.bouncycastle.operator.DigestCalculatorProvider;
  56. import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
  57. import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
  58. import org.openspcoop2.utils.LoggerBuffer;
  59. import org.openspcoop2.utils.Utilities;
  60. import org.openspcoop2.utils.UtilsException;
  61. import org.openspcoop2.utils.UtilsMultiException;
  62. import org.openspcoop2.utils.certificate.CRLDistributionPoint;
  63. import org.openspcoop2.utils.certificate.CertificateInfo;
  64. import org.openspcoop2.utils.certificate.ExtendedKeyUsage;
  65. import org.openspcoop2.utils.certificate.KeyStore;
  66. import org.openspcoop2.utils.date.DateManager;
  67. import org.openspcoop2.utils.date.DateUtils;
  68. import org.openspcoop2.utils.io.Base64Utilities;
  69. import org.openspcoop2.utils.random.RandomGenerator;
  70. import org.openspcoop2.utils.random.SecureRandomAlgorithm;
  71. import org.openspcoop2.utils.transport.TransportUtils;
  72. import org.openspcoop2.utils.transport.http.HttpConstants;
  73. import org.openspcoop2.utils.transport.http.HttpRequest;
  74. import org.openspcoop2.utils.transport.http.HttpRequestMethod;
  75. import org.openspcoop2.utils.transport.http.HttpResponse;
  76. import org.openspcoop2.utils.transport.http.HttpUtilities;
  77. import org.slf4j.Logger;

  78. /**
  79.  * OCSPValidator
  80.  *
  81.  * @author Poli Andrea (apoli@link.it)
  82.  * @author $Author$
  83.  * @version $Rev$, $Date$
  84.  */
  85. public class OCSPValidator {

  86.     private OCSPValidator() {}
  87.    
  88.     public static CertificateStatus check(Logger log, OCSPRequestParams params) throws UtilsException {
  89.         LoggerBuffer lb = new LoggerBuffer();
  90.         lb.setLogDebug(log);
  91.         lb.setLogError(log);
  92.         return check(lb, params, null);
  93.     }
  94.     public static CertificateStatus check(Logger log, OCSPRequestParams params, String crlInput) throws UtilsException {
  95.         LoggerBuffer lb = new LoggerBuffer();
  96.         lb.setLogDebug(log);
  97.         lb.setLogError(log);
  98.         return check(lb, params, crlInput);
  99.     }
  100.     public static CertificateStatus check(LoggerBuffer log, OCSPRequestParams params) throws UtilsException {
  101.         return check(log, params, null);
  102.     }
  103.     public static CertificateStatus check(LoggerBuffer log, OCSPRequestParams params, String crlInput) throws UtilsException {
  104.    
  105.         if(params==null) {
  106.             throw new UtilsException("Params is null");
  107.         }
  108.         if(params.getCertificate()==null) {
  109.             throw new UtilsException("Certificate not provided");
  110.         }
  111.         if(params.getConfig()==null) {
  112.             throw new UtilsException("OCSP config not provided");
  113.         }
  114.         if(params.isSelfSigned()) {
  115.             return CertificateStatus.SELF_SIGNED();
  116.         }

  117.         int indexLimit = 1000;
  118.         int index = 0;
  119.         OCSPRequestParams req = params;
  120.         CertificateStatus principalStatus = null;
  121.         while(index<indexLimit) {

  122.             CertificateStatus status = checkEngine(log, req, crlInput);
  123.             if(principalStatus==null) {
  124.                 principalStatus = status;
  125.             }
  126.            
  127.             if(status.isREVOKED() || status.isEXPIRED() || status.isUNKNOWN()) {
  128.                 return status;
  129.             }
  130.             else {
  131.                 if(params.getConfig().isCertificateChainVerify()
  132.                         &&
  133.                         !req.isSelfSigned() // altrimenti sono arrivato alla ca radice
  134.                         ) {
  135.                     OCSPRequestParams newReq = OCSPRequestParams.build(log, req.getIssuerCertificate(), params.getConfigTrustStore(),
  136.                             params.getConfig(), params.getReader());
  137.                     req = newReq;
  138.                 }
  139.                 else {
  140.                     return principalStatus; // ritorno lo stato di verifica del certificato principale
  141.                 }
  142.             }          

  143.             index++;
  144.         }

  145.         throw new UtilsException("Certificate chain too big");
  146.     }

  147.     private static CertificateStatus checkEngine(LoggerBuffer log, OCSPRequestParams params, String crlInput) throws UtilsException {

  148.         if(params==null) {
  149.             throw new UtilsException("Params is null");
  150.         }

  151.         if(params.getCertificate()==null) {
  152.             throw new UtilsException("Certificate not provided");
  153.         }
  154.         if(params.getConfig()==null) {
  155.             throw new UtilsException("OCSP config not provided");
  156.         }

  157.         String prefixCert = "OCSP [certificate: "+params.getCertificate().getSubjectDN()+"] ";

  158.         CertificateInfo certificateInfo = new CertificateInfo(params.getCertificate(), "certificate");
  159.         log.debug(prefixCert+"issuer: "+params.getCertificate().getIssuerDN());
  160.         try {
  161.             log.debug(prefixCert+"CAissuer: "+(certificateInfo.getAuthorityInformationAccess()!=null ? certificateInfo.getAuthorityInformationAccess().getCAIssuers() : null));
  162.         }catch(Exception t) {
  163.             log.debug(prefixCert+"CAissuer: read error: "+t.getMessage(),t);
  164.         }
  165.         try {
  166.             log.debug(prefixCert+"OCSP: "+(certificateInfo.getAuthorityInformationAccess()!=null ? certificateInfo.getAuthorityInformationAccess().getOCSPs() : null));
  167.         }catch(Exception t) {
  168.             log.debug(prefixCert+"OCSP: read error: "+t.getMessage(),t);
  169.         }
  170.         try {
  171.             if(certificateInfo.getCRLDistributionPoints()!=null &&
  172.                     certificateInfo.getCRLDistributionPoints().getCRLDistributionPoints()!=null &&
  173.                     !certificateInfo.getCRLDistributionPoints().getCRLDistributionPoints().isEmpty()) {
  174.                 int indexCRL = 0;
  175.                 for (CRLDistributionPoint point : certificateInfo.getCRLDistributionPoints().getCRLDistributionPoints()) {
  176.                     log.debug(prefixCert+"CRL-"+indexCRL+"-Issuer: "+point.getCRLIssuers());
  177.                     log.debug(prefixCert+"CRL-"+indexCRL+": "+point.getDistributionPointNames());
  178.                     indexCRL++;
  179.                 }
  180.             }
  181.             else {
  182.                 log.debug(prefixCert+"CRL: null");
  183.             }
  184.         }catch(Exception t) {
  185.             log.debug(prefixCert+"CRL: read error: "+t.getMessage(),t);
  186.         }
  187.        
  188.         Date date = DateManager.getDate();

  189.         boolean isCA = false;
  190.         try {
  191.             isCA = certificateInfo.isCA();
  192.         }catch(Exception e) {
  193.             throw new UtilsException(e.getMessage(),e);
  194.         }
  195.        
  196.         // controllo validità temporale
  197.         try {
  198.             if(isCA) {
  199.                 if(params.getConfig().isCheckCAValidity()) {
  200.                     log.debug(prefixCert+"Check validity ..."); // la faccio sempre per avere l'errore puntuale
  201.                     certificateInfo.checkValid(date);
  202.                 }
  203.             }
  204.             else {
  205.                 if(params.getConfig().isCheckValidity()) {
  206.                     log.debug(prefixCert+"Check validity ..."); // la faccio sempre per avere l'errore puntuale
  207.                     certificateInfo.checkValid(date);      
  208.                 }
  209.             }
  210.         }catch(CertificateExpiredException t) {
  211.             return CertificateStatus.EXPIRED(prefixCert+t.getMessage(), certificateInfo.getNotAfter());
  212.         }catch(CertificateNotYetValidException t) {
  213.             return CertificateStatus.EXPIRED(prefixCert+t.getMessage(), certificateInfo.getNotBefore());
  214.         }catch(Exception t) {
  215.             return CertificateStatus.EXPIRED(prefixCert+t.getMessage(), certificateInfo.getNotAfter());
  216.         }
  217.        
  218.         if(certificateInfo.isSelfSigned()) {
  219.             log.debug(prefixCert+"Self signed");            
  220.             return CertificateStatus.SELF_SIGNED();
  221.         }
  222.        
  223.         if(params.getIssuerCertificate()==null) {
  224.             if(params.getConfig().isRejectsCertificateWithoutCA()) {
  225.                 throw new UtilsException(prefixCert+"IssuerCertificate not provided");
  226.             }
  227.             else {
  228.                 return CertificateStatus.ISSUER_NOT_FOUND();
  229.             }
  230.         }
  231.        
  232.         if(params.getConfig().isCrl()) {
  233.             // viene richiesta una validazione CRL alternativa al OCSP
  234.             return checkCRLEngine(log, params, crlInput, date, prefixCert);
  235.         }

  236.         if (params.getResponderURIs() == null || params.getResponderURIs().isEmpty()) {
  237.             boolean reject = true;
  238.             if(isCA) {
  239.                 reject = params.getConfig().isRejectsCAWithoutResponderUrl();
  240.             }
  241.             else {
  242.                 reject = params.getConfig().isRejectsCertificateWithoutResponderUrl();
  243.             }
  244.             if(reject) {
  245.                 throw new UtilsException(prefixCert+"At least one OCSP responder required");
  246.             }
  247.             else {
  248.                 if(isCA && params.getConfig().isCrlCaCheck()) {
  249.                     return checkCRLEngine(log, params, crlInput, date, prefixCert);
  250.                 }
  251.                 else {
  252.                     return CertificateStatus.OCSP_RESPONDER_NOT_FOUND();
  253.                 }
  254.             }
  255.         }

  256.         StringBuilder sbError = new StringBuilder();
  257.        
  258.         for (String responderURI : params.getResponderURIs()) {

  259.             String prefix =prefixCert+ "["+responderURI+"] ";

  260.             OCSPResponseCode responseCode = null;
  261.             UtilsException error = null;
  262.            
  263.             OCSPRequestSigned ocspRequest = null;
  264.             try {
  265.                 log.debug(prefix+"Build request ...");
  266.                 ocspRequest = buildOCSPReq(log, prefix, params);
  267.             }catch(Exception t) {
  268.                 responseCode = OCSPResponseCode.OCSP_BUILD_REQUEST_FAILED;
  269.                 error = new UtilsException(prefixCert+"(url: "+responderURI+"): "+t.getMessage(),t);
  270.                 log.error(prefix+"costruzione richiesta fallita: "+t.getMessage(),t);
  271.                 /** gestito sotto con gli stati throw error; */
  272.             }
  273.                
  274.             BasicOCSPResp ocspResp = null;
  275.             if(ocspRequest!=null) {
  276.                 try {
  277.                     log.debug(prefix+"Invoke ocsp ...");
  278.                     OCSPResp ocspResponse = invokeOCSP(log, ocspRequest, responderURI, params);
  279.    
  280.                     log.debug(prefix+"Analyze response ...");
  281.                     responseCode = OCSPResponseCode.toOCSPResponseCode(ocspResponse.getStatus());
  282.    
  283.                     if(OCSPResponseCode.SUCCESSFUL.equals(responseCode)) {
  284.                         if(ocspResponse.getResponseObject()==null) {
  285.                             throw new UtilsException("OCSP response object not found");
  286.                         }
  287.                         else if (ocspResponse.getResponseObject() instanceof BasicOCSPResp) {
  288.                             ocspResp = (BasicOCSPResp) ocspResponse.getResponseObject();
  289.                         } else {
  290.                             throw new UtilsException("Invalid or unknown OCSP response");
  291.                         }
  292.                     }
  293.                    
  294.                 }catch(Exception t) {
  295.                     responseCode = OCSPResponseCode.OCSP_INVOKE_FAILED;
  296.                     error = new UtilsException(prefixCert+"(url: "+responderURI+"): "+t.getMessage(),t);
  297.                     log.error(prefix+"invocazione servizio ocsp fallita: "+t.getMessage(),t);
  298.                     /** gestito sotto con gli stati throw error; */
  299.                 }
  300.             }
  301.                
  302.             try {
  303.                 if(OCSPResponseCode.SUCCESSFUL.equals(responseCode) && ocspResp!=null) {
  304.                     return analyzeResponse(log, prefix, params, date, ocspRequest, ocspResp);
  305.                 }
  306.                 else {
  307.                    
  308.                     String msgFailed = responseCode.getMessage();
  309.                     if(error!=null) {
  310.                         msgFailed = msgFailed+"; "+error.getMessage();
  311.                     }
  312.                     String exceptionMessage = "OCSP response error ("+responseCode.getCode()+" - "+responseCode.name()+"): "+msgFailed;
  313.                    
  314.                     if(params.getConfig().getResponderBreakStatus()==null ||
  315.                             params.getConfig().getResponderBreakStatus().isEmpty() ||
  316.                             params.getConfig().getResponderBreakStatus().contains(responseCode)) {
  317.                         throw new UtilsException(exceptionMessage);
  318.                     }
  319.                     else {
  320.                         // continue verso il prossimo responder url
  321.                         // salvo la prima risposta, e se dopo aver provato tutte le url non ho trovato una risposta valido ritorna la prima
  322.                         if(sbError.length()>0) {
  323.                             sbError.append("\n");
  324.                         }
  325.                         sbError.append("[").append(responderURI).append("] ");
  326.                         sbError.append(exceptionMessage);
  327.                     }
  328.                 }

  329.             }catch(Exception t) {
  330.                 log.error(prefix+"analisi fallita: "+t.getMessage(),t);
  331.                 throw new UtilsException(prefixCert+"OCSP analysis failed (url: "+responderURI+"): "+t.getMessage(),t);
  332.             }

  333.         }
  334.        
  335.         throw new UtilsException("OCSP analysis failed.\n"+sbError.toString());
  336.     }

  337.     private static OCSPRequestSigned buildOCSPReq(LoggerBuffer log, String prefix, OCSPRequestParams params) throws UtilsException {

  338.         try {

  339.             OCSPRequestSigned ocspRequestSigned = new OCSPRequestSigned();

  340.             DigestCalculatorProvider digestCalculatorProvider = new JcaDigestCalculatorProviderBuilder().build();
  341.             DigestCalculator digestCalculator = digestCalculatorProvider.get(CertificateID.HASH_SHA1);

  342.             JcaCertificateID certificateID = new JcaCertificateID(digestCalculator, params.getIssuerCertificate(), params.getCertificate().getSerialNumber());

  343.             // Nounce extension (evitare reply attacks)
  344.             SecureRandomAlgorithm secureRandomAlgorithm = params.getConfig().getSecureRandomAlgorithm();
  345.             if(secureRandomAlgorithm==null) {
  346.                 secureRandomAlgorithm = SecureRandomAlgorithm.SHA1PRNG;
  347.             }
  348.             RandomGenerator randomGenerator = new RandomGenerator(true, secureRandomAlgorithm);
  349.             BigInteger nounce = BigInteger.valueOf(Math.abs(randomGenerator.nextInt()));

  350.             DEROctetString derNounceString = new DEROctetString(nounce.toByteArray());
  351.             Extension nounceExtension = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, false, derNounceString);
  352.             Extensions extensions = new Extensions(nounceExtension);

  353.             OCSPReqBuilder builder = new OCSPReqBuilder();
  354.             if(params.getConfig().isNonce()) {
  355.                 log.debug(prefix+"Build nonce value '"+derNounceString+"' ...");
  356.                 builder.addRequest(certificateID, extensions); // l'estensione nonce viene aggiunta come 'Request Single Extensions' dentro 'Requestor List'
  357.                 builder.setRequestExtensions(extensions); // l'estensione nonce viene aggiunta come 'Request Extensions' nel livello principale
  358.             }
  359.             else {
  360.                 builder.addRequest(certificateID);
  361.             }
  362.             OCSPReq ocspRequest = builder.build();

  363.             ocspRequestSigned.request = ocspRequest;
  364.             ocspRequestSigned.certificateID = certificateID;
  365.             ocspRequestSigned.nounce = nounce;

  366.             return ocspRequestSigned;

  367.         }catch(Exception t) {
  368.             throw new UtilsException("Build OCSP Request failed: "+t.getMessage(),t);
  369.         }

  370.     }

  371.     private static OCSPResp invokeOCSP(LoggerBuffer log, OCSPRequestSigned ocspRequest, String responderURI, OCSPRequestParams params) throws UtilsException {

  372.         try {
  373.            
  374.             /**java.io.File f = new java.io.File("/tmp/ocsp.request");
  375.             org.openspcoop2.utils.resources.FileSystemUtilities.writeFile(f, ocspRequest.request.getEncoded());*/
  376.             // Per verificare serializzare response su file e usare il comando: openssl ocsp -reqin /tmp/ocsp.request -noverify -text
  377.            
  378.             if(!responderURI.trim().startsWith("http") && !responderURI.trim().startsWith("file")) {
  379.                 throw new UtilsException("Unsupported protocol");
  380.             }
  381.            
  382.             HttpRequest req = new HttpRequest();
  383.             req.setMethod(HttpRequestMethod.POST);
  384.             req.setContentType(HttpConstants.CONTENT_TYPE_OCSP_REQUEST);
  385.             req.setContent(ocspRequest.request.getEncoded());
  386.            
  387.             responderURI = responderURI.trim();
  388.             if(params.getConfig().getForwardProxyUrl()!=null && StringUtils.isNotEmpty(params.getConfig().getForwardProxyUrl())) {
  389.                 String forwardProxyUrl = params.getConfig().getForwardProxyUrl();
  390.                 String remoteLocation = params.getConfig().isForwardProxyBase64() ? Base64Utilities.encodeAsString(responderURI.getBytes()) : responderURI;
  391.                 if(params.getConfig().getForwardProxyHeader()!=null && StringUtils.isNotEmpty(params.getConfig().getForwardProxyHeader())) {
  392.                     req.addHeader(params.getConfig().getForwardProxyHeader(), remoteLocation);
  393.                 }
  394.                 else if(params.getConfig().getForwardProxyQueryParameter()!=null && StringUtils.isNotEmpty(params.getConfig().getForwardProxyQueryParameter())) {
  395.                     Map<String, List<String>> queryParameters = new HashMap<>();
  396.                     TransportUtils.addParameter(queryParameters,params.getConfig().getForwardProxyQueryParameter(), remoteLocation);
  397.                     forwardProxyUrl = TransportUtils.buildUrlWithParameters(queryParameters, forwardProxyUrl, false, log.getLogDebug());
  398.                 }
  399.                 else {
  400.                     throw new UtilsException("Forward Proxy configuration error: header and query parameter not found");
  401.                 }
  402.                 req.setUrl(forwardProxyUrl);
  403.             }
  404.             else {
  405.                 req.setUrl(responderURI);
  406.             }
  407.            
  408.             if(req.getUrl().startsWith("https")) {
  409.                 req.setHostnameVerifier(params.getConfig().isExternalResourcesHostnameVerifier());
  410.                 req.setTrustAllCerts(params.getConfig().isExternalResourcesTrustAllCerts());
  411.                 if(params.getHttpsTrustStore()!=null) {
  412.                     req.setTrustStore(params.getHttpsTrustStore().getKeystore());
  413.                 }
  414.                 if(params.getHttpsKeyStore()!=null) {
  415.                     req.setKeyStore(params.getHttpsKeyStore().getKeystore());
  416.                     req.setKeyAlias(params.getConfig().getExternalResourcesKeyAlias());
  417.                     req.setKeyPassword(params.getConfig().getExternalResourcesKeyPassword());
  418.                 }
  419.             }
  420.             req.setConnectTimeout(params.getConfig().getConnectTimeout());
  421.             req.setReadTimeout(params.getConfig().getReadTimeout());

  422.             HttpResponse res = HttpUtilities.httpInvoke(req);

  423.             List<Integer> returnCodeValid = params.getConfig().getResponderReturnCodeOk();
  424.             if(returnCodeValid==null) {
  425.                 returnCodeValid = new ArrayList<>();
  426.             }
  427.             if(returnCodeValid.isEmpty()) {
  428.                 returnCodeValid.add(200);
  429.             }
  430.             boolean isValid = false;
  431.             for (Integer rt : returnCodeValid) {
  432.                 if(rt!=null && rt.intValue() == res.getResultHTTPOperation()) {
  433.                     isValid = true;
  434.                     break;
  435.                 }
  436.             }

  437.             byte[] response = res.getContent();
  438.            
  439.             /**java.io.File f = new java.io.File("/tmp/ocsp.response");
  440.             org.openspcoop2.utils.resources.FileSystemUtilities.writeFile(f, response);*/
  441.             // Per verificare serializzare response su file e usare il comando:  openssl ocsp -respin /tmp/ocsp.response -noverify -text

  442.             if(isValid) {
  443.                 if(response!=null && response.length>0) {
  444.                     return new OCSPResp(res.getContent());
  445.                 }
  446.                 else {
  447.                     throw new UtilsException("OCSP empty response (http code: "+res.getResultHTTPOperation()+")");
  448.                 }
  449.             }
  450.             else {
  451.                 String error = null;
  452.                 if(response.length<=(2048)) {
  453.                     error = Utilities.convertToPrintableText(response, 2048);
  454.                     if(error!=null && error.contains("Visualizzazione non riuscita")) {
  455.                         error = null;
  456.                     }
  457.                 }
  458.                 if(error==null) {
  459.                     error="";
  460.                 }
  461.                 else {
  462.                     error = ": "+error;
  463.                 }
  464.                 throw new UtilsException("OCSP response error (http code: "+res.getResultHTTPOperation()+")"+error);
  465.             }

  466.         }catch(Exception t) {
  467.             throw new UtilsException("Invoke OCSP '"+responderURI+"' failed: "+t.getMessage(),t);
  468.         }

  469.     }

  470.     private static CertificateStatus analyzeResponse(LoggerBuffer log, String prefix, OCSPRequestParams params, Date date, OCSPRequestSigned requestSigned, BasicOCSPResp basicOcspResponse)
  471.             throws UtilsException {

  472.         // Verifico la risposta ottenuta dal OCSP
  473.         verifyResponse(log, prefix, params, date, requestSigned, basicOcspResponse);
  474.        
  475.         // Analizzo la risposta, la quale potrebbe contenere informazioni anche per altri certificati.
  476.         SingleResp expectedResponseForCertificate = null;
  477.         for (SingleResp resp : basicOcspResponse.getResponses()) {
  478.             if (isEquals(requestSigned.certificateID, resp.getCertID())) {
  479.                 expectedResponseForCertificate = resp;
  480.                 break;
  481.             }
  482.         }
  483.         if (expectedResponseForCertificate == null) {
  484.             throw new UtilsException("OSPC Response does not contain info for certificate supplied in the OCSP request");
  485.         }
  486.         org.bouncycastle.cert.ocsp.CertificateStatus certStatus = expectedResponseForCertificate.getCertStatus();
  487.        
  488.         // NO! lo stato null è proprio il GOOD. GOOD è un alias a null
  489.         /**if(certStatus==null) {
  490.             throw new UtilsException("OSPC Response does not contain status info for certificate supplied in the OCSP request");
  491.         }*/
  492.         if (certStatus == org.bouncycastle.cert.ocsp.CertificateStatus.GOOD) {
  493.              return CertificateStatus.GOOD();
  494.         }
  495.         else if (certStatus instanceof RevokedStatus) {
  496.             RevokedStatus revoked = (RevokedStatus)certStatus;
  497.             CRLReason reason = null;
  498.             if (revoked.hasRevocationReason()) {
  499.                 reason = CRLReason.values()[revoked.getRevocationReason()];
  500.             }
  501.             return CertificateStatus.REVOKED(reason, revoked.getRevocationTime());
  502.         }
  503.         else if (certStatus instanceof UnknownStatus) {
  504.             return CertificateStatus.UNKNOWN();
  505.         }
  506.         else {
  507.             throw new UtilsException("OSPC Response contain unknown revocation status ("+certStatus+")");
  508.         }
  509.        
  510.     }

  511.     private static boolean isEquals(JcaCertificateID ocspRequestCertificateId, CertificateID ocspResponseCertificateId) {
  512.         if (ocspRequestCertificateId == null || ocspResponseCertificateId == null)
  513.             return false;
  514.         if (ocspRequestCertificateId == ocspResponseCertificateId)
  515.             return true;        
  516.         boolean serialNumberEquals = ocspRequestCertificateId.getSerialNumber()!=null && ocspResponseCertificateId.getSerialNumber()!=null && ocspRequestCertificateId.getSerialNumber().equals(ocspResponseCertificateId.getSerialNumber());
  517.         boolean nameHashEquals = ocspRequestCertificateId.getIssuerNameHash()!=null && ocspResponseCertificateId.getIssuerNameHash()!=null && Arrays.equals(ocspRequestCertificateId.getIssuerNameHash(), ocspResponseCertificateId.getIssuerNameHash());
  518.         boolean keyHashEquals = ocspRequestCertificateId.getIssuerKeyHash()!=null && ocspResponseCertificateId.getIssuerKeyHash()!=null && Arrays.equals(ocspRequestCertificateId.getIssuerKeyHash(), ocspResponseCertificateId.getIssuerKeyHash());
  519.         return serialNumberEquals && nameHashEquals && keyHashEquals;

  520.     }

  521.     private static void verifyResponse(LoggerBuffer log, String prefix, OCSPRequestParams params, Date date, OCSPRequestSigned requestSigned, BasicOCSPResp basicOcspResponse) throws UtilsException {

  522.         List<X509CertificateHolder> certs = new ArrayList<>(Arrays.asList(basicOcspResponse.getCerts()));
  523.         try {
  524.             // certificati ritornati con la risposta ocsp
  525.             X509CertificateHolder[] ospcCerts = basicOcspResponse.getCerts();
  526.             if(ospcCerts!=null && ospcCerts.length>0) {
  527.                 certs.addAll(Arrays.asList(ospcCerts));
  528.             }
  529.            
  530.             // certificato dell'issuer
  531.             certs.add(new JcaX509CertificateHolder(params.getIssuerCertificate()));
  532.            
  533.             // eventuale certificato di firma del responder OCSP autorizzato puntualmente
  534.             if (params.getSignerCertificate() != null) {
  535.                 certs.add(new JcaX509CertificateHolder(params.getSignerCertificate()));
  536.             }
  537.         } catch (Exception e) {
  538.             throw new UtilsException("OCSP Response signature unverifiable; read certs failed: "+e.getMessage(),e);
  539.         }
  540.        
  541.         // Ottengo il certificato utilizzato per firmare la risposta
  542.         X509Certificate signingCert = readSigningCertByResponderId(log, certs, basicOcspResponse);
  543.         if (signingCert == null) {
  544.             throw new UtilsException("OCSP Response signature unverifiable: signing cert not found");
  545.         }

  546.         // Valido il certificato
  547.         verifySigningCert(log, prefix, signingCert, params, date);
  548.        
  549.         // Verifico la risposta rispetto al certificato
  550.         try {
  551.             log.debug(prefix+"Verify OCSP Response ...");
  552.             JcaContentVerifierProviderBuilder builder = new JcaContentVerifierProviderBuilder();
  553.             builder.setProvider(org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME);
  554.             ContentVerifierProvider contentVerifier = builder.build(signingCert.getPublicKey());
  555.             boolean valid = basicOcspResponse.isSignatureValid(contentVerifier);
  556.             if(!valid) {
  557.                 throw new UtilsException("invalid signature");
  558.             }
  559.         } catch(Exception t) {
  560.             throw new UtilsException("Verifying OCSP Response's signature failed: "+t.getMessage(),t);
  561.         }

  562.         // Verifico validità risposta rispetto a nonce o data
  563.         verifyNonceOrDates(log, prefix, params, requestSigned, basicOcspResponse, date);
  564.     }
  565.    
  566.     private static X509Certificate readSigningCertByResponderId(LoggerBuffer log, List<X509CertificateHolder> certs, BasicOCSPResp basicOcspResponse) throws UtilsException {
  567.        
  568.         if(basicOcspResponse.getResponderId()==null) {
  569.             throw new UtilsException("OSPC Response does not contain responder id");
  570.         }
  571.         ResponderID responderId = basicOcspResponse.getResponderId().toASN1Primitive();
  572.         if(responderId==null) {
  573.             throw new UtilsException("OSPC Response does not contain responder id (asn1)");
  574.         }
  575.        
  576.         List<Throwable> listThrowable = new ArrayList<>();
  577.        
  578.         if (certs!=null && !certs.isEmpty()) {

  579.             X500Name responderName = responderId.getName();
  580.             byte[] responderKey = responderId.getKeyHash();

  581.             if (responderName != null) {
  582.                 for (X509CertificateHolder certHolder : certs) {
  583.                     try {
  584.                         JcaX509CertificateConverter converter = new JcaX509CertificateConverter();
  585.                         converter.setProvider(org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME);
  586.                         X509Certificate certCheck = converter.getCertificate(certHolder);
  587.                         X500Name nameCheck = new X500Name(certCheck.getSubjectX500Principal().getName());
  588.                         if (responderName.equals(nameCheck)) {
  589.                             return certCheck;
  590.                         }
  591.                     } catch (Exception t) {
  592.                         log.debug("check (responderName) failed: "+t.getMessage(),t);
  593.                         listThrowable.add(t);
  594.                     }
  595.                 }
  596.             } else if (responderKey != null) {
  597.                 SubjectKeyIdentifier responderSubjectKey = new SubjectKeyIdentifier(responderKey);
  598.                 for (X509CertificateHolder certHolder : certs) {
  599.                     try {
  600.                         JcaX509CertificateConverter converter = new JcaX509CertificateConverter();
  601.                         converter.setProvider(org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME);
  602.                         X509Certificate certCheck = converter.getCertificate(certHolder);
  603.                        
  604.                         // verifico per subject key identifier
  605.                         SubjectKeyIdentifier subjectKeyIdentifierCheck = null;
  606.                         if (certHolder.getExtensions() != null) {
  607.                             subjectKeyIdentifierCheck = SubjectKeyIdentifier.fromExtensions(certHolder.getExtensions());
  608.                         }
  609.                         if (subjectKeyIdentifierCheck != null && responderSubjectKey.equals(subjectKeyIdentifierCheck)) {
  610.                             return certCheck;
  611.                         }

  612.                         // verifico per chiave pubblica
  613.                         subjectKeyIdentifierCheck = new JcaX509ExtensionUtils().createSubjectKeyIdentifier(certCheck.getPublicKey());
  614.                         if (responderSubjectKey.equals(subjectKeyIdentifierCheck)) {
  615.                             return certCheck;
  616.                         }
  617.                     } catch (Exception t) {
  618.                         log.debug("check (responderKey) failed: "+t.getMessage(),t);
  619.                         listThrowable.add(t);
  620.                     }
  621.                 }
  622.             }
  623.         }
  624.        
  625.         if(!listThrowable.isEmpty()) {
  626.             if(listThrowable.size()==1) {
  627.                 Throwable t = listThrowable.get(0);
  628.                 throw new UtilsException("OCSP Response signature unverifiable: signing cert not found; "+t.getMessage(),t);
  629.             }
  630.             else {
  631.                 UtilsMultiException multi = new UtilsMultiException("OCSP Response signature unverifiable: signing cert not found; multiple exception",listThrowable.toArray(new Throwable[1]));
  632.                 throw new UtilsException(multi.getMessage(),multi);
  633.             }
  634.         }
  635.        
  636.         return null;
  637.     }
  638.    
  639.     private static void verifySigningCert(LoggerBuffer log, String prefix, X509Certificate signingCert, OCSPRequestParams params, Date date) throws UtilsException {
  640.        
  641.         // Un responder OCSP può firmare le risposte in 3 modi (https://www.rfc-editor.org/rfc/rfc6960#section-2.6):
  642.         // 1) La risposta viene firmata utilizzando lo stesso certificato della CA che ha emesso i certificati che si controlla.
  643.         //    In questo caso, nella risposta non viene ritornato alcun certificato.
  644.         //    Questo caso non richiede altri controlli al di fuori delle normali verifiche previste nella connessione TLS o gestione della sicurezza messaggio da cui il certificato proviene.
  645.         // 2) La risposta viene firmata utilizzando un altro certificato firmato dalla stessa CA che ha emesso i certificati che si controlla.
  646.         //    In questo caso nella risposta viene ritornato il certificato di firma utilizzato.
  647.         //    Deve essere controllato che il certificato abbia una 'Extended Key Usage Extension' impostata a 'id-kp-OCSPSigning' così che possa essere considerato affidabile per questo scopo.
  648.         //    Il controllo è fondamentale per prevenire attacchi "man in the middle" (siamo in http durante la comunicazione con OCSP)
  649.         //    dove la risposta intercettata viene alterata firmandola con un altro certificato rilasciato sempre dalla CA, ma non adibito a firmare risposte OCSP.
  650.         // 3) La risposta viene firmata usando un altro certificato che non è in relazione con il certificato che si sta controllando.
  651.         //    Deve quindi essere verificato ulteriormente il certificato ritornato dal responder per assicurarsi che sia "trusted"
  652.            
  653.         if(signingCert==null) {
  654.             throw new UtilsException("Signing certificate not found");
  655.         }

  656.         if (signingCert.equals(params.getIssuerCertificate())) {

  657.             /** System.out.println("*** [Case 1] OCSP response is signed by the certificate's Issuing CA ***"); */
  658.             // Non sono richiesti altri controlli
  659.            
  660.             log.debug(prefix+"[Case 1] OCSP response is signed by the certificate's Issuing CA");
  661.            
  662.         } else if (params.getSignerCertificate() != null && signingCert.equals(params.getSignerCertificate())) {
  663.            
  664.             /** System.out.println("*** [Case 3] OCSP response is signed by an authorized responder certificate manually configured***"); */
  665.             // Essendo un certificato fornito manualmente non servono altri controlli
  666.             // NOTA: essendo fornito puntualmente, è compito di chi lo configura assicurarsi che abbia l'extension key usage corretto o ne accetta il fatto che non le abbia

  667.             log.debug(prefix+"[Case 3] OCSP response is signed by an authorized responder certificate manually configured: "+signingCert.getSubjectDN());
  668.            
  669.         } else {

  670.             /** System.out.println("*** [Case 2 e 3] OCSP response is signed by an responder certificate readed in ocsp response ***"); */

  671.             // Estratto da https://www.rfc-editor.org/rfc/rfc6960#section-4.2.2.2
  672.             // - OCSP signing delegation SHALL be designated by the inclusion of  id-kp-OCSPSigning in an extended key usage certificate extension included in the OCSP response signer's certificate.  
  673.             //   This certificate MUST be issued directly by the CA that is identified in the request.
  674.            
  675.             boolean responderCertificateManuallyAuthorized = false;
  676.             X509Certificate differentIssuerResponderCertificateCA = null;
  677.             KeyStore differentIssuerResponderCertificateTrustStore = null;
  678.            
  679.             if (!signingCert.getIssuerX500Principal().equals(params.getIssuerCertificate().getSubjectX500Principal())) {
  680.                 // Case 3: La risposta viene firmata usando un altro certificato che non è in relazione con il certificato che si sta controllando.
  681.                
  682.                 log.debug(prefix+"[Case 3] OCSP response is signed by an responder certificate readed in ocsp response (different CA '"+signingCert.getIssuerDN()+"'): "+signingCert.getSubjectDN());
  683.                
  684.                 if(params.getSignerTrustStore()!=null) {
  685.                     X509Certificate tmp = (X509Certificate) params.getSignerTrustStore().getCertificateBySubject(signingCert.getSubjectX500Principal());
  686.                     if(tmp!=null && tmp.equals(signingCert)) {
  687.                         responderCertificateManuallyAuthorized = true; // autorizzato puntualmente il certificato
  688.                     }
  689.                     else {
  690.                         differentIssuerResponderCertificateCA = (X509Certificate) params.getSignerTrustStore().getCertificateBySubject(signingCert.getIssuerX500Principal());
  691.                     }
  692.                    
  693.                     differentIssuerResponderCertificateTrustStore = params.getSignerTrustStore();
  694.                 }
  695.                
  696.                 if(!responderCertificateManuallyAuthorized && differentIssuerResponderCertificateCA==null) {
  697.                     throw new UtilsException("Signing certificate is not authorized to sign OCSP responses: unauthorized different issuer certificate '"+signingCert.getIssuerDN()+"'");
  698.                 }
  699.             }
  700.             else {
  701.                 log.debug(prefix+"[Case 2] OCSP response is signed by an responder certificate readed in ocsp response (same CA): "+signingCert.getSubjectDN());
  702.             }
  703.            
  704.             // Controllo Extended Key Usage
  705.            
  706.             CertificateInfo certificateInfo = new CertificateInfo(signingCert, "signingCert");
  707.             List<ExtendedKeyUsage> requiredExtendedKeyUsages = params.getConfig().getExtendedKeyUsageRequired(); // consente di disabilitare il controllo per ambienti di test
  708.             if(requiredExtendedKeyUsages!=null && !requiredExtendedKeyUsages.isEmpty()) {
  709.                 for (ExtendedKeyUsage extendedKeyUsage : requiredExtendedKeyUsages) {
  710.                     boolean hasExtendedKeyUsage = false;
  711.                     try {
  712.                         log.debug(prefix+"Check ExtendedKeyUsage '"+extendedKeyUsage+"' ...");
  713.                         hasExtendedKeyUsage = certificateInfo.hasExtendedKeyUsage(extendedKeyUsage);
  714.                     }catch(Exception t) {
  715.                         throw new UtilsException("Signing certificate not valid for signing OCSP responses: extended key usage '"+extendedKeyUsage+"' not found; "+t.getMessage(),t);
  716.                     }
  717.                     if(!hasExtendedKeyUsage) {
  718.                         throw new UtilsException("Signing certificate not valid for signing OCSP responses: extended key usage '"+extendedKeyUsage+"' not found");
  719.                     }
  720.                 }
  721.             }
  722.             else {
  723.                 log.debug(prefix+"Check ExtendedKeyUsage disable");
  724.             }
  725.            
  726.             // Verifica del certificato di firma
  727.            
  728.             // https://www.rfc-editor.org/rfc/rfc6960#section-4.2.2.2.1
  729.            
  730.             // Revocation Checking of an Authorized Responder
  731.             //    Since an authorized OCSP responder provides status information for one or more CAs, OCSP clients need to know how to check that an Authorized Responder's certificate has not been revoked.  
  732.             //    CAs may  choose to deal with this problem in one of three ways:
  733.             //
  734.             //    1) A CA may specify that an OCSP client can trust a responder for the lifetime of the responder's certificate.  
  735.             //       The CA does so by including the extension id-pkix-ocsp-nocheck.  
  736.             //       This SHOULD be a non-critical extension.  The value of the extension SHALL be NULL.
  737.             //       CAs issuing such a certificate should realize that a compromise of the responder's key is as serious as the compromise of a CA key used to sign CRLs,
  738.             //       at least for the validity period of this certificate.  
  739.             //       CAs may choose to issue this type of certificate with a very short lifetime and renew it frequently.
  740.             /**       Identificativo: id-pkix-ocsp-nocheck OBJECT IDENTIFIER ::= { id-pkix-ocsp 5 } */
  741.             //
  742.             //    2) A CA may specify how the responder's certificate is to be checked for revocation.  
  743.             //        This can be done by using CRL Distribution Points if the check should be done using CRLs,
  744.             //        or by using Authority Information Access if the check should be done in some other way.
  745.             //        Details for specifying either of these two mechanisms are available in [RFC5280].
  746.             //    
  747.             //    3) A CA may choose not to specify any method of revocation checking for the responder's certificate,
  748.             //       in which case it would be up to the OCSP client's local security policy to decide whether that certificate should be checked for revocation or not.
  749.            
  750.             org.openspcoop2.utils.certificate.Extensions exts = null;
  751.             try {
  752.                 exts = certificateInfo.getExtensions();
  753.             }catch(Exception t) {
  754.                 log.debug("Extension read failed: "+t.getMessage(),t);
  755.             }
  756.             boolean ocspNoCheck = exts!=null && exts.hasExtension(OCSPObjectIdentifiers.id_pkix_ocsp_nocheck);
  757.            
  758.             log.debug(prefix+"ocsp_nocheck:"+ocspNoCheck);
  759.            
  760.             // Caso 1 e 3 vengono gestiti in ugual maniera
  761.             // Il caso 2 viene gestito con CRL, mentre non si gestisce un eventuale loop verso un altro servizio OCSP
  762.            
  763.             CRLParams crlParams = null;
  764.             if(!ocspNoCheck &&
  765.                 params.getConfig().isCrlSigningCertCheck()) {
  766.                 log.debug(prefix+"(SigningCert:"+signingCert.getSubjectDN()+") Build CRL params...");
  767.                 KeyStore trustStoreConfig = differentIssuerResponderCertificateTrustStore!=null ? differentIssuerResponderCertificateTrustStore : params.getIssuerTrustStore();
  768.                 try {
  769.                     crlParams = CRLParams.build(log, signingCert, null, trustStoreConfig, params.getConfig(), params.getReader());
  770.                 }catch(Exception t) {
  771.                     throw new UtilsException(t.getMessage(),t);
  772.                 }
  773.             }
  774.            
  775.             if(crlParams==null || crlParams.getCrlCertstore()==null) { // altrimenti la validita' viene verificata insieme alle CRL)
  776.                 // Controllo che non sia scaduto
  777.                 try {
  778.                     log.debug(prefix+" (SigningCert:"+signingCert.getSubjectDN()+") Check valid...");
  779.                     certificateInfo.checkValid(date);
  780.                 }catch(CertificateNotYetValidException t) {
  781.                     throw new UtilsException("Signing certificate not yet valid: "+t.getMessage(),t);
  782.                 }catch(CertificateExpiredException t) {
  783.                     throw new UtilsException("Signing certificate expired: "+t.getMessage(),t);
  784.                 }catch(Exception t) {
  785.                     throw new UtilsException("Signing certificate not valid: "+t.getMessage(),t);
  786.                 }
  787.             }
  788.            
  789.             String ca = "n.d.";
  790.             try {
  791.                 if(responderCertificateManuallyAuthorized) {
  792.                     // non devo verificarlo, è stato inserito nel truststore dedicato il certificato del responder puntualmente
  793.                 }
  794.                 else if(differentIssuerResponderCertificateCA!=null) {
  795.                     if(differentIssuerResponderCertificateCA.getSubjectDN()!=null) {
  796.                         ca = differentIssuerResponderCertificateCA.getSubjectDN().toString();
  797.                     }
  798.                     log.debug(prefix+"(SigningCert:"+signingCert.getSubjectDN()+") verify against ca '"+ca+"'...");
  799.                     certificateInfo.verify(differentIssuerResponderCertificateCA);
  800.                 }
  801.                 else {
  802.                     if(params.getIssuerCertificate().getSubjectDN()!=null) {
  803.                         ca = params.getIssuerCertificate().getSubjectDN().toString();
  804.                     }
  805.                     log.debug(prefix+"(SigningCert:"+signingCert.getSubjectDN()+") verify against ca '"+ca+"'...");
  806.                     certificateInfo.verify(params.getIssuerCertificate());
  807.                 }
  808.             } catch (Exception t) {
  809.                 throw new UtilsException("Signing certificate not valid (CA: "+ca+"): "+t.getMessage(),t);
  810.             }
  811.            
  812.             if(crlParams!=null && crlParams.getCrlCertstore()!=null) {
  813.                 try {
  814.                     log.debug(prefix+"(SigningCert:"+signingCert.getSubjectDN()+") CRL check...");
  815.                     certificateInfo.checkValid(crlParams.getCrlCertstore(), crlParams.getCrlTrustStore(), date);
  816.                 }catch(Exception t) {
  817.                     throw new UtilsException("Signing certificate not valid (CRL): "+t.getMessage(),t);
  818.                 }
  819.             }

  820.         }
  821.        
  822.     }
  823.    
  824.     private static void verifyNonceOrDates(LoggerBuffer log, String prefix, OCSPRequestParams params, OCSPRequestSigned requestSigned, BasicOCSPResp basicOcspResponse, Date date) throws UtilsException {
  825.         // https://www.rfc-editor.org/rfc/rfc6960#section-4.4.1
  826.        
  827.         // The nonce cryptographically binds a request and a response to prevent replay attacks.  
  828.         // The nonce is included as one of the requestExtensions in requests, while in responses it would be included as one of the responseExtensions.  
  829.         // In both the request and the response, the nonce will be identified by the object identifier id-pkix-ocsp-nonce, while the extnValue is the value of the nonce.
  830.         byte[] requestNonce = requestSigned.nounce!=null ? requestSigned.nounce.toByteArray() : null;
  831.         Extension responseNonce = basicOcspResponse.getExtension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce);
  832.         if (responseNonce != null && requestNonce != null) {
  833.             log.debug(prefix+"Verify nonce request-response...");
  834.             if(!Arrays.equals(requestNonce, responseNonce.getExtnValue().getOctets())) {
  835.                 throw new UtilsException("OCSP Response not valid: nonces do not match");
  836.             }
  837.         }
  838.         else {
  839.             log.debug(prefix+"Verify nonce dates...");
  840.            
  841.             // https://www.rfc-editor.org/rfc/rfc6960#section-4.2.2.1
  842.            
  843.             // Responses can contain four times -- thisUpdate, nextUpdate, producedAt, and revocationTime.  
  844.             // The semantics of these fields are defined in Section 2.4.  The format for GeneralizedTime is as specified in Section 4.1.2.5.2 of [RFC5280].

  845.             // The thisUpdate and nextUpdate fields define a recommended validity interval.  This interval corresponds to the {thisUpdate, nextUpdate} interval in CRLs.  
  846.             // Responses whose nextUpdate value is earlier than the local system time value SHOULD be considered unreliable.
  847.             // Responses whose thisUpdate time is later than the local system time SHOULD be considered unreliable.
  848.             // If nextUpdate is not set, the responder is indicating that newer  revocation information is available all the time.
  849.            
  850.             // The semantics of these fields are:

  851.             // thisUpdate      The most recent time at which the status being indicated is known by the responder to have been correct.

  852.             // nextUpdate      The time at or before which newer information will be available about the status of the certificate.

  853.             // producedAt      The time at which the OCSP responder signed this response.

  854.             // revocationTime  The time at which the certificate was revoked or placed on hold.
  855.            
  856.             long current = date.getTime();
  857.             int toleranceMilliseconds = params.getConfig().getResponseCheckDateToleranceMilliseconds();
  858.             Date rightInterval = new Date(current + toleranceMilliseconds);
  859.             Date leftInterval = new Date(current - toleranceMilliseconds);

  860.             SingleResp[] resp = basicOcspResponse.getResponses();
  861.             if(resp!=null && resp.length>0) {
  862.                 // una risposta ci deve essere, se non c'è è già stata sollevata una eccezione in precedenza
  863.                 for (SingleResp singleResp : resp) {
  864.                    
  865.                     // Responses whose thisUpdate time is later than the local system time SHOULD be considered unreliable.
  866.                     if(rightInterval.before(singleResp.getThisUpdate())){
  867.                         throw new UtilsException("OCSP Response is unreliable: this update time '"+DateUtils.getSimpleDateFormatMs().format(singleResp.getThisUpdate())+"' is later than the local system time");
  868.                     }
  869.                    
  870.                     // Responses whose nextUpdate value is earlier than the local system time value SHOULD be considered unreliable.
  871.                     // If nextUpdate is not set, the responder is indicating that newer  revocation information is available all the time.
  872.                     Date dateCheck = singleResp.getNextUpdate() != null ? singleResp.getNextUpdate() : singleResp.getThisUpdate();
  873.                     if(leftInterval.after(dateCheck)) {
  874.                         throw new UtilsException("OCSP Response is unreliable: next update time '"+DateUtils.getSimpleDateFormatMs().format(dateCheck)+"' is earlier than the local system time");
  875.                     }
  876.                 }
  877.             }

  878.         }
  879.        
  880.     }
  881.    
  882.     private static CertificateStatus checkCRLEngine(LoggerBuffer log, OCSPRequestParams params, String crlInput, Date date, String prefix) throws UtilsException {
  883.        
  884.         log.debug(prefix+"Build CRL request ...");
  885.        
  886.         CRLParams crlParams = null;
  887.         try {
  888.             crlParams = CRLParams.build(log, params.getCertificate(), crlInput, params.getIssuerTrustStore(), params.getConfig(), params.getReader());
  889.         }catch(Exception t) {
  890.             throw new UtilsException(t.getMessage(),t);
  891.         }
  892.                
  893.         CertificateInfo certificateInfo = new CertificateInfo(params.getCertificate(), "certificateCrlCheck");
  894.        
  895.         if(!certificateInfo.isSelfSigned()) {
  896.             log.debug(prefix+"Verify against CA ...");
  897.            
  898.             String ca = "n.d.";
  899.             try {
  900.                 if(params.getIssuerCertificate().getSubjectDN()!=null) {
  901.                     ca = params.getIssuerCertificate().getSubjectDN().toString();
  902.                 }
  903.                 certificateInfo.verify(params.getIssuerCertificate());
  904.             } catch (Exception t) {
  905.                 CertificateStatus cs = CertificateStatus.REVOKED(CRLReason.UNSPECIFIED, null);
  906.                 String eMessage = t.getMessage();
  907.                 String msgError = "Certificate not valid (CA: "+ca+"): "+eMessage;
  908.                 log.error(msgError, t);
  909.                 cs.setDetails(msgError);
  910.                 return cs;
  911.             }
  912.         }
  913.         else {
  914.             log.debug(prefix+"Certificate self-signed");
  915.         }
  916.        
  917.         CertificateStatus status = CertificateStatus.GOOD();
  918.        
  919.         try {
  920.             if(crlParams.getCrlCertstore()!=null) {
  921.                
  922.                 log.debug(prefix+"Verify CRL ...");
  923.                
  924.                 certificateInfo.checkValid(crlParams.getCrlCertstore(), crlParams.getCrlTrustStore(), date);
  925.             }
  926.             else {
  927.                 log.debug(prefix+"CRL undefined");
  928.                
  929.                 status = CertificateStatus.CRL_NOT_FOUND();
  930.             }
  931.         }catch(Exception t) {
  932.             CertificateStatus cs = CertificateStatus.REVOKED(CRLReason.UNSPECIFIED, null);
  933.             String eMessage = t.getMessage();
  934.             if(Utilities.existsInnerException(t, java.security.cert.CertificateExpiredException.class)) {
  935.                 Throwable inner = Utilities.getInnerException(t, java.security.cert.CertificateExpiredException.class);
  936.                 if(inner!=null && inner.getMessage()!=null) {
  937.                     eMessage = inner.getMessage();
  938.                 }
  939.             }
  940.             else if (Utilities.existsInnerException(t, java.security.cert.CertificateRevokedException.class)) {
  941.                 Throwable inner = Utilities.getInnerException(t, java.security.cert.CertificateRevokedException.class);
  942.                 if(inner instanceof java.security.cert.CertificateRevokedException) {
  943.                     java.security.cert.CertificateRevokedException cre = (java.security.cert.CertificateRevokedException) inner;
  944.                     if(cre.getRevocationReason()!=null) {
  945.                         cs = CertificateStatus.REVOKED(cre.getRevocationReason(), cre.getRevocationDate());
  946.                     }
  947.                 }
  948.             }
  949.             String msgError = "Certificate not valid (CRL): "+eMessage;
  950.             log.error(msgError, t);
  951.             cs.setDetails(msgError);
  952.             return cs;
  953.         }
  954.        
  955.         return status;
  956.     }
  957. }

  958. class OCSPRequestSigned {
  959.     OCSPReq request;
  960.     BigInteger nounce;
  961.     JcaCertificateID certificateID;
  962. }