How to Programmatically Check If a Certificate Has Been Revoked

How to programmatically check if a certificate has been revoked?

Checking if a certificate is revoked can be a complex process. First you have to look for a CDP or OCSP AIA, then make a request, parse the response, and check that the response is signed against by a CA that is authorized to respond for the certificate in question. If it is a CRL you then need to see if the serial number of the certificate you're checking is present in the list. If it is OCSP then you need to see if you've received a "good" response (as opposed to unknown, revoked, or any of the various OCSP responder errors like unauthorized). Additionally you may want to verify that the certificate is within its validity period and chains to a trusted root. Finally, you should do revocation checks against every intermediate as well and check the certificate's fingerprint against the explicit blacklists that Mozilla/Apple/Google/Microsoft maintain.

I'm unaware of any Ruby libraries that automate the revocation checking process for you (eventually I hope to add it to r509), but given your more specific use case here's some untested code that should point you in the right direction.

require 'r509'
require 'net/http'
cert = R509::Cert.load_from_file("some_iphone_cert.pem")
crl_uri = cert.crl_distribution_points.crl.uris[0]
crl = Net::HTTP.get_response(URI(crl_uri)) # you may need to follow redirects here, but let's assume you got the CRL.
# Also note that the Apple WWDRCA CRL is like 28MB so you may want to cache this damned thing. OCSP would be nicer but it's a bit trickier to validate.
parsed_crl = R509::CRL::SignedList.new(crl)
if not parsed_crl.verify(cert.public_key)
raise StandardError, "Invalid CRL for certificate"
end
if parsed_crl.revoked?(cert.serial)
puts 'revoked'
end

Unfortunately, due to the enormous size (~680k entries) of the Apple WWDRCA CRL this check can be quite slow with r509's current hash map model.

If you're interested in going down the OCSP path I can write up how to generate OCSP requests/parse responses in Ruby as well.

Edit: It appears the iPhone developer certificates I have do not contain an embedded OCSP AIA so the only option for revocation checking will be via CRL distribution point as presented above.

Edit2: Oh why not, let's do an OCSP check in Ruby! For this we'll need the certificate and its issuing certificate. You can't use a WWDRCA certificate for this so just grab one from your favorite website. I'm using my own website.

require 'net/http'
require 'r509'
cert = R509::Cert.load_from_file("my_website.pem")
# get the first OCSP AIA URI. There can be more than one
# (degenerate example!)
ocsp_uri = cert.aia.ocsp.uris[0]
issuer = R509::Cert.load_from_file("my_issuer.pem")
cert_id = OpenSSL::OCSP::CertificateId.new(cert.cert,issuer.cert)
request = OpenSSL::OCSP::Request.new
request.add_certid(cert_id)
# we're going to make a GET request per RFC 5019. You can also POST the
# binary DER encoded version if you're more of an RFC 2560 partisan
request_uri = URI(ocsp_uri+"/"+URI.encode_www_form_component(req_pem.strip)
http_response = Net::HTTP.get_response(request_uri)
if http_response.code != "200"
raise StandardError, "Invalid response code from OCSP responder"
end
response = OpenSSL::OCSP::Response.new(http_response.body)
if response.status != 0
raise StandardError, "Not a successful status"
end
if response.basic[0][0].serial != cert.serial
raise StandardError, "Not the same serial"
end
if response.basic[0][1] != 0 # 0 is good, 1 is revoked, 2 is unknown.
raise StandardError, "Not a good status"
end
current_time = Time.now
if response.basic[0][4] > current_time or response.basic[0][5] < current_time
raise StandardError, "The response is not within its validity window"
end
# we also need to verify that the OCSP response is signed by
# a certificate that is allowed and chains up to a trusted root.
# To do this you'll need to build an OpenSSL::X509::Store object
# that contains the certificate you're checking + intermediates + root.
store = OpenSSL::X509::Store.new
store.add_cert(cert.cert)
store.add_cert(issuer.cert) #assuming issuer is a trusted root here, but in reality you'll need at least one more certificate
if response.basic.verify([],store) != true
raise StandardError, "Certificate verification error"
end

The example code above neglects to handle many possible edge cases, so it should be considered a starting point only. Good luck!

How do I check if an X509 certificate has been revoked in Java?

Every CA publishes the list of the certificates it has revoked.
This list includes the serial number of the certificates and the revocation date

to get the url of the certificate revocation list (CRL) follow the below steps

  • open the certificate
  • go to Details Tab and find the field "CRL Distribution Point" in the details list

It will show you the value something like this

[1]CRL Distribution Point
Distribution Point Name:
Full Name:
URL=mscrl.microsoft.com/pki/mscorp/crl/msitwww2.crl
URL=crl.microsoft.com/pki/mscorp/crl/msitwww2.crl

So in your code you need to download these files and check for the certificate serial number in them to see if it's revoked or not

Find below the sample code for it

public class CertVerification {

public static void main(String[] args) throws Exception {

String certificatePath = "C:\\Users\\user1\\Desktop\\test.cer";

CertificateFactory cf = CertificateFactory.getInstance("X509");

X509Certificate certificate = null;
X509CRLEntry revokedCertificate = null;
X509CRL crl = null;

certificate = (X509Certificate) cf.generateCertificate(new FileInputStream(new File(certificatePath)));

URL url = new URL("http://<someUrl from certificate>.crl");
URLConnection connection = url.openConnection();

try(DataInputStream inStream = new DataInputStream(connection.getInputStream())){

crl = (X509CRL)cf.generateCRL(inStream);
}

revokedCertificate = crl.getRevokedCertificate(certificate.getSerialNumber());

if(revokedCertificate !=null){
System.out.println("Revoked");
}
else{
System.out.println("Valid");
}

}

}

Please See

These lists are updated periodically

You can get these Revocation URL's from the certificate as well, i have just given an example

This is just a basic example to give you a head start

Update

I found this sample class to check certificate, it also verifies with the CRL issued by the certificate's CA and certification chain, so you don't need to provide the CRL url as well

https://svn.cesecore.eu/svn/ejbca/branches/Branch_3_2_3_utf8/ejbca/doc/samples/ValidateCertUseCRL.java

Is it possible to revoke a valid SSL certificate

The certificates include as part of their information how to check revocation, normally a CRL (Certificate Revocation List) or online service OCSP (Online Certificate Status Protocol)

A certification service provider revoke a certificate by including it in the CRL and its OCSP service. When the customer wants to check revocation of a certificate download the CRL to check or performs an OCSP request

To that could be done you would need:
1) Have a OCSP service and / or CRL
2) Include in the self-generated certificate the URL to OCSP/CRL.
3) Revoke certificates including them in the list of your revocation service

For example, for secure HTTPS websites the browser is responsible for performing revocation checking. However Google decided in 2012 to default Chrome not to check for certificate revocation on non-EV certificates

Therefore, be aware that control of revocation is client responsibility and could not do

TCP Socket - Java SSL Certificate Revocation Checking

I figured how to enable CRL checking within a SSLContext without implementing a custom validator, as suggested in the comments.

It is mainly about properly initializing the SSLContext's TrustManagers with a revocation checker, only a few lines, no custom check logic and the CRL is now checked automatically as well as the verification path.

Here's a snippet...

KeyStore ts = KeyStore.getInstance("JKS");
FileInputStream tfis = new FileInputStream(trustStorePath);
ts.load(tfis, trustStorePass.toCharArray());

KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

// initialize certification path checking for the offered certificates and revocation checks against CLRs
CertPathBuilder cpb = CertPathBuilder.getInstance("PKIX");
PKIXRevocationChecker rc = (PKIXRevocationChecker)cpb.getRevocationChecker();
rc.setOptions(EnumSet.of(
PKIXRevocationChecker.Option.PREFER_CRLS, // prefer CLR over OCSP
PKIXRevocationChecker.Option.ONLY_END_ENTITY,
PKIXRevocationChecker.Option.NO_FALLBACK)); // don't fall back to OCSP checking

PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(ts, new X509CertSelector());
pkixParams.addCertPathChecker(rc);

tmf.init( new CertPathTrustManagerParameters(pkixParams) );
// init KeyManagerFactory
kmf.init(...)

SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(kmf.getKeyManagers), tmf.getTrustManagers(), null);

That essentially did what I needed in my application, checking whether a certificate issued to a client is revoked in our CRL. Only checking the end entity and allowing the CRL check to fail is accepted because its all our infrastructure.



Related Topics



Leave a reply



Submit