/** * Obtains the revocation status of a certificate using OCSP using the most * common defaults. The OCSP responder URI is retrieved from the * certificate's AIA extension. The OCSP responder certificate is assumed * to be the issuer's certificate (or issued by the issuer CA). * * @param cert the certificate to be checked * @param issuerCert the issuer certificate * @return the RevocationStatus * @throws IOException if there is an exception connecting to or * communicating with the OCSP responder * @throws CertPathValidatorException if an exception occurs while * encoding the OCSP Request or validating the OCSP Response */ public static RevocationStatus check(X509Certificate cert, X509Certificate issuerCert) throws IOException, CertPathValidatorException { CertId certId = null; URI responderURI = null; try { X509CertImpl certImpl = X509CertImpl.toImpl(cert); responderURI = getResponderURI(certImpl); if (responderURI == null) { throw new CertPathValidatorException ("No OCSP Responder URI in certificate"); } certId = new CertId(issuerCert, certImpl.getSerialNumberObject()); } catch (CertificateException | IOException e) { throw new CertPathValidatorException ("Exception while encoding OCSPRequest", e); } OCSPResponse ocspResponse = check(Collections.singletonList(certId), responderURI, issuerCert, null, null, Collections.<Extension>emptyList()); return (RevocationStatus)ocspResponse.getSingleResponse(certId); }
public static RevocationStatus check(X509Certificate cert, X509Certificate issuerCert, URI responderURI, X509Certificate responderCert, Date date, List<Extension> extensions) throws IOException, CertPathValidatorException { CertId certId = null; try { X509CertImpl certImpl = X509CertImpl.toImpl(cert); certId = new CertId(issuerCert, certImpl.getSerialNumberObject()); } catch (CertificateException | IOException e) { throw new CertPathValidatorException ("Exception while encoding OCSPRequest", e); } OCSPResponse ocspResponse = check(Collections.singletonList(certId), responderURI, issuerCert, responderCert, date, extensions); return (RevocationStatus) ocspResponse.getSingleResponse(certId); }
/** * Send the encoded {@code OCSPStatusRequest} out through the provided * {@code HandshakeOutputStream} * * @param s the {@code HandshakeOutputStream} on which to send the encoded * data * * @throws IOException if any encoding errors occur */ @Override public void send(HandshakeOutStream s) throws IOException { s.putInt16(ridListLen); for (ResponderId rid : responderIds) { s.putBytes16(rid.getEncoded()); } DerOutputStream seqOut = new DerOutputStream(); DerOutputStream extBytes = new DerOutputStream(); if (extensions.size() > 0) { for (Extension ext : extensions) { ext.encode(extBytes); } seqOut.write(DerValue.tag_Sequence, extBytes); } s.putBytes16(seqOut.toByteArray()); }
/** * Create a string representation of this {@code OCSPStatusRequest} * * @return a string representation of this {@code OCSPStatusRequest} */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("OCSPStatusRequest\n"); sb.append(" ResponderIds:"); if (responderIds.isEmpty()) { sb.append(" <EMPTY>"); } else { for (ResponderId rid : responderIds) { sb.append("\n ").append(rid.toString()); } } sb.append("\n").append(" Extensions:"); if (extensions.isEmpty()) { sb.append(" <EMPTY>"); } else { for (Extension ext : extensions) { sb.append("\n ").append(ext.toString()); } } return sb.toString(); }
public static RevocationStatus check(X509Certificate cert, URI responderURI, TrustAnchor anchor, X509Certificate issuerCert, X509Certificate responderCert, Date date, List<Extension> extensions, String variant) throws IOException, CertPathValidatorException { CertId certId; try { X509CertImpl certImpl = X509CertImpl.toImpl(cert); certId = new CertId(issuerCert, certImpl.getSerialNumberObject()); } catch (CertificateException | IOException e) { throw new CertPathValidatorException ("Exception while encoding OCSPRequest", e); } OCSPResponse ocspResponse = check(Collections.singletonList(certId), responderURI, new OCSPResponse.IssuerInfo(anchor, issuerCert), responderCert, date, extensions, variant); return (RevocationStatus) ocspResponse.getSingleResponse(certId); }
/** * Display the {@code LocalSingleRequest} in human readable form. * * @return a {@code String} representation of the * {@code LocalSingleRequest} */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("CertId, Algorithm = "); sb.append(cid.getHashAlgorithm()).append("\n"); sb.append("\tIssuer Name Hash: "); sb.append(dumpHexBytes(cid.getIssuerNameHash(), 256, "", "")); sb.append("\n"); sb.append("\tIssuer Key Hash: "); sb.append(dumpHexBytes(cid.getIssuerKeyHash(), 256, "", "")); sb.append("\n"); sb.append("\tSerial Number: ").append(cid.getSerialNumber()); if (!extensions.isEmpty()) { sb.append("Extensions (").append(extensions.size()). append(")\n"); for (Extension ext : extensions.values()) { sb.append("\t").append(ext).append("\n"); } } return sb.toString(); }
/** * Set the response extensions based on the request extensions * that were received. Right now, this is limited to the * OCSP nonce extension. * * @param reqExts a {@code Map} of zero or more request extensions * * @return a {@code Map} of zero or more response extensions, keyed * by the extension object identifier in {@code String} form. */ private Map<String, Extension> setResponseExtensions( Map<String, Extension> reqExts) { Map<String, Extension> respExts = new HashMap<>(); String ocspNonceStr = PKIXExtensions.OCSPNonce_Id.toString(); if (reqExts != null) { for (String id : reqExts.keySet()) { if (id.equals(ocspNonceStr)) { // We found a nonce, add it into the response extensions Extension ext = reqExts.get(id); if (ext != null) { respExts.put(id, ext); log("Added OCSP Nonce to response"); } else { log("Error: Found nonce entry, but found null " + "value. Skipping"); } } } } return respExts; }
@Override public Map.Entry<Boolean, String> runTest() { Boolean pass = Boolean.FALSE; String message = null; try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { Extension nonceByLength = new OCSPNonceExtension(true, 32); Extension nonceByValue = new OCSPNonceExtension(true, DEADBEEF_16); pass = nonceByLength.isCritical() && nonceByValue.isCritical(); if (!pass) { message = "nonceByLength or nonceByValue was not marked " + "critical as expected"; } } catch (Exception e) { e.printStackTrace(System.out); message = e.getClass().getName(); } return new AbstractMap.SimpleEntry<>(pass, message); }
/** * Construct an {@code OCSPStatusRequest} object from data read from * a {@code HandshakeInputStream} * * @param s the {@code HandshakeInputStream} providing the encoded data * * @throws IOException if any decoding errors happen during object * construction. */ OCSPStatusRequest(HandshakeInStream in) throws IOException { responderIds = new ArrayList<>(); extensions = new ArrayList<>(); int ridListBytesRemaining = in.getInt16(); while (ridListBytesRemaining != 0) { byte[] ridBytes = in.getBytes16(); responderIds.add(new ResponderId(ridBytes)); ridListBytesRemaining -= (ridBytes.length + 2); // Make sure that no individual responder ID's length caused an // overrun relative to the outer responder ID list length if (ridListBytesRemaining < 0) { throw new SSLException("Responder ID length overflow: " + "current rid = " + ridBytes.length + ", remaining = " + ridListBytesRemaining); } } int extensionLength = in.getInt16(); if (extensionLength > 0) { byte[] extensionData = new byte[extensionLength]; in.read(extensionData); DerInputStream dis = new DerInputStream(extensionData); DerValue[] extSeqContents = dis.getSequence(extensionData.length); for (DerValue extDerVal : extSeqContents) { extensions.add(new sun.security.x509.Extension(extDerVal)); } } }
/** * Obtain the length of the {@code OCSPStatusRequest} object in its * encoded form * * @return the length of the {@code OCSPStatusRequest} object in its * encoded form */ @Override public int length() { // If we've previously calculated encodedLen simply return it if (encodedLen != 0) { return encodedLen; } ridListLen = 0; for (ResponderId rid : responderIds) { ridListLen += rid.length() + 2; } extListLen = 0; if (!extensions.isEmpty()) { try { DerOutputStream extSequence = new DerOutputStream(); DerOutputStream extEncoding = new DerOutputStream(); for (Extension ext : extensions) { ext.encode(extEncoding); } extSequence.write(DerValue.tag_Sequence, extEncoding); extListLen = extSequence.size(); } catch (IOException ioe) { // Not sure what to do here } } // Total length is the responder ID list length and extensions length // plus each lists' 2-byte length fields. encodedLen = ridListLen + extListLen + 4; return encodedLen; }
/** * Check the cache for a given {@code CertId}. * * @param cid the CertId of the response to look up * @param ocspRequest the OCSP request structure sent by the client * in the TLS status_request[_v2] hello extension. * * @return the {@code ResponseCacheEntry} for a specific CertId, or * {@code null} if it is not found or a nonce extension has been * requested by the caller. */ private ResponseCacheEntry getFromCache(CertId cid, OCSPStatusRequest ocspRequest) { // Determine if the nonce extension is present in the request. If // so, then do not attempt to retrieve the response from the cache. for (Extension ext : ocspRequest.getExtensions()) { if (ext.getId().equals(PKIXExtensions.OCSPNonce_Id.toString())) { debugLog("Nonce extension found, skipping cache check"); return null; } } ResponseCacheEntry respEntry = responseCache.get(cid); // If the response entry has a nextUpdate and it has expired // before the cache expiration, purge it from the cache // and do not return it as a cache hit. if (respEntry != null && respEntry.nextUpdate != null && respEntry.nextUpdate.before(new Date())) { debugLog("nextUpdate threshold exceeded, purging from cache"); respEntry = null; } debugLog("Check cache for SN" + cid.getSerialNumber() + ": " + (respEntry != null ? "HIT" : "MISS")); return respEntry; }
public static RevocationStatus check(X509Certificate cert, X509Certificate issuerCert, URI responderURI, X509Certificate responderCert, Date date, List<Extension> extensions, String variant) throws IOException, CertPathValidatorException { return check(cert, responderURI, null, issuerCert, responderCert, date, extensions, variant); }
/** * Checks the revocation status of a list of certificates using OCSP. * * @param certIds the CertIds to be checked * @param responderURI the URI of the OCSP responder * @param issuerInfo the issuer's certificate and/or subject and public key * @param responderCert the OCSP responder's certificate * @param date the time the validity of the OCSP responder's certificate * should be checked against. If null, the current time is used. * @param extensions zero or more OCSP extensions to be included in the * request. If no extensions are requested, an empty {@code List} must * be used. A {@code null} value is not allowed. * @return the OCSPResponse * @throws IOException if there is an exception connecting to or * communicating with the OCSP responder * @throws CertPathValidatorException if an exception occurs while * encoding the OCSP Request or validating the OCSP Response */ static OCSPResponse check(List<CertId> certIds, URI responderURI, OCSPResponse.IssuerInfo issuerInfo, X509Certificate responderCert, Date date, List<Extension> extensions, String variant) throws IOException, CertPathValidatorException { byte[] nonce = null; for (Extension ext : extensions) { if (ext.getId().equals(PKIXExtensions.OCSPNonce_Id.toString())) { nonce = ext.getValue(); } } OCSPResponse ocspResponse = null; try { byte[] response = getOCSPBytes(certIds, responderURI, extensions); ocspResponse = new OCSPResponse(response); // verify the response ocspResponse.verify(certIds, issuerInfo, responderCert, date, nonce, variant); } catch (IOException ioe) { throw new CertPathValidatorException( "Unable to determine revocation status due to network error", ioe, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } return ocspResponse; }
/** * Add multiple extensions contained in a {@code List}. * * @param extList The {@link List} of extensions to be added to * the certificate. */ public void addExtensions(List<Extension> extList) { Objects.requireNonNull(extList, "Caught null extension list"); for (Extension ext : extList) { extensions.put(ext.getId(), ext); } }
/** * Parse a SEQUENCE of extensions. This routine is used both * at the overall request level and down at the singleRequest layer. * * @param extDerItems an array of {@code DerValue} items, each one * consisting of a DER-encoded extension. * * @return a {@code Map} of zero or more extensions, * keyed by its object identifier in {@code String} form. * * @throws IOException if any parsing errors occur. */ private Map<String, Extension> parseExtensions(DerValue[] extDerItems) throws IOException { Map<String, Extension> extMap = new HashMap<>(); if (extDerItems != null && extDerItems.length != 0) { for (DerValue extDerVal : extDerItems) { sun.security.x509.Extension ext = new sun.security.x509.Extension(extDerVal); extMap.put(ext.getId(), ext); } } return extMap; }
/** * Construct a response from a list of certificate * status objects and extensions. * * @param respStat the status of the entire response * @param itemMap a {@code Map} of {@code CertId} objects and their * respective revocation statuses from the server's response DB. * @param reqExtensions a {@code Map} of request extensions * * @throws IOException if an error happens during encoding * @throws NullPointerException if {@code respStat} is {@code null} * or {@code respStat} is successful, and a {@code null} {@code itemMap} * has been provided. */ public LocalOcspResponse(OCSPResponse.ResponseStatus respStat, Map<CertId, CertStatusInfo> itemMap, Map<String, Extension> reqExtensions) throws IOException { responseStatus = Objects.requireNonNull(respStat, "Illegal null response status"); if (responseStatus == ResponseStatus.SUCCESSFUL) { respItemMap = Objects.requireNonNull(itemMap, "SUCCESSFUL responses must have a response map"); producedAtDate = new Date(); // Turn the answerd from the response DB query into a list // of single responses. for (CertId id : itemMap.keySet()) { singleResponseList.add( new LocalSingleResponse(id, itemMap.get(id))); } responseExtensions = setResponseExtensions(reqExtensions); certificates = new ArrayList<>(); if (signerCert != issuerCert) { certificates.add(signerCert); } certificates.add(issuerCert); } else { respItemMap = null; producedAtDate = null; responseExtensions = null; certificates = null; } encodedResponse = this.getBytes(); }
private void encodeExtensions(DerOutputStream tbsStream) throws IOException { DerOutputStream extSequence = new DerOutputStream(); DerOutputStream extItems = new DerOutputStream(); for (Extension ext : responseExtensions.values()) { ext.encode(extItems); } extSequence.write(DerValue.tag_Sequence, extItems); tbsStream.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)1), extSequence); }
/** * Make a ClientHello using a status_request that has no * responder IDs but does provide the nonce extension. */ private static ByteBuffer makeStatReqNoRidNonce() throws IOException { HelloExtensions exts = new HelloExtensions(); exts.add(RNIEXT); exts.add(SIGALGEXT); List<Extension> ocspExts = new ArrayList<Extension>() {{ add(new OCSPNonceExtension(16)); }}; exts.add(new CertStatusReqExtension(StatusRequestType.OCSP, new OCSPStatusRequest(null, ocspExts))); return createTlsRecord(Record.ct_handshake, VER_1_2, createClientHelloMsg(VER_1_2, SID, SUITES, exts)); }
/** * Checks the revocation status of a list of certificates using OCSP. * * @param certIds the CertIds to be checked * @param responderURI the URI of the OCSP responder * @param issuerCert the issuer's certificate * @param responderCert the OCSP responder's certificate * @param date the time the validity of the OCSP responder's certificate * should be checked against. If null, the current time is used. * @param extensions zero or more OCSP extensions to be included in the * request. If no extensions are requested, an empty {@code List} must * be used. A {@code null} value is not allowed. * @return the OCSPResponse * @throws IOException if there is an exception connecting to or * communicating with the OCSP responder * @throws CertPathValidatorException if an exception occurs while * encoding the OCSP Request or validating the OCSP Response */ static OCSPResponse check(List<CertId> certIds, URI responderURI, X509Certificate issuerCert, X509Certificate responderCert, Date date, List<Extension> extensions) throws IOException, CertPathValidatorException { byte[] nonce = null; for (Extension ext : extensions) { if (ext.getId().equals(PKIXExtensions.OCSPNonce_Id.toString())) { nonce = ext.getValue(); } } OCSPResponse ocspResponse = null; try { byte[] response = getOCSPBytes(certIds, responderURI, extensions); ocspResponse = new OCSPResponse(response); // verify the response ocspResponse.verify(certIds, issuerCert, responderCert, date, nonce); } catch (IOException ioe) { throw new CertPathValidatorException( "Unable to determine revocation status due to network error", ioe, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); } return ocspResponse; }