/**
  * Gets OCSP responses from the Document Security Store.
  *
  * @return a list of BasicOCSPResp objects
  * @throws IOException
  * @throws GeneralSecurityException
  */
 public List<BasicOCSPResp> getOCSPResponsesFromDSS()
     throws IOException, GeneralSecurityException {
   List<BasicOCSPResp> ocsps = new ArrayList<BasicOCSPResp>();
   if (dss == null) return ocsps;
   PdfArray ocsparray = dss.getAsArray(PdfName.OCSPS);
   if (ocsparray == null) return ocsps;
   for (int i = 0; i < ocsparray.size(); i++) {
     PRStream stream = (PRStream) ocsparray.getAsStream(i);
     OCSPResp ocspResponse = new OCSPResp(PdfReader.getStreamBytes(stream));
     if (ocspResponse.getStatus() == 0)
       try {
         ocsps.add((BasicOCSPResp) ocspResponse.getResponseObject());
       } catch (OCSPException e) {
         throw new GeneralSecurityException(e);
       }
   }
   return ocsps;
 }
Beispiel #2
0
  /**
   * This method returns the {@code BasicOCSPResp} from a {@code OCSPResp}.
   *
   * @param ocspResp {@code OCSPResp} to analysed
   * @return
   */
  public static BasicOCSPResp getBasicOCSPResp(final OCSPResp ocspResp) {

    BasicOCSPResp basicOCSPResp = null;
    try {
      final Object responseObject = ocspResp.getResponseObject();
      if (responseObject instanceof BasicOCSPResp) {

        basicOCSPResp = (BasicOCSPResp) responseObject;
      } else {
        LOG.warn("Unknown OCSP response type: {}", responseObject.getClass());
      }
    } catch (OCSPException e) {
      LOG.error("Impossible to process OCSPResp!", e);
    }
    return basicOCSPResp;
  }
Beispiel #3
0
  public void performTest() throws Exception {
    String signDN = "O=Bouncy Castle, C=AU";
    KeyPair signKP = OCSPTestUtil.makeKeyPair();
    X509CertificateHolder testCert =
        new JcaX509CertificateHolder(OCSPTestUtil.makeCertificate(signKP, signDN, signKP, signDN));

    String origDN = "CN=Eric H. Echidna, [email protected], O=Bouncy Castle, C=AU";
    GeneralName origName = new GeneralName(new X509Name(origDN));
    DigestCalculatorProvider digCalcProv =
        new JcaDigestCalculatorProviderBuilder().setProvider(BC).build();

    //
    // general id value for our test issuer cert and a serial number.
    //
    CertificateID id =
        new CertificateID(
            digCalcProv.get(CertificateID.HASH_SHA1), testCert, BigInteger.valueOf(1));

    //
    // basic request generation
    //
    OCSPReqBuilder gen = new OCSPReqBuilder();

    gen.addRequest(
        new CertificateID(
            digCalcProv.get(CertificateID.HASH_SHA1), testCert, BigInteger.valueOf(1)));

    OCSPReq req = gen.build();

    if (req.isSigned()) {
      fail("signed but shouldn't be");
    }

    X509CertificateHolder[] certs = req.getCerts();

    if (certs.length != 0) {
      fail("0 certs expected, but not found");
    }

    Req[] requests = req.getRequestList();

    if (!requests[0].getCertID().equals(id)) {
      fail("Failed isFor test");
    }

    //
    // request generation with signing
    //
    X509CertificateHolder[] chain = new X509CertificateHolder[1];

    gen = new OCSPReqBuilder();

    gen.setRequestorName(new GeneralName(GeneralName.directoryName, new X509Principal("CN=fred")));

    gen.addRequest(
        new CertificateID(
            digCalcProv.get(CertificateID.HASH_SHA1), testCert, BigInteger.valueOf(1)));

    chain[0] = testCert;

    req =
        gen.build(
            new JcaContentSignerBuilder("SHA1withRSA").setProvider(BC).build(signKP.getPrivate()),
            chain);

    if (!req.isSigned()) {
      fail("not signed but should be");
    }

    if (!req.isSignatureValid(
        new JcaContentVerifierProviderBuilder().setProvider(BC).build(signKP.getPublic()))) {
      fail("signature failed to verify");
    }

    requests = req.getRequestList();

    if (!requests[0].getCertID().equals(id)) {
      fail("Failed isFor test");
    }

    certs = req.getCerts();

    if (certs == null) {
      fail("null certs found");
    }

    if (certs.length != 1 || !certs[0].equals(testCert)) {
      fail("incorrect certs found in request");
    }

    //
    // encoding test
    //
    byte[] reqEnc = req.getEncoded();

    OCSPReq newReq = new OCSPReq(reqEnc);

    if (!newReq.isSignatureValid(
        new JcaContentVerifierProviderBuilder().setProvider(BC).build(signKP.getPublic()))) {
      fail("newReq signature failed to verify");
    }

    //
    // request generation with signing and nonce
    //
    chain = new X509CertificateHolder[1];

    gen = new OCSPReqBuilder();

    Vector oids = new Vector();
    Vector values = new Vector();
    byte[] sampleNonce = new byte[16];
    Random rand = new Random();

    rand.nextBytes(sampleNonce);

    gen.setRequestorName(new GeneralName(GeneralName.directoryName, new X509Principal("CN=fred")));

    oids.addElement(OCSPObjectIdentifiers.id_pkix_ocsp_nonce);
    values.addElement(
        new X509Extension(false, new DEROctetString(new DEROctetString(sampleNonce))));

    gen.setRequestExtensions(new X509Extensions(oids, values));

    gen.addRequest(
        new CertificateID(
            digCalcProv.get(CertificateID.HASH_SHA1), testCert, BigInteger.valueOf(1)));

    chain[0] = testCert;

    req =
        gen.build(
            new JcaContentSignerBuilder("SHA1withRSA").setProvider(BC).build(signKP.getPrivate()),
            chain);

    if (!req.isSigned()) {
      fail("not signed but should be");
    }

    if (!req.isSignatureValid(
        new JcaContentVerifierProviderBuilder().setProvider(BC).build(signKP.getPublic()))) {
      fail("signature failed to verify");
    }

    //
    // extension check.
    //
    Set extOids = req.getCriticalExtensionOIDs();

    if (extOids.size() != 0) {
      fail("wrong number of critical extensions in OCSP request.");
    }

    extOids = req.getNonCriticalExtensionOIDs();

    if (extOids.size() != 1) {
      fail("wrong number of non-critical extensions in OCSP request.");
    }

    X509Extension ext = req.getExtension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce);

    ASN1Encodable extObj = ext.getParsedValue();

    if (!(extObj instanceof ASN1OctetString)) {
      fail("wrong extension type found.");
    }

    if (!areEqual(((ASN1OctetString) extObj).getOctets(), sampleNonce)) {
      fail("wrong extension value found.");
    }

    //
    // request list check
    //
    requests = req.getRequestList();

    if (!requests[0].getCertID().equals(id)) {
      fail("Failed isFor test");
    }

    //
    // response parsing - test 1
    //
    OCSPResp response = new OCSPResp(testResp1);

    if (response.getStatus() != 0) {
      fail("response status not zero.");
    }

    BasicOCSPResp brep = (BasicOCSPResp) response.getResponseObject();
    chain = brep.getCerts();

    if (!brep.isSignatureValid(
        new JcaContentVerifierProviderBuilder().setProvider(BC).build(chain[0]))) {
      fail("response 1 failed to verify.");
    }

    //
    // test 2
    //
    SingleResp[] singleResp = brep.getResponses();

    response = new OCSPResp(testResp2);

    if (response.getStatus() != 0) {
      fail("response status not zero.");
    }

    brep = (BasicOCSPResp) response.getResponseObject();
    chain = brep.getCerts();

    if (!brep.isSignatureValid(
        new JcaContentVerifierProviderBuilder().setProvider(BC).build(chain[0]))) {
      fail("response 2 failed to verify.");
    }

    singleResp = brep.getResponses();

    //
    // simple response generation
    //
    OCSPRespBuilder respGen = new OCSPRespBuilder();
    OCSPResp resp = respGen.build(OCSPRespBuilder.SUCCESSFUL, response.getResponseObject());

    if (!resp.getResponseObject().equals(response.getResponseObject())) {
      fail("response fails to match");
    }

    testECDSA();
    testRSA();
    testIrregularVersionReq();
  }
  private void processRequest(
      final HttpServletRequest request,
      final HttpServletResponse response,
      final ResponderAndRelativeUri r,
      final boolean getMethod)
      throws ServletException, IOException {
    Responder responder = r.getResponder();
    AuditEvent auditEvent = null;

    AuditLevel auditLevel = AuditLevel.INFO;
    AuditStatus auditStatus = AuditStatus.SUCCESSFUL;
    String auditMessage = null;

    long start = 0;

    AuditService auditService =
        (auditServiceRegister == null) ? null : auditServiceRegister.getAuditService();

    if (auditService != null && responder.getAuditOption() != null) {
      start = System.currentTimeMillis();
      auditEvent = new AuditEvent(new Date());
      auditEvent.setApplicationName("OCSP");
      auditEvent.setName("PERF");
    }

    try {
      if (server == null) {
        String message = "responder in servlet not configured";
        LOG.error(message);
        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        response.setContentLength(0);

        auditLevel = AuditLevel.ERROR;
        auditStatus = AuditStatus.FAILED;
        auditMessage = message;
        return;
      }

      InputStream requestStream;
      if (getMethod) {
        String relativeUri = r.getRelativeUri();

        // RFC2560 A.1.1 specifies that request longer than 255 bytes SHOULD be sent by
        // POST, we support GET for longer requests anyway.
        if (relativeUri.length() > responder.getRequestOption().getMaxRequestSize()) {
          response.setContentLength(0);
          response.setStatus(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE);

          auditStatus = AuditStatus.FAILED;
          auditMessage = "request too large";
          return;
        }

        requestStream = new ByteArrayInputStream(Base64.decode(relativeUri));
      } else {
        // accept only "application/ocsp-request" as content type
        if (!CT_REQUEST.equalsIgnoreCase(request.getContentType())) {
          response.setContentLength(0);
          response.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);

          auditStatus = AuditStatus.FAILED;
          auditMessage = "unsupporte media type " + request.getContentType();
          return;
        }

        // request too long
        if (request.getContentLength() > responder.getRequestOption().getMaxRequestSize()) {
          response.setContentLength(0);
          response.setStatus(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE);

          auditStatus = AuditStatus.FAILED;
          auditMessage = "request too large";
          return;
        } // if (CT_REQUEST)

        requestStream = request.getInputStream();
      } // end if (getMethod)

      OCSPRequest ocspRequest;
      try {
        ASN1StreamParser parser = new ASN1StreamParser(requestStream);
        ocspRequest = OCSPRequest.getInstance(parser.readObject());
      } catch (Exception e) {
        response.setContentLength(0);
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);

        auditStatus = AuditStatus.FAILED;
        auditMessage = "bad request";

        final String message = "could not parse the request (OCSPRequest)";
        if (LOG.isErrorEnabled()) {
          LOG.error(
              LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage());
        }
        LOG.debug(message, e);

        return;
      }

      OCSPReq ocspReq = new OCSPReq(ocspRequest);

      response.setContentType(HttpOcspServlet.CT_RESPONSE);

      OcspRespWithCacheInfo ocspRespWithCacheInfo =
          server.answer(responder, ocspReq, auditEvent, getMethod);
      if (ocspRespWithCacheInfo == null) {
        auditMessage = "processRequest returned null, this should not happen";
        LOG.error(auditMessage);
        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        response.setContentLength(0);

        auditLevel = AuditLevel.ERROR;
        auditStatus = AuditStatus.FAILED;
      } else {
        OCSPResp resp = ocspRespWithCacheInfo.getResponse();
        byte[] encodedOcspResp = null;
        response.setStatus(HttpServletResponse.SC_OK);

        ResponseCacheInfo cacheInfo = ocspRespWithCacheInfo.getCacheInfo();
        if (getMethod && cacheInfo != null) {
          encodedOcspResp = resp.getEncoded();
          long now = System.currentTimeMillis();
          // RFC 5019 6.2: Date: The date and time at which the OCSP server generated
          // the HTTP response.
          response.setDateHeader("Date", now);
          // RFC 5019 6.2: Last-Modified: date and time at which the OCSP responder
          // last modified the response.
          response.setDateHeader("Last-Modified", cacheInfo.getThisUpdate());
          // RFC 5019 6.2: Expires: This date and time will be the same as the
          // nextUpdate time-stamp in the OCSP
          // response itself.
          // This is overridden by max-age on HTTP/1.1 compatible components
          if (cacheInfo.getNextUpdate() != null) {
            response.setDateHeader("Expires", cacheInfo.getNextUpdate());
          }
          // RFC 5019 6.2: This profile RECOMMENDS that the ETag value be the ASCII
          // HEX representation of the SHA1 hash of the OCSPResponse structure.
          response.setHeader(
              "ETag",
              new StringBuilder(42)
                  .append('\\')
                  .append(HashCalculator.hexSha1(encodedOcspResp))
                  .append('\\')
                  .toString());

          // Max age must be in seconds in the cache-control header
          long maxAge;
          if (responder.getResponseOption().getCacheMaxAge() != null) {
            maxAge = responder.getResponseOption().getCacheMaxAge().longValue();
          } else {
            maxAge = OcspServer.defaultCacheMaxAge;
          }

          if (cacheInfo.getNextUpdate() != null) {
            maxAge =
                Math.min(maxAge, (cacheInfo.getNextUpdate() - cacheInfo.getThisUpdate()) / 1000);
          }

          response.setHeader(
              "Cache-Control",
              new StringBuilder(55)
                  .append("max-age=")
                  .append(maxAge)
                  .append(",public,no-transform,must-revalidate")
                  .toString());
        } // end if (getMethod && cacheInfo != null)

        if (encodedOcspResp != null) {
          response.getOutputStream().write(encodedOcspResp);
        } else {
          ASN1OutputStream asn1Out = new ASN1OutputStream(response.getOutputStream());
          asn1Out.writeObject(resp.toASN1Structure());
          asn1Out.flush();
        }
      } // end if (ocspRespWithCacheInfo)
    } catch (EOFException e) {
      final String message = "Connection reset by peer";
      if (LOG.isErrorEnabled()) {
        LOG.warn(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage());
      }
      LOG.debug(message, e);

      response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
      response.setContentLength(0);
    } catch (Throwable t) {
      final String message = "Throwable thrown, this should not happen!";
      LOG.error(message, t);

      response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
      response.setContentLength(0);

      auditLevel = AuditLevel.ERROR;
      auditStatus = AuditStatus.FAILED;
      auditMessage = "internal error";
    } finally {
      try {
        response.flushBuffer();
      } finally {
        if (auditEvent != null) {
          if (auditLevel != null) {
            auditEvent.setLevel(auditLevel);
          }

          if (auditStatus != null) {
            auditEvent.setStatus(auditStatus);
          }

          if (auditMessage != null) {
            auditEvent.addEventData(new AuditEventData("message", auditMessage));
          }

          auditEvent.setDuration(System.currentTimeMillis() - start);

          if (!auditEvent.containsChildAuditEvents()) {
            auditService.logEvent(auditEvent);
          } else {
            List<AuditEvent> expandedAuditEvents = auditEvent.expandAuditEvents();
            for (AuditEvent event : expandedAuditEvents) {
              auditService.logEvent(event);
            }
          }
        } // end if (auditEvent != null)
      } // end inner try
    } // end external try
  } // method processRequest