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