1 /** 2 * OCSP 3 * 4 * Copyright: 5 * (C) 2012 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.cert.x509.ocsp; 12 13 import botan.constants; 14 static if (BOTAN_HAS_X509_CERTIFICATES): 15 16 public import botan.cert.x509.cert_status; 17 public import botan.cert.x509.certstor; 18 public import botan.cert.x509.x509cert; 19 import botan.cert.x509.ocsp_types; 20 import botan.asn1.asn1_time; 21 import botan.asn1.x509_dn; 22 import botan.asn1.der_enc; 23 import botan.asn1.ber_dec; 24 import botan.asn1.asn1_obj; 25 import botan.cert.x509.x509_ext; 26 import botan.asn1.oids; 27 import botan.codec.base64; 28 import botan.pubkey.pubkey; 29 import botan.cert.x509.x509path; 30 import botan.utils.http_util.http_util; 31 import botan.utils.types; 32 import std.datetime; 33 import std.algorithm : splitter; 34 import core.sync.mutex; 35 import botan.utils.mem_ops; 36 37 alias OCSPResponse = RefCounted!OCSPResponseImpl; 38 39 struct OCSPRequest 40 { 41 public: 42 @disable this(); 43 44 this(X509Certificate issuer_cert, 45 X509Certificate subject_cert) 46 47 { 48 logTrace("OCSPRequest: Issuer Cert: ", issuer_cert.toString()); 49 logTrace("OCSPRequest: Subject Cert: ", subject_cert.toString()); 50 m_issuer = issuer_cert; 51 m_subject = subject_cert; 52 } 53 54 Vector!ubyte BER_encode() const 55 { 56 CertID certid = CertID(m_issuer, m_subject); 57 58 return DEREncoder().startCons(ASN1Tag.SEQUENCE) 59 .startCons(ASN1Tag.SEQUENCE) 60 .startExplicit(0) 61 .encode(cast(size_t)(0)) // version # 62 .endExplicit() 63 .startCons(ASN1Tag.SEQUENCE) 64 .startCons(ASN1Tag.SEQUENCE) 65 .encode(certid) 66 .endCons() 67 .endCons() 68 .endCons() 69 .endCons().getContentsUnlocked(); 70 } 71 72 string base64Encode() const 73 { 74 return .base64Encode(BER_encode()); 75 } 76 77 const(X509Certificate) issuer() const { return m_issuer; } 78 79 const(X509Certificate) subject() const { return m_subject; } 80 private: 81 X509Certificate m_issuer, m_subject; 82 } 83 84 class OCSPResponseImpl 85 { 86 public: 87 this(in CertificateStore trusted_roots, 88 in string response_bits) 89 { 90 Vector!ubyte response_vec = Vector!ubyte(response_bits); 91 BERDecoder response_outer = BERDecoder(response_vec).startCons(ASN1Tag.SEQUENCE); 92 93 size_t resp_status = 0; 94 95 response_outer.decode(resp_status, ASN1Tag.ENUMERATED, ASN1Tag.UNIVERSAL); 96 97 if (resp_status != 0) 98 throw new Exception("OCSP response status " ~ to!string(resp_status)); 99 100 if (response_outer.moreItems()) 101 { 102 BERDecoder response_bytes = response_outer.startCons((cast(ASN1Tag) 0), ASN1Tag.CONTEXT_SPECIFIC).startCons(ASN1Tag.SEQUENCE); 103 104 response_bytes.decodeAndCheck(OID("1.3.6.1.5.5.7.48.1.1"), "Unknown response type in OCSP response"); 105 106 BERDecoder basicresponse = BERDecoder(response_bytes.getNextOctetString()).startCons(ASN1Tag.SEQUENCE); 107 108 Vector!ubyte tbs_bits; 109 auto sig_algo = AlgorithmIdentifier(); 110 Vector!ubyte signature; 111 Vector!X509Certificate certs; 112 113 basicresponse.startCons(ASN1Tag.SEQUENCE) 114 .rawBytes(tbs_bits) 115 .endCons() 116 .decode(sig_algo) 117 .decode(signature, ASN1Tag.BIT_STRING); 118 119 decodeOptionalList(basicresponse, (cast(ASN1Tag) 0), certs); 120 121 size_t responsedata_version; 122 X509DN name = X509DN(); 123 Vector!ubyte key_hash; 124 X509Time produced_at; 125 X509Extensions extensions; 126 127 BERDecoder(tbs_bits) 128 .decodeOptional(responsedata_version, (cast(ASN1Tag) 0), ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC) 129 .decodeOptional(name, (cast(ASN1Tag)1), ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC) 130 .decodeOptionalString(key_hash, ASN1Tag.OCTET_STRING, 2, ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC) 131 .decode(produced_at) 132 .decodeList(m_responses) 133 .decodeOptional(extensions, (cast(ASN1Tag)1), ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC); 134 135 if (certs.empty) 136 { 137 if (auto cert = trusted_roots.findCert(name, Vector!ubyte())) 138 certs.pushBack(cert); 139 else 140 throw new Exception("Could not find certificate that signed OCSP response"); 141 } 142 143 checkSignature(tbs_bits, sig_algo, signature, trusted_roots, certs); 144 } 145 146 response_outer.endCons(); 147 } 148 149 CertificateStatusCode statusFor(in X509Certificate issuer, 150 in X509Certificate subject) const 151 { 152 //logTrace("Responses: ", m_responses.length); 153 foreach (response; m_responses[]) 154 { 155 if (response.certid().isIdFor(issuer, subject)) 156 { 157 X509Time current_time = X509Time(Clock.currTime(UTC())); 158 159 if (response.certStatus() == 1) 160 return CertificateStatusCode.CERT_IS_REVOKED; 161 162 if (response.thisUpdate() > current_time) 163 return CertificateStatusCode.OCSP_NOT_YET_VALID; 164 165 if (response.nextUpdate().timeIsSet() && current_time > response.nextUpdate()) 166 return CertificateStatusCode.OCSP_HAS_EXPIRED; 167 168 if (response.certStatus() == 0) 169 return CertificateStatusCode.OCSP_RESPONSE_GOOD; 170 else 171 return CertificateStatusCode.OCSP_BAD_STATUS; 172 } 173 } 174 175 return CertificateStatusCode.OCSP_CERT_NOT_LISTED; 176 } 177 178 @property bool empty() { 179 return m_responses.length == 0; 180 } 181 private: 182 Vector!( SingleResponse ) m_responses; 183 } 184 185 186 void decodeOptionalList(ref BERDecoder ber, 187 ASN1Tag tag, 188 ref Vector!X509Certificate output) 189 { 190 BERObject obj = ber.getNextObject(); 191 192 if (obj.type_tag != tag || obj.class_tag != (ASN1Tag.CONTEXT_SPECIFIC | ASN1Tag.CONSTRUCTED)) 193 { 194 ber.pushBack(obj); 195 return; 196 } 197 198 BERDecoder list = BERDecoder(obj.value); 199 200 while (list.moreItems()) 201 { 202 BERObject certbits = list.getNextObject(); 203 X509Certificate cert = X509Certificate(unlock(certbits.value)); 204 output.pushBack(cert); 205 } 206 } 207 208 /// Does not use trusted roots 209 /// Throws if not trusted 210 void checkSignature(ALLOC)(auto const ref Vector!(ubyte, ALLOC) tbs_response, 211 const AlgorithmIdentifier sig_algo, 212 const ref Vector!ubyte signature, 213 const X509Certificate cert) 214 { 215 Unique!PublicKey pub_key = cert.subjectPublicKey(); 216 217 const Vector!string sig_info = splitter(OIDS.lookup(sig_algo.oid), '/'); 218 219 if (sig_info.length != 2 || sig_info[0] != pub_key.algoName) 220 throw new Exception("Information in OCSP response does not match cert"); 221 222 string padding = sig_info[1]; 223 SignatureFormat format = (pub_key.messageParts() >= 2) ? DER_SEQUENCE : IEEE_1363; 224 225 PKVerifier verifier = PKVerifier(*pub_key, padding, format); 226 if (!verifier.verifyMessage(putInSequence(tbs_response), signature)) 227 throw new Exception("Signature on OCSP response does not verify"); 228 } 229 230 /// Iterates over trusted roots certificate store 231 /// throws if not trusted 232 void checkSignature(ALLOC)(auto const ref Vector!(ubyte, ALLOC) tbs_response, 233 const AlgorithmIdentifier sig_algo, 234 const ref Vector!ubyte signature, 235 const CertificateStore trusted_roots, 236 const ref Vector!X509Certificate certs) 237 { 238 if (certs.length < 1) 239 throw new InvalidArgument("Short cert chain for checkSignature"); 240 241 if (trusted_roots.certificateKnown(certs[0])) 242 return checkSignature(tbs_response, sig_algo, signature, certs[0]); 243 244 // Otherwise attempt to chain the signing cert to a trust root 245 246 if (!certs[0].allowedUsage("PKIX.OCSPSigning")) 247 throw new Exception("OCSP response cert does not allow OCSP signing"); 248 249 auto result = x509PathValidate(certs, PathValidationRestrictions(), trusted_roots); 250 251 if (!result.successfulValidation()) 252 throw new Exception("Certificate validation failure: " ~ result.resultString()); 253 254 if (!trusted_roots.certificateKnown(result.trustRoot())) // not needed anymore? 255 throw new Exception("Certificate chain roots in unknown/untrusted CA"); 256 257 checkSignature(tbs_response, sig_algo, signature, result.certPath()[0]); 258 } 259 260 //version(Have_vibe_d) import vibe.core.concurrency : Tid, send; else 261 import core.sync.mutex : Mutex; 262 /// Checks the certificate online 263 struct OnlineCheck { 264 size_t id; 265 OCSPResponse* resp; 266 const(X509Certificate)* issuer; 267 const(X509Certificate)* subject; 268 const(CertificateStore)* trusted_roots; 269 270 void run() { 271 /// TODO: Test OCSP with correct BER Decoding 272 logTrace("Checking OCSP online"); 273 string responder_url; 274 if (!issuer) { 275 *cast(OCSPResponse*)resp = OCSPResponse.init; 276 return; 277 } 278 responder_url = (*cast(X509Certificate*)issuer).ocspResponder(); 279 logTrace("Responder url: ", responder_url.length); 280 281 if (responder_url.length == 0) { 282 logTrace("Aborting OCSP for ID#", id.to!string); 283 *cast(OCSPResponse*)resp = OCSPResponse.init; 284 return; 285 } 286 287 OCSPRequest req = OCSPRequest(*cast(X509Certificate*)issuer, *cast(X509Certificate*)subject); 288 289 logTrace("POST_sync"); 290 HTTPResponse res = POST_sync(responder_url, "application/ocsp-request", req.BER_encode()); 291 292 res.throwUnlessOk(); 293 294 // Check the MIME type? 295 *cast(OCSPResponse*)resp = OCSPResponse(*(cast(CertificateStore*)trusted_roots), res._body()); 296 } 297 }