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 const(X509Certificate)* issuer, 89 in string response_bits) 90 { 91 this.trusted_roots = trusted_roots; 92 this.issuer = issuer; 93 this.response_bits = response_bits; 94 } 95 96 void check() 97 { 98 Vector!ubyte response_vec = Vector!ubyte(response_bits); 99 BERDecoder response_outer = BERDecoder(response_vec).startCons(ASN1Tag.SEQUENCE); 100 101 size_t resp_status = 0; 102 103 response_outer.decode(resp_status, ASN1Tag.ENUMERATED, ASN1Tag.UNIVERSAL); 104 105 if (resp_status != 0) 106 throw new Exception("OCSP response status " ~ to!string(resp_status)); 107 108 if (response_outer.moreItems()) 109 { 110 BERDecoder response_bytes = response_outer.startCons((cast(ASN1Tag) 0), ASN1Tag.CONTEXT_SPECIFIC).startCons(ASN1Tag.SEQUENCE); 111 112 response_bytes.decodeAndCheck(OID("1.3.6.1.5.5.7.48.1.1"), "Unknown response type in OCSP response"); 113 114 BERDecoder basicresponse = BERDecoder(response_bytes.getNextOctetString()).startCons(ASN1Tag.SEQUENCE); 115 116 Vector!ubyte tbs_bits; 117 auto sig_algo = AlgorithmIdentifier(); 118 Vector!ubyte signature; 119 Vector!X509Certificate certs; 120 121 basicresponse.startCons(ASN1Tag.SEQUENCE) 122 .rawBytes(tbs_bits) 123 .endCons() 124 .decode(sig_algo) 125 .decode(signature, ASN1Tag.BIT_STRING); 126 127 decodeOptionalList(basicresponse, (cast(ASN1Tag) 0), certs); 128 129 size_t responsedata_version; 130 X509DN name = X509DN(); 131 Vector!ubyte key_hash; 132 X509Time produced_at = X509Time(Clock.currTime(UTC())); 133 X509Extensions extensions = X509Extensions(true); 134 135 BERDecoder(tbs_bits) 136 .decodeOptional(responsedata_version, (cast(ASN1Tag) 0), ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC) 137 .decodeOptional(name, (cast(ASN1Tag)1), ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC) 138 .decodeOptionalString(key_hash, ASN1Tag.OCTET_STRING, 2, ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC) 139 .decode(produced_at) 140 .decodeList(m_responses) 141 .decodeOptional(extensions, (cast(ASN1Tag)1), ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC); 142 143 if (certs.empty && issuer && issuer.subjectDn() == name) 144 checkSignature(tbs_bits, sig_algo, signature, *issuer); 145 else 146 { 147 if (certs.empty) 148 { 149 if (auto cert = trusted_roots.findCert(name, Vector!ubyte())) 150 certs.pushBack(cert); 151 else 152 throw new Exception("Could not find certificate that signed OCSP response"); 153 } 154 155 checkSignature(tbs_bits, sig_algo, signature, trusted_roots, certs); 156 } 157 } 158 159 response_outer.endCons(); 160 } 161 162 CertificateStatusCode statusFor(in X509Certificate issuer, 163 in X509Certificate subject) const 164 { 165 //logTrace("Responses: ", m_responses.length); 166 foreach (response; m_responses[]) 167 { 168 if (response.certid().isIdFor(issuer, subject)) 169 { 170 X509Time current_time = X509Time(Clock.currTime(UTC())); 171 172 if (response.certStatus() == 1) 173 return CertificateStatusCode.CERT_IS_REVOKED; 174 175 if (response.thisUpdate() > current_time) 176 return CertificateStatusCode.OCSP_NOT_YET_VALID; 177 178 if (response.nextUpdate().timeIsSet() && current_time > response.nextUpdate()) 179 return CertificateStatusCode.OCSP_HAS_EXPIRED; 180 181 if (response.certStatus() == 0) 182 return CertificateStatusCode.OCSP_RESPONSE_GOOD; 183 else 184 return CertificateStatusCode.OCSP_BAD_STATUS; 185 } 186 } 187 188 return CertificateStatusCode.OCSP_CERT_NOT_LISTED; 189 } 190 191 @property bool empty() { 192 return m_responses.length == 0; 193 } 194 private: 195 const(CertificateStore) trusted_roots; 196 const(X509Certificate)* issuer; 197 string response_bits; 198 Vector!( SingleResponse ) m_responses; 199 } 200 201 202 void decodeOptionalList(ref BERDecoder ber, 203 ASN1Tag tag, 204 ref Vector!X509Certificate output) 205 { 206 BERObject obj = ber.getNextObject(); 207 208 if (obj.type_tag != tag || obj.class_tag != (ASN1Tag.CONTEXT_SPECIFIC | ASN1Tag.CONSTRUCTED)) 209 { 210 ber.pushBack(obj); 211 return; 212 } 213 214 BERDecoder list = BERDecoder(obj.value); 215 216 while (list.moreItems()) 217 { 218 BERObject certbits = list.getNextObject(); 219 X509Certificate cert = X509Certificate(unlock(certbits.value)); 220 output.pushBack(cert); 221 } 222 } 223 224 /// Does not use trusted roots 225 /// Throws if not trusted 226 void checkSignature(ALLOC)(auto const ref Vector!(ubyte, ALLOC) tbs_response, 227 const AlgorithmIdentifier sig_algo, 228 const ref Vector!ubyte signature, 229 const X509Certificate cert) 230 { 231 Unique!PublicKey pub_key = cert.subjectPublicKey(); 232 233 const Vector!string sig_info = splitter(OIDS.lookup(sig_algo.oid), '/'); 234 235 if (sig_info.length != 2 || sig_info[0] != pub_key.algoName) 236 throw new Exception("Information in OCSP response does not match cert"); 237 238 string padding = sig_info[1]; 239 SignatureFormat format = (pub_key.messageParts() >= 2) ? DER_SEQUENCE : IEEE_1363; 240 241 PKVerifier verifier = PKVerifier(*pub_key, padding, format); 242 if (!verifier.verifyMessage(putInSequence(tbs_response), signature)) 243 throw new Exception("Signature on OCSP response does not verify"); 244 } 245 246 /// Iterates over trusted roots certificate store 247 /// throws if not trusted 248 void checkSignature(ALLOC)(auto const ref Vector!(ubyte, ALLOC) tbs_response, 249 const AlgorithmIdentifier sig_algo, 250 const ref Vector!ubyte signature, 251 const CertificateStore trusted_roots, 252 const ref Vector!X509Certificate certs) 253 { 254 if (certs.length < 1) 255 throw new InvalidArgument("Short cert chain for checkSignature"); 256 257 if (trusted_roots.certificateKnown(certs[0])) 258 return checkSignature(tbs_response, sig_algo, signature, certs[0]); 259 260 // Otherwise attempt to chain the signing cert to a trust root 261 262 if (!certs[0].allowedUsage("PKIX.OCSPSigning")) 263 throw new Exception("OCSP response cert does not allow OCSP signing"); 264 265 auto result = x509PathValidate(certs, PathValidationRestrictions(), trusted_roots); 266 267 if (!result.successfulValidation()) 268 throw new Exception("Certificate validation failure: " ~ result.resultString()); 269 270 if (!trusted_roots.certificateKnown(result.trustRoot())) // not needed anymore? 271 throw new Exception("Certificate chain roots in unknown/untrusted CA"); 272 273 checkSignature(tbs_response, sig_algo, signature, result.certPath()[0]); 274 } 275 276 //version(Have_vibe_d) import vibe.core.concurrency : Tid, send; else 277 import core.sync.mutex : Mutex; 278 /// Checks the certificate online 279 struct OnlineCheck { 280 size_t id; 281 OCSPResponse* resp; 282 const(X509Certificate)* issuer; 283 const(X509Certificate)* subject; 284 const(CertificateStore)* trusted_roots; 285 286 void run() { 287 /// TODO: Test OCSP with correct BER Decoding 288 logTrace("Checking OCSP online"); 289 string responder_url; 290 if (!issuer) { 291 *cast(OCSPResponse*)resp = OCSPResponse.init; 292 return; 293 } 294 responder_url = (*cast(X509Certificate*)subject).ocspResponder(); 295 logTrace("Responder url: ", responder_url.length); 296 297 if (responder_url.length == 0) { 298 logTrace("Aborting OCSP for ID#", id.to!string); 299 *cast(OCSPResponse*)resp = OCSPResponse.init; 300 return; 301 } 302 303 OCSPRequest req = OCSPRequest(*cast(X509Certificate*)issuer, *cast(X509Certificate*)subject); 304 305 logTrace("POST_sync"); 306 HTTPResponse res = POST_sync(responder_url, "application/ocsp-request", req.BER_encode()); 307 308 res.throwUnlessOk(); 309 310 // Check the MIME type? 311 *cast(OCSPResponse*)resp = OCSPResponse(*(cast(CertificateStore*)trusted_roots), 312 issuer is subject ? null : issuer, 313 res._body()); 314 resp.check(); 315 } 316 }