1 /** 2 * X.509 CRL 3 * 4 * Copyright: 5 * (C) 1999-2007 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.x509_crl; 12 13 import botan.constants; 14 static if (BOTAN_HAS_X509_CERTIFICATES): 15 16 import botan.cert.x509.x509_obj; 17 import botan.cert.x509.crl_ent; 18 import botan.cert.x509.x509_ext; 19 import botan.cert.x509.x509cert; 20 import botan.asn1.x509_dn; 21 import botan.asn1.ber_dec; 22 import botan.utils.parsing; 23 import botan.math.bigint.bigint; 24 import botan.asn1.oids; 25 import botan.asn1.asn1_time; 26 import botan.utils.types; 27 28 alias X509CRL = RefCounted!X509CRLImpl; 29 30 /** 31 * This class represents X.509 Certificate Revocation Lists (CRLs). 32 */ 33 final class X509CRLImpl : X509Object 34 { 35 public: 36 /** 37 * This class represents CRL related errors. 38 */ 39 class X509CRLError : Exception 40 { 41 this(in string error) { 42 super("X509CRL: " ~ error); 43 } 44 } 45 46 /** 47 * Check if this particular certificate is listed in the CRL 48 */ 49 bool isRevoked(in X509Certificate cert) const 50 { 51 /* 52 If the cert wasn't issued by the CRL issuer, it's possible the cert 53 is revoked, but not by this CRL. Maybe throw new an exception instead? 54 */ 55 if (cert.issuerDn() != issuerDn()) 56 return false; 57 58 Vector!ubyte crl_akid = authorityKeyId(); 59 const Vector!ubyte cert_akid = cert.authorityKeyId(); 60 61 if (!crl_akid.empty && !cert_akid.empty) 62 if (crl_akid != cert_akid) 63 return false; 64 65 const Vector!ubyte cert_serial = cert.serialNumber(); 66 67 bool is_revoked = false; 68 69 foreach (const revoked; m_revoked[]) 70 { 71 if (cert_serial == revoked.serialNumber()) 72 { 73 if (revoked.reasonCode() == REMOVE_FROM_CRL) 74 is_revoked = false; 75 else 76 is_revoked = true; 77 } 78 } 79 80 return is_revoked; 81 } 82 83 84 /** 85 * Get the entries of this CRL in the form of a vector. 86 * Returns: vector containing the entries of this CRL. 87 */ 88 ref const(Vector!CRLEntry) getRevoked() const 89 { 90 return m_revoked; 91 } 92 93 /** 94 * Get the issuer DN of this CRL. 95 * Returns: CRLs issuer DN 96 */ 97 X509DN issuerDn() const 98 { 99 return createDn(m_info); 100 } 101 102 103 /** 104 * Get the AuthorityKeyIdentifier of this CRL. 105 * Returns: this CRLs AuthorityKeyIdentifier 106 */ 107 Vector!ubyte authorityKeyId() const 108 { 109 return m_info.get1Memvec("X509v3.AuthorityKeyIdentifier"); 110 } 111 112 /** 113 * Get the serial number of this CRL. 114 * Returns: CRLs serial number 115 */ 116 uint crlNumber() const 117 { 118 return m_info.get1Uint("X509v3.CRLNumber"); 119 } 120 121 /** 122 * Get the CRL's thisUpdate value. 123 * Returns: CRLs thisUpdate 124 */ 125 const(X509Time) thisUpdate() const 126 { 127 return X509Time(m_info.get1("X509.CRL.start")); 128 } 129 130 /** 131 * Get the CRL's nextUpdate value. 132 * Returns: CRLs nextdUpdate 133 */ 134 const(X509Time) nextUpdate() const 135 { 136 return X509Time(m_info.get1("X509.CRL.end")); 137 } 138 139 /** 140 * Construct a CRL from a data source. 141 * 142 * Params: 143 * input = the data source providing the DER or PEM encoded CRL. 144 * throw_on_unknown_critical_ = should we throw new an exception 145 * if an unknown CRL extension marked as critical is encountered. 146 */ 147 this(DataSource input, bool throw_on_unknown_critical_ = false) 148 { 149 m_throw_on_unknown_critical = throw_on_unknown_critical_; 150 super(input, "X509 CRL/CRL"); 151 doDecode(); 152 } 153 154 /** 155 * Construct a CRL from a file containing the DER or PEM encoded CRL. 156 * 157 * Params: 158 * filename = the name of the CRL file 159 * throw_on_unknown_critical_ = should we throw new an exception 160 * if an unknown CRL extension marked as critical is encountered. 161 */ 162 this(in string filename, 163 bool throw_on_unknown_critical_ = false) 164 { 165 m_throw_on_unknown_critical = throw_on_unknown_critical_; 166 super(filename, "CRL/X509 CRL"); 167 doDecode(); 168 } 169 170 /** 171 * Construct a CRL from a binary vector 172 * Params: 173 * vec = the binary (DER) representation of the CRL 174 * throw_on_unknown_critical_ = should we throw new an exception 175 * if an unknown CRL extension marked as critical is encountered. 176 */ 177 this(const ref Vector!ubyte vec, bool throw_on_unknown_critical_ = false) 178 { 179 m_throw_on_unknown_critical = throw_on_unknown_critical_; 180 super(vec, "CRL/X509 CRL"); 181 doDecode(); 182 } 183 184 protected: 185 186 /* 187 * Decode the TBSCertList data 188 */ 189 override void forceDecode() 190 { 191 logTrace("Starting Decode CRL"); 192 BERDecoder tbs_crl = BERDecoder(m_tbs_bits); 193 194 size_t _version; 195 tbs_crl.decodeOptional(_version, ASN1Tag.INTEGER, ASN1Tag.UNIVERSAL); 196 197 if (_version != 0 && _version != 1) 198 throw new X509CRLError("Unknown X.509 CRL version " ~ to!string(_version+1)); 199 200 auto sig_algo_inner = AlgorithmIdentifier(); 201 tbs_crl.decode(sig_algo_inner); 202 203 logTrace("Sig algo inner: ", OIDS.lookup(sig_algo_inner.oid)); 204 if (m_sig_algo != sig_algo_inner) 205 throw new X509CRLError("Algorithm identifier mismatch"); 206 207 X509DN dn_issuer = X509DN(); 208 tbs_crl.decode(dn_issuer); 209 m_info.add(dn_issuer.contents()); 210 211 X509Time start, end; 212 tbs_crl.decode(start).decode(end); 213 logTrace("CRL Start, ", start.readableString()); 214 m_info.add("X509.CRL.start", start.readableString()); 215 logTrace("CRL End"); 216 logTrace("CRL Start, ", end.readableString()); 217 m_info.add("X509.CRL.end", end.readableString()); 218 219 BERObject next = tbs_crl.getNextObject(); 220 221 logTrace("Next..."); 222 if (next.type_tag == ASN1Tag.SEQUENCE && next.class_tag == ASN1Tag.CONSTRUCTED) 223 { 224 BERDecoder cert_list = BERDecoder(next.value); 225 226 while (cert_list.moreItems()) 227 { 228 CRLEntry entry = CRLEntry(m_throw_on_unknown_critical); 229 cert_list.decode(entry); 230 m_revoked.pushBack(entry); 231 } 232 next = tbs_crl.getNextObject(); 233 } 234 235 if (next.type_tag == 0 && 236 next.class_tag == (ASN1Tag.CONSTRUCTED | ASN1Tag.CONTEXT_SPECIFIC)) 237 { 238 BERDecoder crl_options = BERDecoder(next.value); 239 240 X509Extensions extensions = X509Extensions(m_throw_on_unknown_critical); 241 242 crl_options.decode(extensions).verifyEnd(); 243 244 extensions.contentsTo(m_info, m_info); 245 246 next = tbs_crl.getNextObject(); 247 } 248 249 if (next.type_tag != ASN1Tag.NO_OBJECT) 250 throw new X509CRLError("Unknown tag in CRL"); 251 252 tbs_crl.verifyEnd(); 253 } 254 255 256 bool m_throw_on_unknown_critical; 257 Vector!CRLEntry m_revoked; 258 DataStore m_info; 259 }