1 module hunt.security.x509.X509CRLImpl; 2 3 import hunt.security.Key; 4 import hunt.security.Principal; 5 import hunt.security.Provider; 6 7 import hunt.security.cert.Certificate; 8 import hunt.security.cert.X509CRL; 9 import hunt.security.cert.X509CRLEntry; 10 import hunt.security.cert.X509Certificate; 11 12 import hunt.security.x500.X500Principal; 13 import hunt.security.x509.AlgorithmId; 14 import hunt.security.x509.CRLExtensions; 15 import hunt.security.x509.Extension; 16 import hunt.security.x509.KeyIdentifier; 17 import hunt.security.x509.X500Name; 18 import hunt.security.x509.X509CRLEntryImpl; 19 import hunt.security.x509.X509Factory; 20 21 import hunt.security.util.DerEncoder; 22 import hunt.security.util.DerValue; 23 import hunt.security.util.DerInputStream; 24 import hunt.security.util.DerOutputStream; 25 import hunt.security.util.ObjectIdentifier; 26 27 import hunt.collection; 28 import hunt.Exceptions; 29 import hunt.stream.Common; 30 import hunt.text.Common; 31 import hunt.util.Common; 32 import hunt.util.StringBuilder; 33 34 import std.algorithm; 35 import std.bigint; 36 import std.conv; 37 import std.datetime; 38 39 alias BigInteger = BigInt; 40 41 /** 42 * <p> 43 * An implementation for X509 CRL (Certificate Revocation List). 44 * <p> 45 * The X.509 v2 CRL format is described below in ASN.1: 46 * <pre> 47 * CertificateList ::= SEQUENCE { 48 * tbsCertList TBSCertList, 49 * signatureAlgorithm AlgorithmIdentifier, 50 * signature BIT STRING } 51 * </pre> 52 * More information can be found in 53 * <a href="http://www.ietf.org/rfc/rfc3280.txt">RFC 3280: Internet X.509 54 * Public Key Infrastructure Certificate and CRL Profile</a>. 55 * <p> 56 * The ASN.1 definition of <code>tbsCertList</code> is: 57 * <pre> 58 * TBSCertList ::= SEQUENCE { 59 * version Version OPTIONAL, 60 * -- if present, must be v2 61 * signature AlgorithmIdentifier, 62 * issuer Name, 63 * thisUpdate ChoiceOfTime, 64 * nextUpdate ChoiceOfTime OPTIONAL, 65 * revokedCertificates SEQUENCE OF SEQUENCE { 66 * userCertificate CertificateSerialNumber, 67 * revocationDate ChoiceOfTime, 68 * crlEntryExtensions Extensions OPTIONAL 69 * -- if present, must be v2 70 * } OPTIONAL, 71 * crlExtensions [0] EXPLICIT Extensions OPTIONAL 72 * -- if present, must be v2 73 * } 74 * </pre> 75 * 76 * @author Hemma Prafullchandra 77 * @see X509CRL 78 */ 79 class X509CRLImpl : X509CRL , DerEncoder { 80 81 // CRL data, and its envelope 82 private byte[] signedCRL = null; // DER encoded crl 83 private byte[] signature = null; // raw signature bits 84 private byte[] tbsCertList = null; // DER encoded "to-be-signed" CRL 85 private AlgorithmId sigAlgId = null; // sig alg in CRL 86 87 // crl information 88 private int _version; 89 private AlgorithmId infoSigAlgId; // sig alg in "to-be-signed" crl 90 private X500Name issuer = null; 91 private X500Principal issuerPrincipal = null; 92 private Date thisUpdate; 93 private Date nextUpdate; 94 private Map!(X509IssuerSerial,X509CRLEntry) revokedMap; 95 private List!X509CRLEntry revokedList; 96 private CRLExtensions extensions = null; 97 private enum bool isExplicit = true; 98 private enum long YR_2050 = 2524636800000L; 99 100 private bool readOnly = false; 101 102 /** 103 * PublicKey that has previously been used to successfully verify 104 * the signature of this CRL. Null if the CRL has not 105 * yet been verified (successfully). 106 */ 107 private PublicKey verifiedPublicKey; 108 /** 109 * If verifiedPublicKey is not null, name of the provider used to 110 * successfully verify the signature of this CRL, or the 111 * empty string if no provider was explicitly specified. 112 */ 113 private string verifiedProvider; 114 115 /** 116 * Not to be used. As it would lead to cases of uninitialized 117 * CRL objects. 118 */ 119 private this() { 120 revokedMap = new TreeMap!(X509IssuerSerial,X509CRLEntry)(); 121 revokedList = new LinkedList!X509CRLEntry(); 122 } 123 124 /** 125 * Unmarshals an X.509 CRL from its encoded form, parsing the encoded 126 * bytes. This form of constructor is used by agents which 127 * need to examine and use CRL contents. Note that the buffer 128 * must include only one CRL, and no "garbage" may be left at 129 * the end. 130 * 131 * @param crlData the encoded bytes, with no trailing padding. 132 * @exception CRLException on parsing errors. 133 */ 134 this(byte[] crlData) { 135 this(); 136 try { 137 parse(new DerValue(crlData)); 138 } catch (IOException e) { 139 signedCRL = null; 140 throw new CRLException("Parsing error: " ~ e.msg); 141 } 142 } 143 144 /** 145 * Unmarshals an X.509 CRL from an DER value. 146 * 147 * @param val a DER value holding at least one CRL 148 * @exception CRLException on parsing errors. 149 */ 150 this(DerValue val) { 151 this(); 152 try { 153 parse(val); 154 } catch (IOException e) { 155 signedCRL = null; 156 throw new CRLException("Parsing error: " ~ e.msg); 157 } 158 } 159 160 /** 161 * Unmarshals an X.509 CRL from an input stream. Only one CRL 162 * is expected at the end of the input stream. 163 * 164 * @param inStrm an input stream holding at least one CRL 165 * @exception CRLException on parsing errors. 166 */ 167 this(InputStream inStrm) { 168 this(); 169 try { 170 parse(new DerValue(inStrm)); 171 } catch (IOException e) { 172 signedCRL = null; 173 throw new CRLException("Parsing error: " ~ e.msg); 174 } 175 } 176 177 /** 178 * Initial CRL constructor, no revoked certs, and no extensions. 179 * 180 * @param issuer the name of the CA issuing this CRL. 181 * @param thisUpdate the Date of this issue. 182 * @param nextUpdate the Date of the next CRL. 183 */ 184 this(X500Name issuer, Date thisDate, Date nextDate) { 185 this(); 186 this.issuer = issuer; 187 this.thisUpdate = thisDate; 188 this.nextUpdate = nextDate; 189 } 190 191 /** 192 * CRL constructor, revoked certs, no extensions. 193 * 194 * @param issuer the name of the CA issuing this CRL. 195 * @param thisUpdate the Date of this issue. 196 * @param nextUpdate the Date of the next CRL. 197 * @param badCerts the array of CRL entries. 198 * 199 * @exception CRLException on parsing/construction errors. 200 */ 201 this(X500Name issuer, Date thisDate, Date nextDate, 202 X509CRLEntry[] badCerts) 203 { 204 this(); 205 this.issuer = issuer; 206 this.thisUpdate = thisDate; 207 this.nextUpdate = nextDate; 208 if (badCerts !is null) { 209 X500Principal crlIssuer = getIssuerX500Principal(); 210 X500Principal badCertIssuer = crlIssuer; 211 for (int i = 0; i < badCerts.length; i++) { 212 X509CRLEntryImpl badCert = cast(X509CRLEntryImpl)badCerts[i]; 213 try { 214 badCertIssuer = getCertIssuer(badCert, badCertIssuer); 215 } catch (IOException ioe) { 216 throw new CRLException("", ioe); 217 } 218 badCert.setCertificateIssuer(crlIssuer, badCertIssuer); 219 X509IssuerSerial issuerSerial = new X509IssuerSerial 220 (badCertIssuer, badCert.getSerialNumber()); 221 this.revokedMap.put(issuerSerial, cast(X509CRLEntry)badCert); 222 this.revokedList.add(cast(X509CRLEntry)badCert); 223 if (badCert.hasExtensions()) { 224 this._version = 1; 225 } 226 } 227 } 228 } 229 230 /** 231 * CRL constructor, revoked certs and extensions. 232 * 233 * @param issuer the name of the CA issuing this CRL. 234 * @param thisUpdate the Date of this issue. 235 * @param nextUpdate the Date of the next CRL. 236 * @param badCerts the array of CRL entries. 237 * @param crlExts the CRL extensions. 238 * 239 * @exception CRLException on parsing/construction errors. 240 */ 241 this(X500Name issuer, Date thisDate, Date nextDate, 242 X509CRLEntry[] badCerts, CRLExtensions crlExts) 243 { 244 this(issuer, thisDate, nextDate, badCerts); 245 if (crlExts !is null) { 246 this.extensions = crlExts; 247 this._version = 1; 248 } 249 } 250 251 /** 252 * Compares this CRL for equality with the given 253 * object. If the {@code other} object is an 254 * {@code instanceof} {@code X509CRL}, then 255 * its encoded form is retrieved and compared with the 256 * encoded form of this CRL. 257 * 258 * @param other the object to test for equality with this CRL. 259 * 260 * @return true iff the encoded forms of the two CRLs 261 * match, false otherwise. 262 */ 263 override bool opEquals(Object other) { 264 if (other is null) { 265 return false; 266 } 267 if (this is other) { 268 return true; 269 } 270 X509CRL ot = cast(X509CRL)other; 271 try { 272 byte[] thisCRL = X509CRLImpl.getEncodedInternal(this); 273 byte[] otherCRL = X509CRLImpl.getEncodedInternal(ot); 274 275 return thisCRL == otherCRL; 276 } catch (CRLException e) { 277 return false; 278 } 279 } 280 281 /** 282 * Returns a hashcode value for this CRL from its 283 * encoded form. 284 * 285 * @return the hashcode value. 286 */ 287 override size_t toHash() @trusted nothrow { 288 size_t retval = 0; 289 try { 290 byte[] crlData; 291 crlData = X509CRLImpl.getEncodedInternal(this); 292 for (size_t i = 1; i < crlData.length; i++) { 293 retval += crlData[i] * i; 294 } 295 return retval; 296 } catch (Exception e) { 297 return retval; 298 } 299 } 300 301 /** 302 * Returned the encoding as an uncloned byte array. Callers must 303 * guarantee that they neither modify it nor expose it to untrusted 304 * code. 305 */ 306 byte[] getEncodedInternal() { 307 if (signedCRL is null) { 308 throw new CRLException("Null CRL to encode"); 309 } 310 return signedCRL; 311 } 312 313 /** 314 * Returns the ASN.1 DER encoded form of this CRL. 315 * 316 * @exception CRLException if an encoding error occurs. 317 */ 318 override byte[] getEncoded() { 319 return getEncodedInternal().dup; 320 } 321 322 /** 323 * Encodes the "to-be-signed" CRL to the OutputStream. 324 * 325 * @param out the OutputStream to write to. 326 * @exception CRLException on encoding errors. 327 */ 328 void encodeInfo(OutputStream outputStream) { 329 try { 330 DerOutputStream tmp = new DerOutputStream(); 331 DerOutputStream rCerts = new DerOutputStream(); 332 DerOutputStream seq = new DerOutputStream(); 333 334 if (_version != 0) // v2 crl encode version 335 tmp.putInteger(_version); 336 infoSigAlgId.encode(tmp); 337 if ((_version == 0) && (issuer.toString() is null)) 338 throw new CRLException("Null Issuer DN not allowed in v1 CRL"); 339 issuer.encode(tmp); 340 341 // if (thisUpdate.getTime() < YR_2050) 342 // tmp.putUTCTime(thisUpdate); 343 // else 344 // tmp.putGeneralizedTime(thisUpdate); 345 346 // if (nextUpdate !is null) { 347 // if (nextUpdate.getTime() < YR_2050) 348 // tmp.putUTCTime(nextUpdate); 349 // else 350 // tmp.putGeneralizedTime(nextUpdate); 351 // } 352 implementationMissing(); 353 354 if (!revokedList.isEmpty()) { 355 foreach (X509CRLEntry entry ; revokedList) { 356 (cast(X509CRLEntryImpl)entry).encode(rCerts); 357 } 358 tmp.write(DerValue.tag_Sequence, rCerts); 359 } 360 361 if (extensions !is null) 362 extensions.encode(tmp, isExplicit); 363 364 seq.write(DerValue.tag_Sequence, tmp); 365 366 tbsCertList = seq.toByteArray(); 367 outputStream.write(tbsCertList); 368 } catch (IOException e) { 369 throw new CRLException("Encoding error: " ~ e.msg); 370 } 371 } 372 373 /** 374 * Verifies that this CRL was signed using the 375 * private key that corresponds to the given key. 376 * 377 * @param key the PublicKey used to carry out the verification. 378 * 379 * @exception NoSuchAlgorithmException on unsupported signature 380 * algorithms. 381 * @exception InvalidKeyException on incorrect key. 382 * @exception NoSuchProviderException if there's no default provider. 383 * @exception SignatureException on signature errors. 384 * @exception CRLException on encoding errors. 385 */ 386 override void verify(PublicKey key) { 387 verify(key, ""); 388 } 389 390 /** 391 * Verifies that this CRL was signed using the 392 * private key that corresponds to the given key, 393 * and that the signature verification was computed by 394 * the given provider. 395 * 396 * @param key the PublicKey used to carry out the verification. 397 * @param sigProvider the name of the signature provider. 398 * 399 * @exception NoSuchAlgorithmException on unsupported signature 400 * algorithms. 401 * @exception InvalidKeyException on incorrect key. 402 * @exception NoSuchProviderException on incorrect provider. 403 * @exception SignatureException on signature errors. 404 * @exception CRLException on encoding errors. 405 */ 406 override void verify(PublicKey key, string sigProvider) { 407 if (sigProvider is null) { 408 sigProvider = ""; 409 } 410 // if ((verifiedPublicKey !is null) && verifiedPublicKey.equals(key)) { 411 // // this CRL has already been successfully verified using 412 // // this key. Make sure providers match, too. 413 // if (sigProvider.equals(verifiedProvider)) { 414 // return; 415 // } 416 // } 417 // if (signedCRL is null) { 418 // throw new CRLException("Uninitialized CRL"); 419 // } 420 // Signature sigVerf = null; 421 // if (sigProvider.length == 0) { 422 // sigVerf = Signature.getInstance(sigAlgId.getName()); 423 // } else { 424 // sigVerf = Signature.getInstance(sigAlgId.getName(), sigProvider); 425 // } 426 // sigVerf.initVerify(key); 427 428 // if (tbsCertList is null) { 429 // throw new CRLException("Uninitialized CRL"); 430 // } 431 432 // sigVerf.update(tbsCertList, 0, tbsCertList.length); 433 434 // if (!sigVerf.verify(signature)) { 435 // throw new SignatureException("Signature does not match."); 436 // } 437 // verifiedPublicKey = key; 438 // verifiedProvider = sigProvider; 439 implementationMissing(); 440 } 441 442 /** 443 * Verifies that this CRL was signed using the 444 * private key that corresponds to the given key, 445 * and that the signature verification was computed by 446 * the given provider. Note that the specified Provider object 447 * does not have to be registered in the provider list. 448 * 449 * @param key the PublicKey used to carry out the verification. 450 * @param sigProvider the signature provider. 451 * 452 * @exception NoSuchAlgorithmException on unsupported signature 453 * algorithms. 454 * @exception InvalidKeyException on incorrect key. 455 * @exception SignatureException on signature errors. 456 * @exception CRLException on encoding errors. 457 */ 458 override void verify(PublicKey key, Provider sigProvider) { 459 460 if (signedCRL is null) { 461 throw new CRLException("Uninitialized CRL"); 462 } 463 // Signature sigVerf = null; 464 // if (sigProvider is null) { 465 // sigVerf = Signature.getInstance(sigAlgId.getName()); 466 // } else { 467 // sigVerf = Signature.getInstance(sigAlgId.getName(), sigProvider); 468 // } 469 // sigVerf.initVerify(key); 470 471 // if (tbsCertList is null) { 472 // throw new CRLException("Uninitialized CRL"); 473 // } 474 475 // sigVerf.update(tbsCertList, 0, tbsCertList.length); 476 477 // if (!sigVerf.verify(signature)) { 478 // throw new SignatureException("Signature does not match."); 479 // } 480 implementationMissing(); 481 verifiedPublicKey = key; 482 } 483 484 /** 485 * This static method is the default implementation of the 486 * verify(PublicKey key, Provider sigProvider) method in X509CRL. 487 * Called from java.security.cert.X509CRL.verify(PublicKey key, 488 * Provider sigProvider) 489 */ 490 static void verify(X509CRL crl, PublicKey key, 491 Provider sigProvider) { 492 crl.verify(key, sigProvider); 493 } 494 495 /** 496 * Encodes an X.509 CRL, and signs it using the given key. 497 * 498 * @param key the private key used for signing. 499 * @param algorithm the name of the signature algorithm used. 500 * 501 * @exception NoSuchAlgorithmException on unsupported signature 502 * algorithms. 503 * @exception InvalidKeyException on incorrect key. 504 * @exception NoSuchProviderException on incorrect provider. 505 * @exception SignatureException on signature errors. 506 * @exception CRLException if any mandatory data was omitted. 507 */ 508 void sign(PrivateKey key, string algorithm) { 509 sign(key, algorithm, null); 510 } 511 512 /** 513 * Encodes an X.509 CRL, and signs it using the given key. 514 * 515 * @param key the private key used for signing. 516 * @param algorithm the name of the signature algorithm used. 517 * @param provider the name of the provider. 518 * 519 * @exception NoSuchAlgorithmException on unsupported signature 520 * algorithms. 521 * @exception InvalidKeyException on incorrect key. 522 * @exception NoSuchProviderException on incorrect provider. 523 * @exception SignatureException on signature errors. 524 * @exception CRLException if any mandatory data was omitted. 525 */ 526 void sign(PrivateKey key, string algorithm, string provider) { 527 try { 528 if (readOnly) 529 throw new CRLException("cannot over-write existing CRL"); 530 // Signature sigEngine = null; 531 // if ((provider is null) || (provider.length == 0)) 532 // sigEngine = Signature.getInstance(algorithm); 533 // else 534 // sigEngine = Signature.getInstance(algorithm, provider); 535 536 // sigEngine.initSign(key); 537 538 // // in case the name is reset 539 // sigAlgId = AlgorithmId.get(sigEngine.getAlgorithm()); 540 // infoSigAlgId = sigAlgId; 541 542 // DerOutputStream ot = new DerOutputStream(); 543 // DerOutputStream tmp = new DerOutputStream(); 544 545 // // encode crl info 546 // encodeInfo(tmp); 547 548 // // encode algorithm identifier 549 // sigAlgId.encode(tmp); 550 551 // // Create and encode the signature itself. 552 // sigEngine.update(tbsCertList, 0, tbsCertList.length); 553 // signature = sigEngine.sign(); 554 // tmp.putBitString(signature); 555 556 // // Wrap the signed data in a SEQUENCE { data, algorithm, sig } 557 // ot.write(DerValue.tag_Sequence, tmp); 558 // signedCRL = ot.toByteArray(); 559 // readOnly = true; 560 561 } catch (IOException e) { 562 throw new CRLException("Error while encoding data: " ~ 563 e.msg); 564 } 565 implementationMissing(); 566 } 567 568 /** 569 * Returns a printable string of this CRL. 570 * 571 * @return value of this CRL in a printable form. 572 */ 573 override string toString() { 574 StringBuilder sb = new StringBuilder(); 575 sb.append("X.509 CRL v" ~ (_version+1).to!string() ~ "\n"); 576 if (sigAlgId !is null) 577 sb.append("Signature Algorithm: " ~ sigAlgId.toString() ~ 578 ", OID=" ~ (sigAlgId.getOID()).toString() ~ "\n"); 579 if (issuer !is null) 580 sb.append("Issuer: " ~ issuer.toString() ~ "\n"); 581 if (thisUpdate != Date.init) 582 sb.append("\nThis Update: " ~ thisUpdate.toString() ~ "\n"); 583 if (nextUpdate != Date.init) 584 sb.append("Next Update: " ~ nextUpdate.toString() ~ "\n"); 585 if (revokedList.isEmpty()) 586 sb.append("\nNO certificates have been revoked\n"); 587 else { 588 sb.append("\nRevoked Certificates: " ~ revokedList.size().to!string()); 589 int i = 1; 590 foreach (X509CRLEntry entry; revokedList) { 591 sb.append("\n[" ~ to!string(i++) ~ "] " ~ entry.toString()); 592 } 593 } 594 implementationMissing(); 595 // if (extensions !is null) { 596 // Collection!Extension allExts = extensions.getAllExtensions(); 597 // Object[] objs = allExts.toArray(); 598 // sb.append("\nCRL Extensions: " ~ objs.length); 599 // for (int i = 0; i < objs.length; i++) { 600 // sb.append("\n[" ~ (i+1) ~ "]: "); 601 // Extension ext = cast(Extension)objs[i]; 602 // try { 603 // if (OIDMap.getClass(ext.getExtensionId()) is null) { 604 // sb.append(ext.toString()); 605 // byte[] extValue = ext.getExtensionValue(); 606 // if (extValue !is null) { 607 // DerOutputStream ot = new DerOutputStream(); 608 // ot.putOctetString(extValue); 609 // extValue = ot.toByteArray(); 610 // HexDumpEncoder enc = new HexDumpEncoder(); 611 // sb.append("Extension unknown: " 612 // ~ "DER encoded OCTET string =\n" 613 // + enc.encodeBuffer(extValue) ~ "\n"); 614 // } 615 // } else 616 // sb.append(ext.toString()); // sub-class exists 617 // } catch (Exception e) { 618 // sb.append(", Error parsing this extension"); 619 // } 620 // } 621 // } 622 // if (signature !is null) { 623 // HexDumpEncoder encoder = new HexDumpEncoder(); 624 // sb.append("\nSignature:\n" ~ encoder.encodeBuffer(signature) 625 // ~ "\n"); 626 // } else 627 // sb.append("NOT signed yet\n"); 628 return sb.toString(); 629 } 630 631 /** 632 * Checks whether the given certificate is on this CRL. 633 * 634 * @param cert the certificate to check for. 635 * @return true if the given certificate is on this CRL, 636 * false otherwise. 637 */ 638 override bool isRevoked(Certificate cert) { 639 if (revokedMap.isEmpty()) { 640 return false; 641 } 642 X509Certificate xcert = cast(X509Certificate) cert; 643 if(xcert is null) return false; 644 645 X509IssuerSerial issuerSerial = new X509IssuerSerial(xcert); 646 return revokedMap.containsKey(issuerSerial); 647 } 648 649 /** 650 * Gets the version number from this CRL. 651 * The ASN.1 definition for this is: 652 * <pre> 653 * Version ::= INTEGER { v1(0), v2(1), v3(2) } 654 * -- v3 does not apply to CRLs but appears for consistency 655 * -- with definition of Version for certs 656 * </pre> 657 * @return the version number, i.e. 1 or 2. 658 */ 659 override int getVersion() { 660 return _version+1; 661 } 662 663 /** 664 * Gets the issuer distinguished name from this CRL. 665 * The issuer name identifies the entity who has signed (and 666 * issued the CRL). The issuer name field contains an 667 * X.500 distinguished name (DN). 668 * The ASN.1 definition for this is: 669 * <pre> 670 * issuer Name 671 * 672 * Name ::= CHOICE { RDNSequence } 673 * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName 674 * RelativeDistinguishedName ::= 675 * SET OF AttributeValueAssertion 676 * 677 * AttributeValueAssertion ::= SEQUENCE { 678 * AttributeType, 679 * AttributeValue } 680 * AttributeType ::= OBJECT IDENTIFIER 681 * AttributeValue ::= ANY 682 * </pre> 683 * The Name describes a hierarchical name composed of attributes, 684 * such as country name, and corresponding values, such as US. 685 * The type of the component AttributeValue is determined by the 686 * AttributeType; in general it will be a directoryString. 687 * A directoryString is usually one of PrintableString, 688 * TeletexString or UniversalString. 689 * @return the issuer name. 690 */ 691 override Principal getIssuerDN() { 692 return cast(Principal)issuer; 693 } 694 695 /** 696 * Return the issuer as X500Principal. Overrides method in X509CRL 697 * to provide a slightly more efficient version. 698 */ 699 override X500Principal getIssuerX500Principal() { 700 if (issuerPrincipal is null) { 701 issuerPrincipal = issuer.asX500Principal(); 702 } 703 return issuerPrincipal; 704 } 705 706 /** 707 * Gets the thisUpdate date from the CRL. 708 * The ASN.1 definition for this is: 709 * 710 * @return the thisUpdate date from the CRL. 711 */ 712 override Date getThisUpdate() { 713 return (Date(thisUpdate.dayOfGregorianCal)); 714 } 715 716 /** 717 * Gets the nextUpdate date from the CRL. 718 * 719 * @return the nextUpdate date from the CRL, or null if 720 * not present. 721 */ 722 override Date getNextUpdate() { 723 if (nextUpdate == Date.init) 724 return Date.init; 725 return (Date(nextUpdate.dayOfGregorianCal())); 726 } 727 728 /** 729 * Gets the CRL entry with the given serial number from this CRL. 730 * 731 * @return the entry with the given serial number, or <code>null</code> if 732 * no such entry exists in the CRL. 733 * @see X509CRLEntry 734 */ 735 override X509CRLEntry getRevokedCertificate(BigInteger serialNumber) { 736 if (revokedMap.isEmpty()) { 737 return null; 738 } 739 // assume this is a direct CRL entry (cert and CRL issuer are the same) 740 X509IssuerSerial issuerSerial = new X509IssuerSerial 741 (getIssuerX500Principal(), serialNumber); 742 return revokedMap.get(issuerSerial); 743 } 744 745 /** 746 * Gets the CRL entry for the given certificate. 747 */ 748 override X509CRLEntry getRevokedCertificate(X509Certificate cert) { 749 if (revokedMap.isEmpty()) { 750 return null; 751 } 752 X509IssuerSerial issuerSerial = new X509IssuerSerial(cert); 753 return revokedMap.get(issuerSerial); 754 } 755 756 /** 757 * Gets all the revoked certificates from the CRL. 758 * A Set of X509CRLEntry. 759 * 760 * @return all the revoked certificates or <code>null</code> if there are 761 * none. 762 * @see X509CRLEntry 763 */ 764 override Set!X509CRLEntry getRevokedCertificates() { 765 if (revokedList.isEmpty()) { 766 return null; 767 } else { 768 return new TreeSet!X509CRLEntry(revokedList); 769 } 770 } 771 772 /** 773 * Gets the DER encoded CRL information, the 774 * <code>tbsCertList</code> from this CRL. 775 * This can be used to verify the signature independently. 776 * 777 * @return the DER encoded CRL information. 778 * @exception CRLException on encoding errors. 779 */ 780 override byte[] getTBSCertList() { 781 if (tbsCertList is null) 782 throw new CRLException("Uninitialized CRL"); 783 return tbsCertList.dup; 784 } 785 786 /** 787 * Gets the raw Signature bits from the CRL. 788 * 789 * @return the signature. 790 */ 791 override byte[] getSignature() { 792 if (signature is null) 793 return null; 794 return signature.dup; 795 } 796 797 /** 798 * Gets the signature algorithm name for the CRL 799 * signature algorithm. For example, the string "SHA1withDSA". 800 * The ASN.1 definition for this is: 801 * <pre> 802 * AlgorithmIdentifier ::= SEQUENCE { 803 * algorithm OBJECT IDENTIFIER, 804 * parameters ANY DEFINED BY algorithm OPTIONAL } 805 * -- contains a value of the type 806 * -- registered for use with the 807 * -- algorithm object identifier value 808 * </pre> 809 * 810 * @return the signature algorithm name. 811 */ 812 override string getSigAlgName() { 813 if (sigAlgId is null) 814 return null; 815 return sigAlgId.getName(); 816 } 817 818 /** 819 * Gets the signature algorithm OID string from the CRL. 820 * An OID is represented by a set of positive whole number separated 821 * by ".", that means,<br> 822 * <positive whole number>.<positive whole number>.<...> 823 * For example, the string "1.2.840.10040.4.3" identifies the SHA-1 824 * with DSA signature algorithm defined in 825 * <a href="http://www.ietf.org/rfc/rfc3279.txt">RFC 3279: Algorithms and 826 * Identifiers for the Internet X.509 Public Key Infrastructure Certificate 827 * and CRL Profile</a>. 828 * 829 * @return the signature algorithm oid string. 830 */ 831 override string getSigAlgOID() { 832 if (sigAlgId is null) 833 return null; 834 ObjectIdentifier oid = sigAlgId.getOID(); 835 return oid.toString(); 836 } 837 838 /** 839 * Gets the DER encoded signature algorithm parameters from this 840 * CRL's signature algorithm. In most cases, the signature 841 * algorithm parameters are null, the parameters are usually 842 * supplied with the Public Key. 843 * 844 * @return the DER encoded signature algorithm parameters, or 845 * null if no parameters are present. 846 */ 847 override byte[] getSigAlgParams() { 848 if (sigAlgId is null) 849 return null; 850 try { 851 return sigAlgId.getEncodedParams(); 852 } catch (IOException e) { 853 return null; 854 } 855 } 856 857 /** 858 * Gets the signature AlgorithmId from the CRL. 859 * 860 * @return the signature AlgorithmId 861 */ 862 AlgorithmId getSigAlgId() { 863 return sigAlgId; 864 } 865 866 /** 867 * return the AuthorityKeyIdentifier, if any. 868 * 869 * @returns AuthorityKeyIdentifier or null 870 * (if no AuthorityKeyIdentifierExtension) 871 * @throws IOException on error 872 */ 873 KeyIdentifier getAuthKeyId() { 874 // AuthorityKeyIdentifierExtension aki = getAuthKeyIdExtension(); 875 // if (aki !is null) { 876 // KeyIdentifier keyId = cast(KeyIdentifier)aki.get(AuthorityKeyIdentifierExtension.KEY_ID); 877 // return keyId; 878 // } else { 879 // return null; 880 // } 881 implementationMissing(); 882 return null; 883 } 884 885 /** 886 * return the AuthorityKeyIdentifierExtension, if any. 887 * 888 * @returns AuthorityKeyIdentifierExtension or null (if no such extension) 889 * @throws IOException on error 890 */ 891 // AuthorityKeyIdentifierExtension getAuthKeyIdExtension() { 892 // Object obj = getExtension(PKIXExtensions.AuthorityKey_Id); 893 // return cast(AuthorityKeyIdentifierExtension)obj; 894 // } 895 896 /** 897 * return the CRLNumberExtension, if any. 898 * 899 * @returns CRLNumberExtension or null (if no such extension) 900 * @throws IOException on error 901 */ 902 // CRLNumberExtension getCRLNumberExtension() { 903 // Object obj = getExtension(PKIXExtensions.CRLNumber_Id); 904 // return cast(CRLNumberExtension)obj; 905 // } 906 907 /** 908 * return the CRL number from the CRLNumberExtension, if any. 909 * 910 * @returns number or null (if no such extension) 911 * @throws IOException on error 912 */ 913 // BigInteger getCRLNumber() { 914 // CRLNumberExtension numExt = getCRLNumberExtension(); 915 // if (numExt !is null) { 916 // BigInteger num = numExt.get(CRLNumberExtension.NUMBER); 917 // return num; 918 // } else { 919 // return null; 920 // } 921 // } 922 923 /** 924 * return the DeltaCRLIndicatorExtension, if any. 925 * 926 * @returns DeltaCRLIndicatorExtension or null (if no such extension) 927 * @throws IOException on error 928 */ 929 // DeltaCRLIndicatorExtension getDeltaCRLIndicatorExtension() 930 // { 931 // Object obj = getExtension(PKIXExtensions.DeltaCRLIndicator_Id); 932 // return cast(DeltaCRLIndicatorExtension)obj; 933 // } 934 935 /** 936 * return the base CRL number from the DeltaCRLIndicatorExtension, if any. 937 * 938 * @returns number or null (if no such extension) 939 * @throws IOException on error 940 */ 941 // BigInteger getBaseCRLNumber() { 942 // DeltaCRLIndicatorExtension dciExt = getDeltaCRLIndicatorExtension(); 943 // if (dciExt !is null) { 944 // BigInteger num = dciExt.get(DeltaCRLIndicatorExtension.NUMBER); 945 // return num; 946 // } else { 947 // return null; 948 // } 949 // } 950 951 /** 952 * return the IssuerAlternativeNameExtension, if any. 953 * 954 * @returns IssuerAlternativeNameExtension or null (if no such extension) 955 * @throws IOException on error 956 */ 957 // IssuerAlternativeNameExtension getIssuerAltNameExtension() { 958 // Object obj = getExtension(PKIXExtensions.IssuerAlternativeName_Id); 959 // return cast(IssuerAlternativeNameExtension)obj; 960 // } 961 962 /** 963 * return the IssuingDistributionPointExtension, if any. 964 * 965 * @returns IssuingDistributionPointExtension or null 966 * (if no such extension) 967 * @throws IOException on error 968 */ 969 // IssuingDistributionPointExtension getIssuingDistributionPointExtension() { 970 // Object obj = getExtension(PKIXExtensions.IssuingDistributionPoint_Id); 971 // return cast(IssuingDistributionPointExtension) obj; 972 // } 973 974 /** 975 * Return true if a critical extension is found that is 976 * not supported, otherwise return false. 977 */ 978 bool hasUnsupportedCriticalExtension() { 979 if (extensions is null) 980 return false; 981 return extensions.hasUnsupportedCriticalExtension(); 982 } 983 984 /** 985 * Gets a Set of the extension(s) marked CRITICAL in the 986 * CRL. In the returned set, each extension is represented by 987 * its OID string. 988 * 989 * @return a set of the extension oid strings in the 990 * CRL that are marked critical. 991 */ 992 Set!string getCriticalExtensionOIDs() { 993 if (extensions is null) { 994 return null; 995 } 996 Set!string extSet = new TreeSet!string(); 997 foreach (Extension ex ; extensions.getAllExtensions()) { 998 if (ex.isCritical()) { 999 extSet.add(ex.getExtensionId().toString()); 1000 } 1001 } 1002 return extSet; 1003 } 1004 1005 /** 1006 * Gets a Set of the extension(s) marked NON-CRITICAL in the 1007 * CRL. In the returned set, each extension is represented by 1008 * its OID string. 1009 * 1010 * @return a set of the extension oid strings in the 1011 * CRL that are NOT marked critical. 1012 */ 1013 Set!string getNonCriticalExtensionOIDs() { 1014 if (extensions is null) { 1015 return null; 1016 } 1017 Set!string extSet = new TreeSet!string(); 1018 foreach (Extension ex ; extensions.getAllExtensions()) { 1019 if (!ex.isCritical()) { 1020 extSet.add(ex.getExtensionId().toString()); 1021 } 1022 } 1023 return extSet; 1024 } 1025 1026 /** 1027 * Gets the DER encoded OCTET string for the extension value 1028 * (<code>extnValue</code>) identified by the passed in oid string. 1029 * The <code>oid</code> string is 1030 * represented by a set of positive whole number separated 1031 * by ".", that means,<br> 1032 * <positive whole number>.<positive whole number>.<...> 1033 * 1034 * @param oid the Object Identifier value for the extension. 1035 * @return the der encoded octet string of the extension value. 1036 */ 1037 byte[] getExtensionValue(string oid) { 1038 if (extensions is null) 1039 return null; 1040 // try { 1041 // string extAlias = OIDMap.getName(new ObjectIdentifier(oid)); 1042 // Extension crlExt = null; 1043 1044 // if (extAlias is null) { // may be unknown 1045 // ObjectIdentifier findOID = new ObjectIdentifier(oid); 1046 // Extension ex = null; 1047 // ObjectIdentifier inCertOID; 1048 // for (Enumeration!Extension e = extensions.getElements(); 1049 // e.hasMoreElements();) { 1050 // ex = e.nextElement(); 1051 // inCertOID = ex.getExtensionId(); 1052 // if (inCertOID.equals(cast(Object)findOID)) { 1053 // crlExt = ex; 1054 // break; 1055 // } 1056 // } 1057 // } else 1058 // crlExt = extensions.get(extAlias); 1059 // if (crlExt is null) 1060 // return null; 1061 // byte[] extData = crlExt.getExtensionValue(); 1062 // if (extData is null) 1063 // return null; 1064 // DerOutputStream ot = new DerOutputStream(); 1065 // ot.putOctetString(extData); 1066 // return ot.toByteArray(); 1067 // } catch (Exception e) { 1068 // return null; 1069 // } 1070 implementationMissing(); 1071 return null; 1072 } 1073 1074 /** 1075 * get an extension 1076 * 1077 * @param oid ObjectIdentifier of extension desired 1078 * @returns Object of type <extension> or null, if not found 1079 * @throws IOException on error 1080 */ 1081 Object getExtension(ObjectIdentifier oid) { 1082 if (extensions is null) 1083 return null; 1084 1085 // XXX Consider cloning this 1086 // return extensions.get(OIDMap.getName(oid)); 1087 implementationMissing(); 1088 return null; 1089 } 1090 1091 /* 1092 * Parses an X.509 CRL, should be used only by constructors. 1093 */ 1094 private void parse(DerValue val) { 1095 // check if can over write the certificate 1096 if (readOnly) 1097 throw new CRLException("cannot over-write existing CRL"); 1098 1099 // if ( val.getData() is null || val.tag != DerValue.tag_Sequence) 1100 // throw new CRLException("Invalid DER-encoded CRL data"); 1101 1102 // signedCRL = val.toByteArray(); 1103 // DerValue[] seq = new DerValue[3]; 1104 1105 // seq[0] = val.data.getDerValue(); 1106 // seq[1] = val.data.getDerValue(); 1107 // seq[2] = val.data.getDerValue(); 1108 1109 // if (val.data.available() != 0) 1110 // throw new CRLException("signed overrun, bytes = " 1111 // + val.data.available()); 1112 1113 // if (seq[0].tag != DerValue.tag_Sequence) 1114 // throw new CRLException("signed CRL fields invalid"); 1115 1116 // sigAlgId = AlgorithmId.parse(seq[1]); 1117 // signature = seq[2].getBitString(); 1118 1119 // if (seq[1].data.available() != 0) 1120 // throw new CRLException("AlgorithmId field overrun"); 1121 1122 // if (seq[2].data.available() != 0) 1123 // throw new CRLException("Signature field overrun"); 1124 1125 // // the tbsCertsList 1126 // tbsCertList = seq[0].toByteArray(); 1127 1128 // // parse the information 1129 // DerInputStream derStrm = seq[0].data; 1130 // DerValue tmp; 1131 // byte nextByte; 1132 1133 // // version (optional if v1) 1134 // _version = 0; // by default, version = v1 == 0 1135 // nextByte = cast(byte)derStrm.peekByte(); 1136 // if (nextByte == DerValue.tag_Integer) { 1137 // _version = derStrm.getInteger(); 1138 // if (_version != 1) // i.e. v2 1139 // throw new CRLException("Invalid version"); 1140 // } 1141 // tmp = derStrm.getDerValue(); 1142 1143 // // signature 1144 // AlgorithmId tmpId = AlgorithmId.parse(tmp); 1145 1146 // // the "inner" and "outer" signature algorithms must match 1147 // if (! tmpId.equals(sigAlgId)) 1148 // throw new CRLException("Signature algorithm mismatch"); 1149 // infoSigAlgId = tmpId; 1150 1151 // // issuer 1152 // issuer = new X500Name(derStrm); 1153 // if (issuer.isEmpty()) { 1154 // throw new CRLException("Empty issuer DN not allowed in X509CRLs"); 1155 // } 1156 1157 // // thisUpdate 1158 // // check if UTCTime encoded or GeneralizedTime 1159 1160 // nextByte = cast(byte)derStrm.peekByte(); 1161 // if (nextByte == DerValue.tag_UtcTime) { 1162 // thisUpdate = derStrm.getUTCTime(); 1163 // } else if (nextByte == DerValue.tag_GeneralizedTime) { 1164 // thisUpdate = derStrm.getGeneralizedTime(); 1165 // } else { 1166 // throw new CRLException("Invalid encoding for thisUpdate" 1167 // ~ " (tag=" ~ nextByte ~ ")"); 1168 // } 1169 1170 // if (derStrm.available() == 0) 1171 // return; // done parsing no more optional fields present 1172 1173 // // nextUpdate (optional) 1174 // nextByte = cast(byte)derStrm.peekByte(); 1175 // if (nextByte == DerValue.tag_UtcTime) { 1176 // nextUpdate = derStrm.getUTCTime(); 1177 // } else if (nextByte == DerValue.tag_GeneralizedTime) { 1178 // nextUpdate = derStrm.getGeneralizedTime(); 1179 // } // else it is not present 1180 1181 // if (derStrm.available() == 0) 1182 // return; // done parsing no more optional fields present 1183 1184 // // revokedCertificates (optional) 1185 // nextByte = cast(byte)derStrm.peekByte(); 1186 // if ((nextByte == DerValue.tag_SequenceOf) 1187 // && (! ((nextByte & 0x0c0) == 0x080))) { 1188 // DerValue[] badCerts = derStrm.getSequence(4); 1189 1190 // X500Principal crlIssuer = getIssuerX500Principal(); 1191 // X500Principal badCertIssuer = crlIssuer; 1192 // for (int i = 0; i < badCerts.length; i++) { 1193 // X509CRLEntryImpl entry = new X509CRLEntryImpl(badCerts[i]); 1194 // badCertIssuer = getCertIssuer(entry, badCertIssuer); 1195 // entry.setCertificateIssuer(crlIssuer, badCertIssuer); 1196 // X509IssuerSerial issuerSerial = new X509IssuerSerial 1197 // (badCertIssuer, entry.getSerialNumber()); 1198 // revokedMap.put(issuerSerial, entry); 1199 // revokedList.add(entry); 1200 // } 1201 // } 1202 1203 // if (derStrm.available() == 0) 1204 // return; // done parsing no extensions 1205 1206 // // crlExtensions (optional) 1207 // tmp = derStrm.getDerValue(); 1208 // if (tmp.isConstructed() && tmp.isContextSpecific(cast(byte)0)) { 1209 // extensions = new CRLExtensions(tmp.data); 1210 // } 1211 // readOnly = true; 1212 implementationMissing(); 1213 } 1214 1215 /** 1216 * Extract the issuer X500Principal from an X509CRL. Parses the encoded 1217 * form of the CRL to preserve the principal's ASN.1 encoding. 1218 * 1219 * Called by java.security.cert.X509CRL.getIssuerX500Principal(). 1220 */ 1221 static X500Principal getIssuerX500Principal(X509CRL crl) { 1222 // try { 1223 // byte[] encoded = crl.getEncoded(); 1224 // DerInputStream derIn = new DerInputStream(encoded); 1225 // DerValue tbsCert = derIn.getSequence(3)[0]; 1226 // DerInputStream tbsIn = tbsCert.data; 1227 1228 // DerValue tmp; 1229 // // skip version number if present 1230 // byte nextByte = cast(byte)tbsIn.peekByte(); 1231 // if (nextByte == DerValue.tag_Integer) { 1232 // tmp = tbsIn.getDerValue(); 1233 // } 1234 1235 // tmp = tbsIn.getDerValue(); // skip signature 1236 // tmp = tbsIn.getDerValue(); // issuer 1237 // byte[] principalBytes = tmp.toByteArray(); 1238 // return new X500Principal(principalBytes); 1239 // } catch (Exception e) { 1240 // throw new RuntimeException("Could not parse issuer", e); 1241 // } 1242 implementationMissing(); 1243 return null; 1244 1245 } 1246 1247 /** 1248 * Returned the encoding of the given certificate for internal use. 1249 * Callers must guarantee that they neither modify it nor expose it 1250 * to untrusted code. Uses getEncodedInternal() if the certificate 1251 * is instance of X509CertImpl, getEncoded() otherwise. 1252 */ 1253 static byte[] getEncodedInternal(X509CRL crl) { 1254 X509CRLImpl crlImpl = cast(X509CRLImpl)crl; 1255 if (crlImpl is null) { 1256 return crl.getEncoded(); 1257 } else { 1258 return crlImpl.getEncodedInternal(); 1259 } 1260 } 1261 1262 /** 1263 * Utility method to convert an arbitrary instance of X509CRL 1264 * to a X509CRLImpl. Does a cast if possible, otherwise reparses 1265 * the encoding. 1266 */ 1267 static X509CRLImpl toImpl(X509CRL crl) { 1268 X509CRLImpl crlImpl = cast(X509CRLImpl)crl; 1269 if (crlImpl is null) { 1270 return X509Factory.intern(crl); 1271 } else { 1272 return crlImpl; 1273 } 1274 } 1275 1276 /** 1277 * Returns the X500 certificate issuer DN of a CRL entry. 1278 * 1279 * @param entry the entry to check 1280 * @param prevCertIssuer the previous entry's certificate issuer 1281 * @return the X500Principal in a CertificateIssuerExtension, or 1282 * prevCertIssuer if it does not exist 1283 */ 1284 private X500Principal getCertIssuer(X509CRLEntryImpl entry, 1285 X500Principal prevCertIssuer) { 1286 1287 // CertificateIssuerExtension ciExt = 1288 // entry.getCertificateIssuerExtension(); 1289 // if (ciExt !is null) { 1290 // GeneralNames names = ciExt.get(CertificateIssuerExtension.ISSUER); 1291 // X500Name issuerDN = cast(X500Name) names.get(0).getName(); 1292 // return issuerDN.asX500Principal(); 1293 // } else { 1294 // return prevCertIssuer; 1295 // } 1296 implementationMissing(); 1297 return null; 1298 1299 } 1300 1301 override 1302 void derEncode(OutputStream ot) { 1303 if (signedCRL is null) 1304 throw new IOException("Null CRL to encode"); 1305 ot.write(signedCRL.dup); 1306 } 1307 1308 /** 1309 * Immutable X.509 Certificate Issuer DN and serial number pair 1310 */ 1311 private final static class X509IssuerSerial : Comparable!X509IssuerSerial { 1312 X500Principal issuer; 1313 BigInteger serial; 1314 size_t hashcode = 0; 1315 1316 /** 1317 * Create an X509IssuerSerial. 1318 * 1319 * @param issuer the issuer DN 1320 * @param serial the serial number 1321 */ 1322 this(X500Principal issuer, BigInteger serial) { 1323 this.issuer = issuer; 1324 this.serial = serial; 1325 } 1326 1327 /** 1328 * Construct an X509IssuerSerial from an X509Certificate. 1329 */ 1330 this(X509Certificate cert) { 1331 this(cert.getIssuerX500Principal(), cert.getSerialNumber()); 1332 } 1333 1334 /** 1335 * Returns the issuer. 1336 * 1337 * @return the issuer 1338 */ 1339 X500Principal getIssuer() { 1340 return issuer; 1341 } 1342 1343 /** 1344 * Returns the serial number. 1345 * 1346 * @return the serial number 1347 */ 1348 BigInteger getSerial() { 1349 return serial; 1350 } 1351 1352 /** 1353 * Compares this X509Serial with another and returns true if they 1354 * are equivalent. 1355 * 1356 * @param o the other object to compare with 1357 * @return true if equal, false otherwise 1358 */ 1359 override bool opEquals(Object o) { 1360 if(o is null) return false; 1361 1362 if (o is this) { 1363 return true; 1364 } 1365 1366 X509IssuerSerial other = cast(X509IssuerSerial) o; 1367 if(other is null) return false; 1368 1369 if (serial == other.getSerial() && 1370 issuer == other.getIssuer()) { 1371 return true; 1372 } 1373 return false; 1374 } 1375 1376 /** 1377 * Returns a hash code value for this X509IssuerSerial. 1378 * 1379 * @return the hash code value 1380 */ 1381 override size_t toHash() @trusted nothrow { 1382 if (hashcode == 0) { 1383 size_t result = 17; 1384 result = 37*result + issuer.toHash(); 1385 result = 37*result + serial.toHash(); 1386 hashcode = result; 1387 } 1388 return hashcode; 1389 } 1390 1391 override int opCmp(Object o) 1392 { 1393 return opCmp(cast(X509IssuerSerial) o); 1394 } 1395 1396 int opCmp(X509IssuerSerial another) { 1397 int cissuer = std.algorithm.cmp(issuer.toString(), another.issuer.toString()); 1398 if (cissuer != 0) return cissuer; 1399 return (this.serial - another.serial).toInt(); 1400 } 1401 } 1402 }