2016-11-23 196 views
0

我使用iTextSharp 5.5.10來生成簽名的PDF。特別是,我需要LTV簽名。 LTV可以通過CRL和OCSP請求完成。如何緩存iTextSharp(LTV簽名)的OCSP響應?

我有這樣的代碼做了它:

IOcspClient ocspClient = new OcspClientBouncyCastle(); 
ICrlClient crlClient = new CrlClientOnline(myCert.Chain); 
List<ICrlClient> lstCrlClients = new List<ICrlClient> { crlClient }; 

MakeSignature.SignDetached(sap, signature, this.myCert.Chain, lstCrlClients, ocspClient, null, 0, CryptoStandard.CMS); 

的問題是:我登錄了很多很多PDF(總是以相同的證書)。所以,我不想每次都做CRL和OCSP請求,我必須「緩存」它們。

我設法用這種代碼(它依賴於C#的MemoryCache)來緩存CRL:

private List<ICrlClient> GetCachedListCrlClient() 
{ 
    var key = "LstCrlClient"; 

    List<ICrlClient> lstCrlClients = MyGlobalCachingProvider.GetItem<List<ICrlClient>>(key); 
    if (lstCrlClients == null) 
    {   
     lstCrlClients = new List<ICrlClient>(); 
     for (int i = 0; i < myCert.Chain.Length; i++) 
     { 
      String crlUrl = CertificateUtil.GetCRLURL(myCert.Chain[i]); 
      if (crlUrl != null) 
      { 
       byte[] crlDownloaded = new System.Net.WebClient().DownloadData(crlUrl); 
       ICrlClient crlClient = new CrlClientOffline(crlDownloaded); 
       lstCrlClients.Add(crlClient); 
      } 
     } 
     MyGlobalCachingProvider.AddItem(key, lstCrlClients, DateTime.Now.AddHours(2)); 
    } 

    return lstCrlClients; 
} 

我卻無法找到任何解決方案緩存OCSP響應。有人有線索嗎?

+0

OCSP響應通常只有很短的時間去生活。因此,通常緩存它們是不值得的。如果您在短時間內簽署非常多的PDF,您可能需要實施類似於「CrlClientOffline」的「OcspClientOffline」。看看代碼,這是微不足道的。 – mkl

+0

謝謝。你的解決方案是好的。然而,OCSP的迴應並不總是短暫的:幾分鐘到幾天。就我而言,這是10天!所以我認爲iText應該爲我們提供這個問題的標準解決方案... – AEC

+0

10天OCSP響應?哇!好吧,在這種情況下,我確實知道你想要緩存。我只習慣不超過幾分鐘的生活時間。 – mkl

回答

1

感謝mlk評論,我做到了:我實現了我自己的類,靈感來自OcspClientBouncyCastle代碼。代碼確實很簡單。我的班級管理緩存:它只發送一個OCSP請求。這是做這些事的好方法。

示例代碼:

// Once instanciated, this class fires one and only one OCSP request : it keeps the first result in memory. 
// You may want to cache this object ; ie with MemoryCache. 
public class MyOcspClientBouncyCastleSingleRequest : IOcspClient 
{ 
    private static readonly ILogger LOGGER = LoggerFactory.GetLogger(typeof(OcspClientBouncyCastle)); 

    private readonly OcspVerifier verifier; 

    // The request-result 
    private Dictionary<String, BasicOcspResp> _cachedOcspResponse = new Dictionary<string, BasicOcspResp>(); 

    /** 
    * Create default implemention of {@code OcspClient}. 
    * Note, if you use this constructor, OCSP response will not be verified. 
    */ 
    [Obsolete] 
    public MyOcspClientBouncyCastleSingleRequest() 
    { 
     verifier = null; 
    } 

    /** 
    * Create {@code OcspClient} 
    * @param verifier will be used for response verification. {@see OCSPVerifier}. 
    */ 
    public MyOcspClientBouncyCastleSingleRequest(OcspVerifier verifier) 
    { 
     this.verifier = verifier; 
    } 

    /** 
    * Gets OCSP response. If {@see OCSPVerifier} was set, the response will be checked. 
    */ 
    public virtual BasicOcspResp GetBasicOCSPResp(X509Certificate checkCert, X509Certificate rootCert, String url) 
    { 
     String dicKey = checkCert.SubjectDN.ToString() + "-" + rootCert.SubjectDN.ToString() + "-" + url; 
     if (_cachedOcspResponse != null && _cachedOcspResponse.Count > 0 && _cachedOcspResponse.ContainsKey(dicKey)) 
     { 
      BasicOcspResp cachedResult = _cachedOcspResponse[dicKey]; 
      return cachedResult; 
     } 
     else 
     { 
      try 
      { 
       OcspResp ocspResponse = GetOcspResponse(checkCert, rootCert, url); 
       if (ocspResponse == null) 
       { 
        _cachedOcspResponse.Add(dicKey, null); 
        return null; 
       } 
       if (ocspResponse.Status != OcspRespStatus.Successful) 
       { 
        _cachedOcspResponse.Add(dicKey, null); 
        return null; 
       } 
       BasicOcspResp basicResponse = (BasicOcspResp)ocspResponse.GetResponseObject(); 
       if (verifier != null) 
       { 
        verifier.IsValidResponse(basicResponse, rootCert); 
       } 
       _cachedOcspResponse.Add(dicKey, basicResponse); 
       return basicResponse; 
      } 
      catch (Exception ex) 
      { 
       if (LOGGER.IsLogging(Level.ERROR)) 
        LOGGER.Error(ex.Message); 
      } 
      return null; 
     } 
    } 

    /** 
    * Gets an encoded byte array with OCSP validation. The method should not throw an exception. 
    * 
    * @param checkCert to certificate to check 
    * @param rootCert the parent certificate 
    * @param url  to get the verification. It it's null it will be taken 
    *     from the check cert or from other implementation specific source 
    * @return a byte array with the validation or null if the validation could not be obtained 
    */ 
    public byte[] GetEncoded(X509Certificate checkCert, X509Certificate rootCert, String url) 
    { 
     try 
     { 
      BasicOcspResp basicResponse = GetBasicOCSPResp(checkCert, rootCert, url); 
      if (basicResponse != null) 
      { 
       SingleResp[] responses = basicResponse.Responses; 
       if (responses.Length == 1) 
       { 
        SingleResp resp = responses[0]; 
        Object status = resp.GetCertStatus(); 
        if (status == CertificateStatus.Good) 
        { 
         return basicResponse.GetEncoded(); 
        } 
        else if (status is RevokedStatus) 
        { 
         throw new IOException(MessageLocalization.GetComposedMessage("ocsp.status.is.revoked")); 
        } 
        else 
        { 
         throw new IOException(MessageLocalization.GetComposedMessage("ocsp.status.is.unknown")); 
        } 
       } 
      } 
     } 
     catch (Exception ex) 
     { 
      if (LOGGER.IsLogging(Level.ERROR)) 
       LOGGER.Error(ex.Message); 
     } 
     return null; 
    } 

    /** 
    * Generates an OCSP request using BouncyCastle. 
    * @param issuerCert certificate of the issues 
    * @param serialNumber serial number 
    * @return an OCSP request 
    * @throws OCSPException 
    * @throws IOException 
    */ 
    private static OcspReq GenerateOCSPRequest(X509Certificate issuerCert, BigInteger serialNumber) 
    { 
     // Generate the id for the certificate we are looking for 
     CertificateID id = new CertificateID(CertificateID.HashSha1, issuerCert, serialNumber); 

     // basic request generation with nonce 
     OcspReqGenerator gen = new OcspReqGenerator(); 
     gen.AddRequest(id); 

     // create details for nonce extension 
     IDictionary extensions = new Hashtable(); 

     extensions[OcspObjectIdentifiers.PkixOcspNonce] = new X509Extension(false, new DerOctetString(new DerOctetString(PdfEncryption.CreateDocumentId()).GetEncoded())); 

     gen.SetRequestExtensions(new X509Extensions(extensions)); 
     return gen.Generate(); 
    } 

    private OcspResp GetOcspResponse(X509Certificate checkCert, X509Certificate rootCert, String url) 
    { 
     if (checkCert == null || rootCert == null) 
      return null; 
     if (url == null) 
     { 
      url = CertificateUtil.GetOCSPURL(checkCert); 
     } 
     if (url == null) 
      return null; 
     LOGGER.Info("Getting OCSP from " + url); 
     OcspReq request = GenerateOCSPRequest(rootCert, checkCert.SerialNumber); 
     byte[] array = request.GetEncoded(); 

     HttpWebRequest con = (HttpWebRequest)WebRequest.Create(url); 
     con.ContentLength = array.Length; 
     con.ContentType = "application/ocsp-request"; 
     con.Accept = "application/ocsp-response"; 
     con.Method = "POST"; 
     Stream outp = con.GetRequestStream(); 
     outp.Write(array, 0, array.Length); 
     outp.Close(); 
     HttpWebResponse response = (HttpWebResponse)con.GetResponse(); 
     if (response.StatusCode != HttpStatusCode.OK) 
      throw new IOException(MessageLocalization.GetComposedMessage("invalid.http.response.1", (int)response.StatusCode)); 
     Stream inp = response.GetResponseStream(); 
     OcspResp ocspResponse = new OcspResp(inp); 
     inp.Close(); 
     response.Close(); 
     return ocspResponse; 
    }