no.digipost.security.ocsp.OcspLookup Maven / Gradle / Ivy
/**
* Copyright (C) Posten Norge AS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package no.digipost.security.ocsp;
import no.digipost.security.DigipostSecurityException;
import no.digipost.security.Sha1Calculator;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.ocsp.CertificateID;
import org.bouncycastle.cert.ocsp.OCSPException;
import org.bouncycastle.cert.ocsp.OCSPReqBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.Optional;
import static no.digipost.security.DigipostSecurity.describe;
import static org.apache.http.client.methods.RequestBuilder.post;
/**
* Online Certificate Status Protocol (OCSP) is an automated certificate checking
* network protocol. One can query an OCSP responder for the status of a certificate. The responder
* returns whether the certificate is still trusted by the CA that issued it.
*
* @see Internet Engineering Task Force (IETF) RFC6960
*/
public final class OcspLookup {
static final String AUTHORITY_INFO_ACCESS_OID = "1.3.6.1.5.5.7.1.1";
private static final Logger LOG = LoggerFactory.getLogger(OcspLookup.class);
/**
* Prepare a new OCSP lookup request for the given certificate.
*
* @param certificate the certificate to lookup. It must contain an OCSP responder URI.
* @param issuer the issuer of the certificate.
* @return an OCSP request, ready to be {@link OcspLookup#executeUsing(CloseableHttpClient) executed},
* or {@link Optional#empty()} of no OCSP responder URI was found, or any other error occuring.
*/
public static Optional newLookup(X509Certificate certificate, X509Certificate issuer) {
byte[] authorityInfoAccessValue = certificate.getExtensionValue(AUTHORITY_INFO_ACCESS_OID);
if (authorityInfoAccessValue == null) {
return Optional.empty();
}
String ocspResponderUri;
try {
ocspResponderUri = extractResponderUri(authorityInfoAccessValue);
} catch (RuntimeException e) {
LOG.warn("Failed to extract OCSP uri from " + describe(certificate), e);
return Optional.empty();
}
try {
CertificateID certificateId = new CertificateID(new Sha1Calculator(), new X509CertificateHolder(issuer.getEncoded()), certificate.getSerialNumber());
return Optional.of(new OcspLookup(ocspResponderUri, certificateId));
} catch (OCSPException | CertificateEncodingException | IOException e) {
LOG.warn("Failed to create certificate ID from issuer " + issuer + " and certificate " + describe(certificate), e);
return Optional.empty();
}
}
private static String extractResponderUri(byte[] authorityInfoAccessValue) {
try {
DEROctetString base = (DEROctetString) ASN1Primitive.fromByteArray(authorityInfoAccessValue);
DLSequence seq = (DLSequence) ASN1Primitive.fromByteArray(base.getOctets());
Enumeration> objects = seq.getObjects();
while (objects.hasMoreElements()) {
Object elm = objects.nextElement();
if (elm instanceof DLSequence) {
ASN1Encodable id = ((DLSequence)elm).getObjectAt(0);
if (OCSPObjectIdentifiers.id_pkix_ocsp.equals(id)) {
DERTaggedObject dt = (DERTaggedObject)((DLSequence)elm).getObjectAt(1);
DEROctetString dos = (DEROctetString)dt.getObjectParser(dt.getTagNo(), true);
return new String(dos.getOctets());
}
}
}
throw new DigipostSecurityException("Object identifier " + OCSPObjectIdentifiers.id_pkix_ocsp + " not found");
} catch (IOException e) {
throw new DigipostSecurityException("Object identifier " + OCSPObjectIdentifiers.id_pkix_ocsp + " not found", e);
}
}
public final String uri;
public final CertificateID certificateId;
private OcspLookup(String uri, CertificateID certificateId) {
this.certificateId = certificateId;
this.uri = uri;
}
/**
* Execute the OCSP lookup request.
*
* @param client the http client to use for executing the lookup request.
* @return the {@link OcspResult result} of the OCSP lookup.
*/
public OcspResult executeUsing(CloseableHttpClient client) {
try {
HttpEntity ocspRequestEntity = new ByteArrayEntity(new OCSPReqBuilder().addRequest(certificateId).build().getEncoded());
HttpUriRequest ocspRequest = post().setUri(uri)
.addHeader("Content-Type", "application/ocsp-request")
.setEntity(ocspRequestEntity).build();
return new OcspResult(uri, client.execute(ocspRequest));
} catch (OCSPException | IOException e) {
throw new DigipostSecurityException(e);
}
}
@Override
public String toString() {
return "OCSP-lookup to responder uri " + uri;
}
}