1 /** 2 * X.509 Certificate Authority 3 * 4 * Copyright: 5 * (C) 1999-2008 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_ca; 12 13 import botan.constants; 14 static if (BOTAN_HAS_X509_CERTIFICATES): 15 16 public import botan.cert.x509.crl_ent; 17 import botan.asn1.asn1_time; 18 import botan.cert.x509.x509cert; 19 import botan.cert.x509.x509_crl; 20 import botan.cert.x509.x509_ext; 21 import botan.pubkey.pkcs8; 22 import botan.pubkey.pubkey; 23 import botan.cert.x509.pkcs10; 24 import botan.asn1.der_enc; 25 import botan.asn1.ber_dec; 26 import botan.math.bigint.bigint; 27 import botan.utils.parsing; 28 import botan.libstate.lookup; 29 import botan.asn1.oids; 30 import botan.cert.x509.key_constraint; 31 import botan.rng.rng; 32 import std.datetime; 33 import std.algorithm; 34 import botan.utils.mem_ops; 35 import memutils.utils; 36 37 alias X509CA = RefCounted!X509CAImpl; 38 39 /** 40 * This class represents X.509 Certificate Authorities (CAs). 41 */ 42 final class X509CAImpl 43 { 44 public: 45 /** 46 * Sign a PKCS#10 Request. 47 * 48 * Params: 49 * req = the request to sign 50 * rng = the rng to use 51 * not_before = the starting time for the certificate 52 * not_after = the expiration time for the certificate 53 * Returns: resulting certificate 54 */ 55 X509Certificate signRequest(in PKCS10Request req, 56 RandomNumberGenerator rng, 57 in X509Time not_before, 58 in X509Time not_after) 59 { 60 KeyConstraints constraints; 61 if (req.isCA()) 62 constraints = KeyConstraints.KEY_CERT_SIGN | KeyConstraints.CRL_SIGN; 63 else 64 { 65 Unique!PublicKey key = req.subjectPublicKey(); 66 constraints = findConstraints(*key, req.constraints()); 67 } 68 69 X509Extensions extensions = X509Extensions(true); 70 71 extensions.add(new BasicConstraints(req.isCA(), req.pathLimit()), true); 72 73 extensions.add(new KeyUsage(constraints), true); 74 75 extensions.add(new AuthorityKeyID(m_cert.subjectKeyId().dup)); 76 extensions.add(new SubjectKeyID(req.rawPublicKey().dup)); 77 78 extensions.add(new SubjectAlternativeName(req.subjectAltName())); 79 80 extensions.add(new ExtendedKeyUsage(req.exConstraints())); 81 82 return makeCert(m_signer, rng, m_ca_sig_algo, 83 req.rawPublicKey(), 84 not_before, not_after, 85 m_cert.subjectDn(), req.subjectDn(), 86 extensions); 87 } 88 89 /** 90 * Get the certificate of this CA. 91 * Returns: CA certificate 92 */ 93 const(X509Certificate) caCertificate() const 94 { 95 return m_cert; 96 } 97 98 /** 99 * Create a new and empty CRL for this CA. 100 * 101 * Params: 102 * rng = the random number generator to use 103 * next_update = the time to set in next update in seconds 104 * as the offset from the current time 105 * Returns: new CRL 106 */ 107 X509CRL newCRL(RandomNumberGenerator rng, Duration next_update = 0.seconds) const 108 { 109 Vector!CRLEntry empty; 110 return makeCRL(empty, 1, next_update, rng); 111 } 112 113 /** 114 * Create a new CRL by with additional entries. 115 * 116 * Params: 117 * crl = the last CRL of this CA to add the new entries to 118 * new_revoked = contains the new CRL entries to be added to the CRL 119 * rng = the random number generator to use 120 * next_update = the time to set in next update in $(D Duration) from now 121 */ 122 X509CRL updateCRL()(in X509CRL crl, 123 auto const ref Vector!CRLEntry new_revoked, 124 RandomNumberGenerator rng, 125 Duration next_update = 0.seconds) const 126 { 127 128 Vector!CRLEntry revoked = crl.getRevoked().dup; 129 revoked ~= new_revoked[]; 130 return makeCRL(revoked, crl.crlNumber() + 1, next_update, rng); 131 } 132 133 134 /** 135 * Interface for creating new certificates 136 * Params: 137 * signer = a signing object 138 * rng = a random number generator 139 * sig_algo = the signature algorithm identifier 140 * pub_key = the serialized public key 141 * not_before = the start time of the certificate 142 * not_after = the end time of the certificate 143 * issuer_dn = the DN of the issuer 144 * subject_dn = the DN of the subject 145 * extensions = an optional list of certificate extensions 146 * Returns:s newly minted certificate 147 */ 148 static X509Certificate makeCert(ALLOC)(ref PKSigner signer, 149 RandomNumberGenerator rng, 150 in AlgorithmIdentifier sig_algo, 151 auto const ref Vector!(ubyte, ALLOC) pub_key, 152 in X509Time not_before, 153 in X509Time not_after, 154 in X509DN issuer_dn, 155 in X509DN subject_dn, 156 in X509Extensions extensions) 157 { 158 __gshared immutable size_t X509_CERT_VERSION = 3; 159 __gshared immutable size_t SERIAL_BITS = 128; 160 161 BigInt serial_no = BigInt(rng, SERIAL_BITS); 162 auto contents = 163 DEREncoder().startCons(ASN1Tag.SEQUENCE) 164 .startExplicit(0) 165 .encode(X509_CERT_VERSION-1) 166 .endExplicit() 167 168 .encode(serial_no) 169 170 .encode(sig_algo) 171 .encode(issuer_dn) 172 173 .startCons(ASN1Tag.SEQUENCE) 174 .encode(not_before) 175 .encode(not_after) 176 .endCons() 177 178 .encode(subject_dn) 179 .rawBytes(pub_key) 180 181 .startExplicit(3) 182 .startCons(ASN1Tag.SEQUENCE) 183 .encode(extensions) 184 .endCons() 185 .endExplicit() 186 .endCons() 187 .getContents(); 188 Vector!ubyte cert = X509Object.makeSigned(signer, rng, sig_algo, contents.move); 189 190 return X509Certificate(cert.move); 191 } 192 193 /** 194 * Create a new CA object. Load the certificate and private key 195 * Params: 196 * c = the certificate of the CA 197 * key = the private key of the CA 198 * hash_fn = name of a hash function to use for signing 199 */ 200 this(X509Certificate c, 201 in PrivateKey key, 202 in string hash_fn) 203 { 204 m_ca_sig_algo = AlgorithmIdentifier(); 205 m_cert = c; 206 if (!m_cert.isCACert()) 207 throw new InvalidArgument("X509_CA: This certificate is not for a CA"); 208 209 m_signer = chooseSigFormat(key, hash_fn, m_ca_sig_algo); 210 } 211 212 /* 213 * X509_CA Destructor 214 */ 215 ~this() 216 { 217 } 218 private: 219 /* 220 * Create a CRL 221 */ 222 X509CRL makeCRL(const ref Vector!CRLEntry revoked, 223 uint crl_number, Duration next_update, 224 RandomNumberGenerator rng) const 225 { 226 __gshared immutable size_t X509_CRL_VERSION = 2; 227 228 if (next_update == 0.seconds) 229 next_update = 7.days; 230 231 // Totally stupid: ties encoding logic to the return of std::time!! 232 auto current_time = Clock.currTime(UTC()); 233 auto expire_time = current_time + next_update; 234 235 X509Extensions extensions = X509Extensions(true); 236 extensions.add(new AuthorityKeyID(m_cert.subjectKeyId().dup)); 237 extensions.add(new CRLNumber(crl_number)); 238 239 auto contents = 240 DEREncoder().startCons(ASN1Tag.SEQUENCE) 241 .encode(X509_CRL_VERSION-1) 242 .encode(m_ca_sig_algo) 243 .encode(m_cert.issuerDn()) 244 .encode(X509Time(current_time)) 245 .encode(X509Time(expire_time)) 246 .encodeIf (revoked.length > 0, 247 DEREncoder() 248 .startCons(ASN1Tag.SEQUENCE) 249 .encodeList(revoked) 250 .endCons() 251 ) 252 .startExplicit(0) 253 .startCons(ASN1Tag.SEQUENCE) 254 .encode(extensions) 255 .endCons() 256 .endExplicit() 257 .endCons() 258 .getContents(); 259 260 Vector!ubyte crl = X509Object.makeSigned(*cast(PKSigner*)&m_signer, rng, m_ca_sig_algo, contents); 261 262 return X509CRL(crl); 263 } 264 265 266 AlgorithmIdentifier m_ca_sig_algo; 267 X509Certificate m_cert; 268 PKSigner m_signer; 269 } 270 271 /** 272 * Choose the default signature format for a certain public key signature 273 * scheme. 274 * Params: 275 * key = will be the key to choose a padding scheme for 276 * hash_fn = is the desired hash function 277 * sig_algo = will be set to the chosen scheme 278 * Returns: A PKSigner object for generating signatures 279 */ 280 PKSigner chooseSigFormat(in PrivateKey key, 281 in string hash_fn, 282 ref AlgorithmIdentifier sig_algo) 283 { 284 import std.array : Appender; 285 Appender!string padding; 286 287 const string algo_name = key.algoName; 288 289 const HashFunction proto_hash = retrieveHash(hash_fn); 290 if (!proto_hash) 291 throw new AlgorithmNotFound(hash_fn); 292 293 if (key.maxInputBits() < proto_hash.outputLength*8) 294 throw new InvalidArgument("Key is too small for chosen hash function"); 295 296 if (algo_name == "RSA") 297 padding ~= "EMSA3"; 298 else if (algo_name == "DSA") 299 padding ~= "EMSA1"; 300 else if (algo_name == "ECDSA") 301 padding ~= "EMSA1_BSI"; 302 else 303 throw new InvalidArgument("Unknown X.509 signing key type: " ~ algo_name); 304 305 SignatureFormat format = (key.messageParts() > 1) ? DER_SEQUENCE : IEEE_1363; 306 307 padding ~= '(' ~ proto_hash.name ~ ')'; 308 309 sig_algo.oid = OIDS.lookup(algo_name ~ "/" ~ padding.data); 310 auto algo_id = key.algorithmIdentifier(); 311 sig_algo.parameters = algo_id.parameters; 312 //logTrace("chooseSigFormat Sig algo: ", sig_algo.toString()); 313 return PKSigner(key, padding.data, format); 314 }