1 /*
2  * Copyright (c) 1997, 2020, 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 import sun.misc.HexDumpEncoder;
45 
46 /**
47  * <p>
48  * An implementation for X509 CRL (Certificate Revocation List).
49  * <p>
50  * The X.509 v2 CRL format is described below in ASN.1:
51  * <pre>
52  * CertificateList  ::=  SEQUENCE  {
53  *     tbsCertList          TBSCertList,
54  *     signatureAlgorithm   AlgorithmIdentifier,
55  *     signature            BIT STRING  }
56  * </pre>
57  * More information can be found in
58  * <a href="http://tools.ietf.org/html/rfc5280">RFC 5280: Internet X.509
59  * Public Key Infrastructure Certificate and CRL Profile</a>.
60  * <p>
61  * The ASN.1 definition of <code>tbsCertList</code> is:
62  * <pre>
63  * TBSCertList  ::=  SEQUENCE  {
64  *     version                 Version OPTIONAL,
65  *                             -- if present, must be v2
66  *     signature               AlgorithmIdentifier,
67  *     issuer                  Name,
68  *     thisUpdate              ChoiceOfTime,
69  *     nextUpdate              ChoiceOfTime OPTIONAL,
70  *     revokedCertificates     SEQUENCE OF SEQUENCE  {
71  *         userCertificate         CertificateSerialNumber,
72  *         revocationDate          ChoiceOfTime,
73  *         crlEntryExtensions      Extensions OPTIONAL
74  *                                 -- if present, must be v2
75  *         }  OPTIONAL,
76  *     crlExtensions           [0]  EXPLICIT Extensions OPTIONAL
77  *                                  -- if present, must be v2
78  *     }
79  * </pre>
80  *
81  * @author Hemma Prafullchandra
82  * @see X509CRL
83  */
84 public class X509CRLImpl extends X509CRL implements DerEncoder {
85 
86     // CRL data, and its envelope
87     private byte[]      signedCRL = null; // DER encoded crl
88     private byte[]      signature = null; // raw signature bits
89     private byte[]      tbsCertList = null; // DER encoded "to-be-signed" CRL
90     private AlgorithmId sigAlgId = null; // sig alg in CRL
91 
92     // crl information
93     private int              version;
94     private AlgorithmId      infoSigAlgId; // sig alg in "to-be-signed" crl
95     private X500Name         issuer = null;
96     private X500Principal    issuerPrincipal = null;
97     private Date             thisUpdate = null;
98     private Date             nextUpdate = null;
99     private Map<X509IssuerSerial,X509CRLEntry> revokedMap = new TreeMap<>();
100     private List<X509CRLEntry> revokedList = new LinkedList<>();
101     private CRLExtensions    extensions = null;
102     private final static boolean isExplicit = true;
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      */
X509CRLImpl()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      */
X509CRLImpl(byte[] crlData)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      */
X509CRLImpl(DerValue val)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      */
X509CRLImpl(InputStream inStrm)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 thisUpdate the Date of this issue.
180      * @param nextUpdate the Date of the next CRL.
181      */
X509CRLImpl(X500Name issuer, Date thisDate, Date nextDate)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 thisUpdate the Date of this issue.
193      * @param nextUpdate the Date of the next CRL.
194      * @param badCerts the array of CRL entries.
195      *
196      * @exception CRLException on parsing/construction errors.
197      */
X509CRLImpl(X500Name issuer, Date thisDate, Date nextDate, X509CRLEntry[] badCerts)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 thisUpdate the Date of this issue.
232      * @param nextUpdate 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      */
X509CRLImpl(X500Name issuer, Date thisDate, Date nextDate, X509CRLEntry[] badCerts, CRLExtensions crlExts)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      */
getEncodedInternal()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      */
getEncoded()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      */
encodeInfo(OutputStream out)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() < CertificateValidity.YR_2050)
290                 tmp.putUTCTime(thisUpdate);
291             else
292                 tmp.putGeneralizedTime(thisUpdate);
293 
294             if (nextUpdate != null) {
295                 if (nextUpdate.getTime() < CertificateValidity.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      */
verify(PublicKey key)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      */
verify(PublicKey key, String sigProvider)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.length() == 0) {
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      */
verify(PublicKey key, Provider sigProvider)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      */
sign(PrivateKey key, String algorithm)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      */
sign(PrivateKey key, String algorithm, String provider)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.length() == 0))
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      */
toString()535     public String toString() {
536         return toStringWithAlgName("" + sigAlgId);
537     }
538 
539     // Specifically created for keytool to append a (weak) label to sigAlg
toStringWithAlgName(String name)540     public String toStringWithAlgName(String name) {
541         StringBuffer sb = new StringBuffer();
542         sb.append("X.509 CRL v" + (version+1) + "\n");
543         if (sigAlgId != null)
544             sb.append("Signature Algorithm: " + name.toString() +
545                   ", OID=" + (sigAlgId.getOID()).toString() + "\n");
546         if (issuer != null)
547             sb.append("Issuer: " + issuer.toString() + "\n");
548         if (thisUpdate != null)
549             sb.append("\nThis Update: " + thisUpdate.toString() + "\n");
550         if (nextUpdate != null)
551             sb.append("Next Update: " + nextUpdate.toString() + "\n");
552         if (revokedList.isEmpty())
553             sb.append("\nNO certificates have been revoked\n");
554         else {
555             sb.append("\nRevoked Certificates: " + revokedList.size());
556             int i = 1;
557             for (X509CRLEntry entry: revokedList) {
558                 sb.append("\n[" + i++ + "] " + entry.toString());
559             }
560         }
561         if (extensions != null) {
562             Collection<Extension> allExts = extensions.getAllExtensions();
563             Object[] objs = allExts.toArray();
564             sb.append("\nCRL Extensions: " + objs.length);
565             for (int i = 0; i < objs.length; i++) {
566                 sb.append("\n[" + (i+1) + "]: ");
567                 Extension ext = (Extension)objs[i];
568                 try {
569                    if (OIDMap.getClass(ext.getExtensionId()) == null) {
570                        sb.append(ext.toString());
571                        byte[] extValue = ext.getExtensionValue();
572                        if (extValue != null) {
573                            DerOutputStream out = new DerOutputStream();
574                            out.putOctetString(extValue);
575                            extValue = out.toByteArray();
576                            HexDumpEncoder enc = new HexDumpEncoder();
577                            sb.append("Extension unknown: "
578                                      + "DER encoded OCTET string =\n"
579                                      + enc.encodeBuffer(extValue) + "\n");
580                       }
581                    } else
582                        sb.append(ext.toString()); // sub-class exists
583                 } catch (Exception e) {
584                     sb.append(", Error parsing this extension");
585                 }
586             }
587         }
588         if (signature != null) {
589             HexDumpEncoder encoder = new HexDumpEncoder();
590             sb.append("\nSignature:\n" + encoder.encodeBuffer(signature)
591                       + "\n");
592         } else
593             sb.append("NOT signed yet\n");
594         return sb.toString();
595     }
596 
597     /**
598      * Checks whether the given certificate is on this CRL.
599      *
600      * @param cert the certificate to check for.
601      * @return true if the given certificate is on this CRL,
602      * false otherwise.
603      */
isRevoked(Certificate cert)604     public boolean isRevoked(Certificate cert) {
605         if (revokedMap.isEmpty() || (!(cert instanceof X509Certificate))) {
606             return false;
607         }
608         X509Certificate xcert = (X509Certificate) cert;
609         X509IssuerSerial issuerSerial = new X509IssuerSerial(xcert);
610         return revokedMap.containsKey(issuerSerial);
611     }
612 
613     /**
614      * Gets the version number from this CRL.
615      * The ASN.1 definition for this is:
616      * <pre>
617      * Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
618      *             -- v3 does not apply to CRLs but appears for consistency
619      *             -- with definition of Version for certs
620      * </pre>
621      * @return the version number, i.e. 1 or 2.
622      */
getVersion()623     public int getVersion() {
624         return version+1;
625     }
626 
627     /**
628      * Gets the issuer distinguished name from this CRL.
629      * The issuer name identifies the entity who has signed (and
630      * issued the CRL). The issuer name field contains an
631      * X.500 distinguished name (DN).
632      * The ASN.1 definition for this is:
633      * <pre>
634      * issuer    Name
635      *
636      * Name ::= CHOICE { RDNSequence }
637      * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
638      * RelativeDistinguishedName ::=
639      *     SET OF AttributeValueAssertion
640      *
641      * AttributeValueAssertion ::= SEQUENCE {
642      *                               AttributeType,
643      *                               AttributeValue }
644      * AttributeType ::= OBJECT IDENTIFIER
645      * AttributeValue ::= ANY
646      * </pre>
647      * The Name describes a hierarchical name composed of attributes,
648      * such as country name, and corresponding values, such as US.
649      * The type of the component AttributeValue is determined by the
650      * AttributeType; in general it will be a directoryString.
651      * A directoryString is usually one of PrintableString,
652      * TeletexString or UniversalString.
653      * @return the issuer name.
654      */
getIssuerDN()655     public Principal getIssuerDN() {
656         return (Principal)issuer;
657     }
658 
659     /**
660      * Return the issuer as X500Principal. Overrides method in X509CRL
661      * to provide a slightly more efficient version.
662      */
getIssuerX500Principal()663     public X500Principal getIssuerX500Principal() {
664         if (issuerPrincipal == null) {
665             issuerPrincipal = issuer.asX500Principal();
666         }
667         return issuerPrincipal;
668     }
669 
670     /**
671      * Gets the thisUpdate date from the CRL.
672      * The ASN.1 definition for this is:
673      *
674      * @return the thisUpdate date from the CRL.
675      */
getThisUpdate()676     public Date getThisUpdate() {
677         return (new Date(thisUpdate.getTime()));
678     }
679 
680     /**
681      * Gets the nextUpdate date from the CRL.
682      *
683      * @return the nextUpdate date from the CRL, or null if
684      * not present.
685      */
getNextUpdate()686     public Date getNextUpdate() {
687         if (nextUpdate == null)
688             return null;
689         return (new Date(nextUpdate.getTime()));
690     }
691 
692     /**
693      * Gets the CRL entry with the given serial number from this CRL.
694      *
695      * @return the entry with the given serial number, or <code>null</code> if
696      * no such entry exists in the CRL.
697      * @see X509CRLEntry
698      */
getRevokedCertificate(BigInteger serialNumber)699     public X509CRLEntry getRevokedCertificate(BigInteger serialNumber) {
700         if (revokedMap.isEmpty()) {
701             return null;
702         }
703         // assume this is a direct CRL entry (cert and CRL issuer are the same)
704         X509IssuerSerial issuerSerial = new X509IssuerSerial
705             (getIssuerX500Principal(), serialNumber);
706         return revokedMap.get(issuerSerial);
707     }
708 
709     /**
710      * Gets the CRL entry for the given certificate.
711      */
getRevokedCertificate(X509Certificate cert)712     public X509CRLEntry getRevokedCertificate(X509Certificate cert) {
713         if (revokedMap.isEmpty()) {
714             return null;
715         }
716         X509IssuerSerial issuerSerial = new X509IssuerSerial(cert);
717         return revokedMap.get(issuerSerial);
718     }
719 
720     /**
721      * Gets all the revoked certificates from the CRL.
722      * A Set of X509CRLEntry.
723      *
724      * @return all the revoked certificates or <code>null</code> if there are
725      * none.
726      * @see X509CRLEntry
727      */
getRevokedCertificates()728     public Set<X509CRLEntry> getRevokedCertificates() {
729         if (revokedList.isEmpty()) {
730             return null;
731         } else {
732             return new TreeSet<X509CRLEntry>(revokedList);
733         }
734     }
735 
736     /**
737      * Gets the DER encoded CRL information, the
738      * <code>tbsCertList</code> from this CRL.
739      * This can be used to verify the signature independently.
740      *
741      * @return the DER encoded CRL information.
742      * @exception CRLException on encoding errors.
743      */
getTBSCertList()744     public byte[] getTBSCertList() throws CRLException {
745         if (tbsCertList == null)
746             throw new CRLException("Uninitialized CRL");
747         return tbsCertList.clone();
748     }
749 
750     /**
751      * Gets the raw Signature bits from the CRL.
752      *
753      * @return the signature.
754      */
getSignature()755     public byte[] getSignature() {
756         if (signature == null)
757             return null;
758         return signature.clone();
759     }
760 
761     /**
762      * Gets the signature algorithm name for the CRL
763      * signature algorithm. For example, the string "SHA1withDSA".
764      * The ASN.1 definition for this is:
765      * <pre>
766      * AlgorithmIdentifier  ::=  SEQUENCE  {
767      *     algorithm               OBJECT IDENTIFIER,
768      *     parameters              ANY DEFINED BY algorithm OPTIONAL  }
769      *                             -- contains a value of the type
770      *                             -- registered for use with the
771      *                             -- algorithm object identifier value
772      * </pre>
773      *
774      * @return the signature algorithm name.
775      */
getSigAlgName()776     public String getSigAlgName() {
777         if (sigAlgId == null)
778             return null;
779         return sigAlgId.getName();
780     }
781 
782     /**
783      * Gets the signature algorithm OID string from the CRL.
784      * An OID is represented by a set of positive whole number separated
785      * by ".", that means,<br>
786      * &lt;positive whole number&gt;.&lt;positive whole number&gt;.&lt;...&gt;
787      * For example, the string "1.2.840.10040.4.3" identifies the SHA-1
788      * with DSA signature algorithm defined in
789      * <a href="http://www.ietf.org/rfc/rfc3279.txt">RFC 3279: Algorithms and
790      * Identifiers for the Internet X.509 Public Key Infrastructure Certificate
791      * and CRL Profile</a>.
792      *
793      * @return the signature algorithm oid string.
794      */
getSigAlgOID()795     public String getSigAlgOID() {
796         if (sigAlgId == null)
797             return null;
798         ObjectIdentifier oid = sigAlgId.getOID();
799         return oid.toString();
800     }
801 
802     /**
803      * Gets the DER encoded signature algorithm parameters from this
804      * CRL's signature algorithm. In most cases, the signature
805      * algorithm parameters are null, the parameters are usually
806      * supplied with the Public Key.
807      *
808      * @return the DER encoded signature algorithm parameters, or
809      *         null if no parameters are present.
810      */
getSigAlgParams()811     public byte[] getSigAlgParams() {
812         if (sigAlgId == null)
813             return null;
814         try {
815             return sigAlgId.getEncodedParams();
816         } catch (IOException e) {
817             return null;
818         }
819     }
820 
821     /**
822      * Gets the signature AlgorithmId from the CRL.
823      *
824      * @return the signature AlgorithmId
825      */
getSigAlgId()826     public AlgorithmId getSigAlgId() {
827         return sigAlgId;
828     }
829 
830     /**
831      * return the AuthorityKeyIdentifier, if any.
832      *
833      * @returns AuthorityKeyIdentifier or null
834      *          (if no AuthorityKeyIdentifierExtension)
835      * @throws IOException on error
836      */
getAuthKeyId()837     public KeyIdentifier getAuthKeyId() throws IOException {
838         AuthorityKeyIdentifierExtension aki = getAuthKeyIdExtension();
839         if (aki != null) {
840             KeyIdentifier keyId = (KeyIdentifier)aki.get(
841                     AuthorityKeyIdentifierExtension.KEY_ID);
842             return keyId;
843         } else {
844             return null;
845         }
846     }
847 
848     /**
849      * return the AuthorityKeyIdentifierExtension, if any.
850      *
851      * @returns AuthorityKeyIdentifierExtension or null (if no such extension)
852      * @throws IOException on error
853      */
getAuthKeyIdExtension()854     public AuthorityKeyIdentifierExtension getAuthKeyIdExtension()
855         throws IOException {
856         Object obj = getExtension(PKIXExtensions.AuthorityKey_Id);
857         return (AuthorityKeyIdentifierExtension)obj;
858     }
859 
860     /**
861      * return the CRLNumberExtension, if any.
862      *
863      * @returns CRLNumberExtension or null (if no such extension)
864      * @throws IOException on error
865      */
getCRLNumberExtension()866     public CRLNumberExtension getCRLNumberExtension() throws IOException {
867         Object obj = getExtension(PKIXExtensions.CRLNumber_Id);
868         return (CRLNumberExtension)obj;
869     }
870 
871     /**
872      * return the CRL number from the CRLNumberExtension, if any.
873      *
874      * @returns number or null (if no such extension)
875      * @throws IOException on error
876      */
getCRLNumber()877     public BigInteger getCRLNumber() throws IOException {
878         CRLNumberExtension numExt = getCRLNumberExtension();
879         if (numExt != null) {
880             BigInteger num = numExt.get(CRLNumberExtension.NUMBER);
881             return num;
882         } else {
883             return null;
884         }
885     }
886 
887     /**
888      * return the DeltaCRLIndicatorExtension, if any.
889      *
890      * @returns DeltaCRLIndicatorExtension or null (if no such extension)
891      * @throws IOException on error
892      */
getDeltaCRLIndicatorExtension()893     public DeltaCRLIndicatorExtension getDeltaCRLIndicatorExtension()
894         throws IOException {
895 
896         Object obj = getExtension(PKIXExtensions.DeltaCRLIndicator_Id);
897         return (DeltaCRLIndicatorExtension)obj;
898     }
899 
900     /**
901      * return the base CRL number from the DeltaCRLIndicatorExtension, if any.
902      *
903      * @returns number or null (if no such extension)
904      * @throws IOException on error
905      */
getBaseCRLNumber()906     public BigInteger getBaseCRLNumber() throws IOException {
907         DeltaCRLIndicatorExtension dciExt = getDeltaCRLIndicatorExtension();
908         if (dciExt != null) {
909             BigInteger num = dciExt.get(DeltaCRLIndicatorExtension.NUMBER);
910             return num;
911         } else {
912             return null;
913         }
914     }
915 
916     /**
917      * return the IssuerAlternativeNameExtension, if any.
918      *
919      * @returns IssuerAlternativeNameExtension or null (if no such extension)
920      * @throws IOException on error
921      */
getIssuerAltNameExtension()922     public IssuerAlternativeNameExtension getIssuerAltNameExtension()
923         throws IOException {
924         Object obj = getExtension(PKIXExtensions.IssuerAlternativeName_Id);
925         return (IssuerAlternativeNameExtension)obj;
926     }
927 
928     /**
929      * return the IssuingDistributionPointExtension, if any.
930      *
931      * @returns IssuingDistributionPointExtension or null
932      *          (if no such extension)
933      * @throws IOException on error
934      */
935     public IssuingDistributionPointExtension
getIssuingDistributionPointExtension()936         getIssuingDistributionPointExtension() throws IOException {
937 
938         Object obj = getExtension(PKIXExtensions.IssuingDistributionPoint_Id);
939         return (IssuingDistributionPointExtension) obj;
940     }
941 
942     /**
943      * Return true if a critical extension is found that is
944      * not supported, otherwise return false.
945      */
hasUnsupportedCriticalExtension()946     public boolean hasUnsupportedCriticalExtension() {
947         if (extensions == null)
948             return false;
949         return extensions.hasUnsupportedCriticalExtension();
950     }
951 
952     /**
953      * Gets a Set of the extension(s) marked CRITICAL in the
954      * CRL. In the returned set, each extension is represented by
955      * its OID string.
956      *
957      * @return a set of the extension oid strings in the
958      * CRL that are marked critical.
959      */
getCriticalExtensionOIDs()960     public Set<String> getCriticalExtensionOIDs() {
961         if (extensions == null) {
962             return null;
963         }
964         Set<String> extSet = new TreeSet<>();
965         for (Extension ex : extensions.getAllExtensions()) {
966             if (ex.isCritical()) {
967                 extSet.add(ex.getExtensionId().toString());
968             }
969         }
970         return extSet;
971     }
972 
973     /**
974      * Gets a Set of the extension(s) marked NON-CRITICAL in the
975      * CRL. In the returned set, each extension is represented by
976      * its OID string.
977      *
978      * @return a set of the extension oid strings in the
979      * CRL that are NOT marked critical.
980      */
getNonCriticalExtensionOIDs()981     public Set<String> getNonCriticalExtensionOIDs() {
982         if (extensions == null) {
983             return null;
984         }
985         Set<String> extSet = new TreeSet<>();
986         for (Extension ex : extensions.getAllExtensions()) {
987             if (!ex.isCritical()) {
988                 extSet.add(ex.getExtensionId().toString());
989             }
990         }
991         return extSet;
992     }
993 
994     /**
995      * Gets the DER encoded OCTET string for the extension value
996      * (<code>extnValue</code>) identified by the passed in oid String.
997      * The <code>oid</code> string is
998      * represented by a set of positive whole number separated
999      * by ".", that means,<br>
1000      * &lt;positive whole number&gt;.&lt;positive whole number&gt;.&lt;...&gt;
1001      *
1002      * @param oid the Object Identifier value for the extension.
1003      * @return the der encoded octet string of the extension value.
1004      */
getExtensionValue(String oid)1005     public byte[] getExtensionValue(String oid) {
1006         if (extensions == null)
1007             return null;
1008         try {
1009             String extAlias = OIDMap.getName(new ObjectIdentifier(oid));
1010             Extension crlExt = null;
1011 
1012             if (extAlias == null) { // may be unknown
1013                 ObjectIdentifier findOID = new ObjectIdentifier(oid);
1014                 Extension ex = null;
1015                 ObjectIdentifier inCertOID;
1016                 for (Enumeration<Extension> e = extensions.getElements();
1017                                                  e.hasMoreElements();) {
1018                     ex = e.nextElement();
1019                     inCertOID = ex.getExtensionId();
1020                     if (inCertOID.equals((Object)findOID)) {
1021                         crlExt = ex;
1022                         break;
1023                     }
1024                 }
1025             } else
1026                 crlExt = extensions.get(extAlias);
1027             if (crlExt == null)
1028                 return null;
1029             byte[] extData = crlExt.getExtensionValue();
1030             if (extData == null)
1031                 return null;
1032             DerOutputStream out = new DerOutputStream();
1033             out.putOctetString(extData);
1034             return out.toByteArray();
1035         } catch (Exception e) {
1036             return null;
1037         }
1038     }
1039 
1040     /**
1041      * get an extension
1042      *
1043      * @param oid ObjectIdentifier of extension desired
1044      * @returns Object of type <extension> or null, if not found
1045      * @throws IOException on error
1046      */
getExtension(ObjectIdentifier oid)1047     public Object getExtension(ObjectIdentifier oid) {
1048         if (extensions == null)
1049             return null;
1050 
1051         // XXX Consider cloning this
1052         return extensions.get(OIDMap.getName(oid));
1053     }
1054 
1055     /*
1056      * Parses an X.509 CRL, should be used only by constructors.
1057      */
parse(DerValue val)1058     private void parse(DerValue val) throws CRLException, IOException {
1059         // check if can over write the certificate
1060         if (readOnly)
1061             throw new CRLException("cannot over-write existing CRL");
1062 
1063         if ( val.getData() == null || val.tag != DerValue.tag_Sequence)
1064             throw new CRLException("Invalid DER-encoded CRL data");
1065 
1066         signedCRL = val.toByteArray();
1067         DerValue seq[] = new DerValue[3];
1068 
1069         seq[0] = val.data.getDerValue();
1070         seq[1] = val.data.getDerValue();
1071         seq[2] = val.data.getDerValue();
1072 
1073         if (val.data.available() != 0)
1074             throw new CRLException("signed overrun, bytes = "
1075                                      + val.data.available());
1076 
1077         if (seq[0].tag != DerValue.tag_Sequence)
1078             throw new CRLException("signed CRL fields invalid");
1079 
1080         sigAlgId = AlgorithmId.parse(seq[1]);
1081         signature = seq[2].getBitString();
1082 
1083         if (seq[1].data.available() != 0)
1084             throw new CRLException("AlgorithmId field overrun");
1085 
1086         if (seq[2].data.available() != 0)
1087             throw new CRLException("Signature field overrun");
1088 
1089         // the tbsCertsList
1090         tbsCertList = seq[0].toByteArray();
1091 
1092         // parse the information
1093         DerInputStream derStrm = seq[0].data;
1094         DerValue       tmp;
1095         byte           nextByte;
1096 
1097         // version (optional if v1)
1098         version = 0;   // by default, version = v1 == 0
1099         nextByte = (byte)derStrm.peekByte();
1100         if (nextByte == DerValue.tag_Integer) {
1101             version = derStrm.getInteger();
1102             if (version != 1)  // i.e. v2
1103                 throw new CRLException("Invalid version");
1104         }
1105         tmp = derStrm.getDerValue();
1106 
1107         // signature
1108         AlgorithmId tmpId = AlgorithmId.parse(tmp);
1109 
1110         // the "inner" and "outer" signature algorithms must match
1111         if (! tmpId.equals(sigAlgId))
1112             throw new CRLException("Signature algorithm mismatch");
1113         infoSigAlgId = tmpId;
1114 
1115         // issuer
1116         issuer = new X500Name(derStrm);
1117         if (issuer.isEmpty()) {
1118             throw new CRLException("Empty issuer DN not allowed in X509CRLs");
1119         }
1120 
1121         // thisUpdate
1122         // check if UTCTime encoded or GeneralizedTime
1123 
1124         nextByte = (byte)derStrm.peekByte();
1125         if (nextByte == DerValue.tag_UtcTime) {
1126             thisUpdate = derStrm.getUTCTime();
1127         } else if (nextByte == DerValue.tag_GeneralizedTime) {
1128             thisUpdate = derStrm.getGeneralizedTime();
1129         } else {
1130             throw new CRLException("Invalid encoding for thisUpdate"
1131                                    + " (tag=" + nextByte + ")");
1132         }
1133 
1134         if (derStrm.available() == 0)
1135            return;     // done parsing no more optional fields present
1136 
1137         // nextUpdate (optional)
1138         nextByte = (byte)derStrm.peekByte();
1139         if (nextByte == DerValue.tag_UtcTime) {
1140             nextUpdate = derStrm.getUTCTime();
1141         } else if (nextByte == DerValue.tag_GeneralizedTime) {
1142             nextUpdate = derStrm.getGeneralizedTime();
1143         } // else it is not present
1144 
1145         if (derStrm.available() == 0)
1146             return;     // done parsing no more optional fields present
1147 
1148         // revokedCertificates (optional)
1149         nextByte = (byte)derStrm.peekByte();
1150         if ((nextByte == DerValue.tag_SequenceOf)
1151             && (! ((nextByte & 0x0c0) == 0x080))) {
1152             DerValue[] badCerts = derStrm.getSequence(4);
1153 
1154             X500Principal crlIssuer = getIssuerX500Principal();
1155             X500Principal badCertIssuer = crlIssuer;
1156             for (int i = 0; i < badCerts.length; i++) {
1157                 X509CRLEntryImpl entry = new X509CRLEntryImpl(badCerts[i]);
1158                 badCertIssuer = getCertIssuer(entry, badCertIssuer);
1159                 entry.setCertificateIssuer(crlIssuer, badCertIssuer);
1160                 X509IssuerSerial issuerSerial = new X509IssuerSerial
1161                     (badCertIssuer, entry.getSerialNumber());
1162                 revokedMap.put(issuerSerial, entry);
1163                 revokedList.add(entry);
1164             }
1165         }
1166 
1167         if (derStrm.available() == 0)
1168             return;     // done parsing no extensions
1169 
1170         // crlExtensions (optional)
1171         tmp = derStrm.getDerValue();
1172         if (tmp.isConstructed() && tmp.isContextSpecific((byte)0)) {
1173             extensions = new CRLExtensions(tmp.data);
1174         }
1175         readOnly = true;
1176     }
1177 
1178     /**
1179      * Extract the issuer X500Principal from an X509CRL. Parses the encoded
1180      * form of the CRL to preserve the principal's ASN.1 encoding.
1181      *
1182      * Called by java.security.cert.X509CRL.getIssuerX500Principal().
1183      */
getIssuerX500Principal(X509CRL crl)1184     public static X500Principal getIssuerX500Principal(X509CRL crl) {
1185         try {
1186             byte[] encoded = crl.getEncoded();
1187             DerInputStream derIn = new DerInputStream(encoded);
1188             DerValue tbsCert = derIn.getSequence(3)[0];
1189             DerInputStream tbsIn = tbsCert.data;
1190 
1191             DerValue tmp;
1192             // skip version number if present
1193             byte nextByte = (byte)tbsIn.peekByte();
1194             if (nextByte == DerValue.tag_Integer) {
1195                 tmp = tbsIn.getDerValue();
1196             }
1197 
1198             tmp = tbsIn.getDerValue();  // skip signature
1199             tmp = tbsIn.getDerValue();  // issuer
1200             byte[] principalBytes = tmp.toByteArray();
1201             return new X500Principal(principalBytes);
1202         } catch (Exception e) {
1203             throw new RuntimeException("Could not parse issuer", e);
1204         }
1205     }
1206 
1207     /**
1208      * Returned the encoding of the given certificate for internal use.
1209      * Callers must guarantee that they neither modify it nor expose it
1210      * to untrusted code. Uses getEncodedInternal() if the certificate
1211      * is instance of X509CertImpl, getEncoded() otherwise.
1212      */
getEncodedInternal(X509CRL crl)1213     public static byte[] getEncodedInternal(X509CRL crl) throws CRLException {
1214         if (crl instanceof X509CRLImpl) {
1215             return ((X509CRLImpl)crl).getEncodedInternal();
1216         } else {
1217             return crl.getEncoded();
1218         }
1219     }
1220 
1221     /**
1222      * Utility method to convert an arbitrary instance of X509CRL
1223      * to a X509CRLImpl. Does a cast if possible, otherwise reparses
1224      * the encoding.
1225      */
toImpl(X509CRL crl)1226     public static X509CRLImpl toImpl(X509CRL crl)
1227             throws CRLException {
1228         if (crl instanceof X509CRLImpl) {
1229             return (X509CRLImpl)crl;
1230         } else {
1231             return X509Factory.intern(crl);
1232         }
1233     }
1234 
1235     /**
1236      * Returns the X500 certificate issuer DN of a CRL entry.
1237      *
1238      * @param entry the entry to check
1239      * @param prevCertIssuer the previous entry's certificate issuer
1240      * @return the X500Principal in a CertificateIssuerExtension, or
1241      *   prevCertIssuer if it does not exist
1242      */
getCertIssuer(X509CRLEntryImpl entry, X500Principal prevCertIssuer)1243     private X500Principal getCertIssuer(X509CRLEntryImpl entry,
1244         X500Principal prevCertIssuer) throws IOException {
1245 
1246         CertificateIssuerExtension ciExt =
1247             entry.getCertificateIssuerExtension();
1248         if (ciExt != null) {
1249             GeneralNames names = ciExt.get(CertificateIssuerExtension.ISSUER);
1250             X500Name issuerDN = (X500Name) names.get(0).getName();
1251             return issuerDN.asX500Principal();
1252         } else {
1253             return prevCertIssuer;
1254         }
1255     }
1256 
1257     @Override
derEncode(OutputStream out)1258     public void derEncode(OutputStream out) throws IOException {
1259         if (signedCRL == null)
1260             throw new IOException("Null CRL to encode");
1261         out.write(signedCRL.clone());
1262     }
1263 
1264     /**
1265      * Immutable X.509 Certificate Issuer DN and serial number pair
1266      */
1267     private final static class X509IssuerSerial
1268             implements Comparable<X509IssuerSerial> {
1269         final X500Principal issuer;
1270         final BigInteger serial;
1271         volatile int hashcode = 0;
1272 
1273         /**
1274          * Create an X509IssuerSerial.
1275          *
1276          * @param issuer the issuer DN
1277          * @param serial the serial number
1278          */
X509IssuerSerial(X500Principal issuer, BigInteger serial)1279         X509IssuerSerial(X500Principal issuer, BigInteger serial) {
1280             this.issuer = issuer;
1281             this.serial = serial;
1282         }
1283 
1284         /**
1285          * Construct an X509IssuerSerial from an X509Certificate.
1286          */
X509IssuerSerial(X509Certificate cert)1287         X509IssuerSerial(X509Certificate cert) {
1288             this(cert.getIssuerX500Principal(), cert.getSerialNumber());
1289         }
1290 
1291         /**
1292          * Returns the issuer.
1293          *
1294          * @return the issuer
1295          */
getIssuer()1296         X500Principal getIssuer() {
1297             return issuer;
1298         }
1299 
1300         /**
1301          * Returns the serial number.
1302          *
1303          * @return the serial number
1304          */
getSerial()1305         BigInteger getSerial() {
1306             return serial;
1307         }
1308 
1309         /**
1310          * Compares this X509Serial with another and returns true if they
1311          * are equivalent.
1312          *
1313          * @param o the other object to compare with
1314          * @return true if equal, false otherwise
1315          */
equals(Object o)1316         public boolean equals(Object o) {
1317             if (o == this) {
1318                 return true;
1319             }
1320 
1321             if (!(o instanceof X509IssuerSerial)) {
1322                 return false;
1323             }
1324 
1325             X509IssuerSerial other = (X509IssuerSerial) o;
1326             if (serial.equals(other.getSerial()) &&
1327                 issuer.equals(other.getIssuer())) {
1328                 return true;
1329             }
1330             return false;
1331         }
1332 
1333         /**
1334          * Returns a hash code value for this X509IssuerSerial.
1335          *
1336          * @return the hash code value
1337          */
hashCode()1338         public int hashCode() {
1339             if (hashcode == 0) {
1340                 int result = 17;
1341                 result = 37*result + issuer.hashCode();
1342                 result = 37*result + serial.hashCode();
1343                 hashcode = result;
1344             }
1345             return hashcode;
1346         }
1347 
1348         @Override
compareTo(X509IssuerSerial another)1349         public int compareTo(X509IssuerSerial another) {
1350             int cissuer = issuer.toString()
1351                     .compareTo(another.issuer.toString());
1352             if (cissuer != 0) return cissuer;
1353             return this.serial.compareTo(another.serial);
1354         }
1355     }
1356 }
1357