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