1361 lines
47 KiB
Java
1361 lines
47 KiB
Java
/*
|
|
* Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package sun.security.x509;
|
|
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.io.IOException;
|
|
import java.math.BigInteger;
|
|
import java.security.Principal;
|
|
import java.security.PublicKey;
|
|
import java.security.PrivateKey;
|
|
import java.security.Provider;
|
|
import java.security.Signature;
|
|
import java.security.NoSuchAlgorithmException;
|
|
import java.security.InvalidKeyException;
|
|
import java.security.NoSuchProviderException;
|
|
import java.security.SignatureException;
|
|
import java.security.cert.Certificate;
|
|
import java.security.cert.X509CRL;
|
|
import java.security.cert.X509Certificate;
|
|
import java.security.cert.X509CRLEntry;
|
|
import java.security.cert.CRLException;
|
|
import java.util.*;
|
|
|
|
import javax.security.auth.x500.X500Principal;
|
|
|
|
import sun.security.provider.X509Factory;
|
|
import sun.security.util.*;
|
|
import sun.misc.HexDumpEncoder;
|
|
|
|
/**
|
|
* <p>
|
|
* An implementation for X509 CRL (Certificate Revocation List).
|
|
* <p>
|
|
* The X.509 v2 CRL format is described below in ASN.1:
|
|
* <pre>
|
|
* CertificateList ::= SEQUENCE {
|
|
* tbsCertList TBSCertList,
|
|
* signatureAlgorithm AlgorithmIdentifier,
|
|
* signature BIT STRING }
|
|
* </pre>
|
|
* More information can be found in
|
|
* <a href="http://www.ietf.org/rfc/rfc3280.txt">RFC 3280: Internet X.509
|
|
* Public Key Infrastructure Certificate and CRL Profile</a>.
|
|
* <p>
|
|
* The ASN.1 definition of <code>tbsCertList</code> is:
|
|
* <pre>
|
|
* TBSCertList ::= SEQUENCE {
|
|
* version Version OPTIONAL,
|
|
* -- if present, must be v2
|
|
* signature AlgorithmIdentifier,
|
|
* issuer Name,
|
|
* thisUpdate ChoiceOfTime,
|
|
* nextUpdate ChoiceOfTime OPTIONAL,
|
|
* revokedCertificates SEQUENCE OF SEQUENCE {
|
|
* userCertificate CertificateSerialNumber,
|
|
* revocationDate ChoiceOfTime,
|
|
* crlEntryExtensions Extensions OPTIONAL
|
|
* -- if present, must be v2
|
|
* } OPTIONAL,
|
|
* crlExtensions [0] EXPLICIT Extensions OPTIONAL
|
|
* -- if present, must be v2
|
|
* }
|
|
* </pre>
|
|
*
|
|
* @author Hemma Prafullchandra
|
|
* @see X509CRL
|
|
*/
|
|
public class X509CRLImpl extends X509CRL implements DerEncoder {
|
|
|
|
// CRL data, and its envelope
|
|
private byte[] signedCRL = null; // DER encoded crl
|
|
private byte[] signature = null; // raw signature bits
|
|
private byte[] tbsCertList = null; // DER encoded "to-be-signed" CRL
|
|
private AlgorithmId sigAlgId = null; // sig alg in CRL
|
|
|
|
// crl information
|
|
private int version;
|
|
private AlgorithmId infoSigAlgId; // sig alg in "to-be-signed" crl
|
|
private X500Name issuer = null;
|
|
private X500Principal issuerPrincipal = null;
|
|
private Date thisUpdate = null;
|
|
private Date nextUpdate = null;
|
|
private Map<X509IssuerSerial,X509CRLEntry> revokedMap = new TreeMap<>();
|
|
private List<X509CRLEntry> revokedList = new LinkedList<>();
|
|
private CRLExtensions extensions = null;
|
|
private final static boolean isExplicit = true;
|
|
private static final long YR_2050 = 2524636800000L;
|
|
|
|
private boolean readOnly = false;
|
|
|
|
/**
|
|
* PublicKey that has previously been used to successfully verify
|
|
* the signature of this CRL. Null if the CRL has not
|
|
* yet been verified (successfully).
|
|
*/
|
|
private PublicKey verifiedPublicKey;
|
|
/**
|
|
* If verifiedPublicKey is not null, name of the provider used to
|
|
* successfully verify the signature of this CRL, or the
|
|
* empty String if no provider was explicitly specified.
|
|
*/
|
|
private String verifiedProvider;
|
|
|
|
/**
|
|
* Not to be used. As it would lead to cases of uninitialized
|
|
* CRL objects.
|
|
*/
|
|
private X509CRLImpl() { }
|
|
|
|
/**
|
|
* Unmarshals an X.509 CRL from its encoded form, parsing the encoded
|
|
* bytes. This form of constructor is used by agents which
|
|
* need to examine and use CRL contents. Note that the buffer
|
|
* must include only one CRL, and no "garbage" may be left at
|
|
* the end.
|
|
*
|
|
* @param crlData the encoded bytes, with no trailing padding.
|
|
* @exception CRLException on parsing errors.
|
|
*/
|
|
public X509CRLImpl(byte[] crlData) throws CRLException {
|
|
try {
|
|
parse(new DerValue(crlData));
|
|
} catch (IOException e) {
|
|
signedCRL = null;
|
|
throw new CRLException("Parsing error: " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unmarshals an X.509 CRL from an DER value.
|
|
*
|
|
* @param val a DER value holding at least one CRL
|
|
* @exception CRLException on parsing errors.
|
|
*/
|
|
public X509CRLImpl(DerValue val) throws CRLException {
|
|
try {
|
|
parse(val);
|
|
} catch (IOException e) {
|
|
signedCRL = null;
|
|
throw new CRLException("Parsing error: " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unmarshals an X.509 CRL from an input stream. Only one CRL
|
|
* is expected at the end of the input stream.
|
|
*
|
|
* @param inStrm an input stream holding at least one CRL
|
|
* @exception CRLException on parsing errors.
|
|
*/
|
|
public X509CRLImpl(InputStream inStrm) throws CRLException {
|
|
try {
|
|
parse(new DerValue(inStrm));
|
|
} catch (IOException e) {
|
|
signedCRL = null;
|
|
throw new CRLException("Parsing error: " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initial CRL constructor, no revoked certs, and no extensions.
|
|
*
|
|
* @param issuer the name of the CA issuing this CRL.
|
|
* @param thisUpdate the Date of this issue.
|
|
* @param nextUpdate the Date of the next CRL.
|
|
*/
|
|
public X509CRLImpl(X500Name issuer, Date thisDate, Date nextDate) {
|
|
this.issuer = issuer;
|
|
this.thisUpdate = thisDate;
|
|
this.nextUpdate = nextDate;
|
|
}
|
|
|
|
/**
|
|
* CRL constructor, revoked certs, no extensions.
|
|
*
|
|
* @param issuer the name of the CA issuing this CRL.
|
|
* @param thisUpdate the Date of this issue.
|
|
* @param nextUpdate the Date of the next CRL.
|
|
* @param badCerts the array of CRL entries.
|
|
*
|
|
* @exception CRLException on parsing/construction errors.
|
|
*/
|
|
public X509CRLImpl(X500Name issuer, Date thisDate, Date nextDate,
|
|
X509CRLEntry[] badCerts)
|
|
throws CRLException
|
|
{
|
|
this.issuer = issuer;
|
|
this.thisUpdate = thisDate;
|
|
this.nextUpdate = nextDate;
|
|
if (badCerts != null) {
|
|
X500Principal crlIssuer = getIssuerX500Principal();
|
|
X500Principal badCertIssuer = crlIssuer;
|
|
for (int i = 0; i < badCerts.length; i++) {
|
|
X509CRLEntryImpl badCert = (X509CRLEntryImpl)badCerts[i];
|
|
try {
|
|
badCertIssuer = getCertIssuer(badCert, badCertIssuer);
|
|
} catch (IOException ioe) {
|
|
throw new CRLException(ioe);
|
|
}
|
|
badCert.setCertificateIssuer(crlIssuer, badCertIssuer);
|
|
X509IssuerSerial issuerSerial = new X509IssuerSerial
|
|
(badCertIssuer, badCert.getSerialNumber());
|
|
this.revokedMap.put(issuerSerial, badCert);
|
|
this.revokedList.add(badCert);
|
|
if (badCert.hasExtensions()) {
|
|
this.version = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* CRL constructor, revoked certs and extensions.
|
|
*
|
|
* @param issuer the name of the CA issuing this CRL.
|
|
* @param thisUpdate the Date of this issue.
|
|
* @param nextUpdate the Date of the next CRL.
|
|
* @param badCerts the array of CRL entries.
|
|
* @param crlExts the CRL extensions.
|
|
*
|
|
* @exception CRLException on parsing/construction errors.
|
|
*/
|
|
public X509CRLImpl(X500Name issuer, Date thisDate, Date nextDate,
|
|
X509CRLEntry[] badCerts, CRLExtensions crlExts)
|
|
throws CRLException
|
|
{
|
|
this(issuer, thisDate, nextDate, badCerts);
|
|
if (crlExts != null) {
|
|
this.extensions = crlExts;
|
|
this.version = 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returned the encoding as an uncloned byte array. Callers must
|
|
* guarantee that they neither modify it nor expose it to untrusted
|
|
* code.
|
|
*/
|
|
public byte[] getEncodedInternal() throws CRLException {
|
|
if (signedCRL == null) {
|
|
throw new CRLException("Null CRL to encode");
|
|
}
|
|
return signedCRL;
|
|
}
|
|
|
|
/**
|
|
* Returns the ASN.1 DER encoded form of this CRL.
|
|
*
|
|
* @exception CRLException if an encoding error occurs.
|
|
*/
|
|
public byte[] getEncoded() throws CRLException {
|
|
return getEncodedInternal().clone();
|
|
}
|
|
|
|
/**
|
|
* Encodes the "to-be-signed" CRL to the OutputStream.
|
|
*
|
|
* @param out the OutputStream to write to.
|
|
* @exception CRLException on encoding errors.
|
|
*/
|
|
public void encodeInfo(OutputStream out) throws CRLException {
|
|
try {
|
|
DerOutputStream tmp = new DerOutputStream();
|
|
DerOutputStream rCerts = new DerOutputStream();
|
|
DerOutputStream seq = new DerOutputStream();
|
|
|
|
if (version != 0) // v2 crl encode version
|
|
tmp.putInteger(version);
|
|
infoSigAlgId.encode(tmp);
|
|
if ((version == 0) && (issuer.toString() == null))
|
|
throw new CRLException("Null Issuer DN not allowed in v1 CRL");
|
|
issuer.encode(tmp);
|
|
|
|
if (thisUpdate.getTime() < YR_2050)
|
|
tmp.putUTCTime(thisUpdate);
|
|
else
|
|
tmp.putGeneralizedTime(thisUpdate);
|
|
|
|
if (nextUpdate != null) {
|
|
if (nextUpdate.getTime() < YR_2050)
|
|
tmp.putUTCTime(nextUpdate);
|
|
else
|
|
tmp.putGeneralizedTime(nextUpdate);
|
|
}
|
|
|
|
if (!revokedList.isEmpty()) {
|
|
for (X509CRLEntry entry : revokedList) {
|
|
((X509CRLEntryImpl)entry).encode(rCerts);
|
|
}
|
|
tmp.write(DerValue.tag_Sequence, rCerts);
|
|
}
|
|
|
|
if (extensions != null)
|
|
extensions.encode(tmp, isExplicit);
|
|
|
|
seq.write(DerValue.tag_Sequence, tmp);
|
|
|
|
tbsCertList = seq.toByteArray();
|
|
out.write(tbsCertList);
|
|
} catch (IOException e) {
|
|
throw new CRLException("Encoding error: " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Verifies that this CRL was signed using the
|
|
* private key that corresponds to the given public key.
|
|
*
|
|
* @param key the PublicKey used to carry out the verification.
|
|
*
|
|
* @exception NoSuchAlgorithmException on unsupported signature
|
|
* algorithms.
|
|
* @exception InvalidKeyException on incorrect key.
|
|
* @exception NoSuchProviderException if there's no default provider.
|
|
* @exception SignatureException on signature errors.
|
|
* @exception CRLException on encoding errors.
|
|
*/
|
|
public void verify(PublicKey key)
|
|
throws CRLException, NoSuchAlgorithmException, InvalidKeyException,
|
|
NoSuchProviderException, SignatureException {
|
|
verify(key, "");
|
|
}
|
|
|
|
/**
|
|
* Verifies that this CRL was signed using the
|
|
* private key that corresponds to the given public key,
|
|
* and that the signature verification was computed by
|
|
* the given provider.
|
|
*
|
|
* @param key the PublicKey used to carry out the verification.
|
|
* @param sigProvider the name of the signature provider.
|
|
*
|
|
* @exception NoSuchAlgorithmException on unsupported signature
|
|
* algorithms.
|
|
* @exception InvalidKeyException on incorrect key.
|
|
* @exception NoSuchProviderException on incorrect provider.
|
|
* @exception SignatureException on signature errors.
|
|
* @exception CRLException on encoding errors.
|
|
*/
|
|
public synchronized void verify(PublicKey key, String sigProvider)
|
|
throws CRLException, NoSuchAlgorithmException, InvalidKeyException,
|
|
NoSuchProviderException, SignatureException {
|
|
|
|
if (sigProvider == null) {
|
|
sigProvider = "";
|
|
}
|
|
if ((verifiedPublicKey != null) && verifiedPublicKey.equals(key)) {
|
|
// this CRL has already been successfully verified using
|
|
// this public key. Make sure providers match, too.
|
|
if (sigProvider.equals(verifiedProvider)) {
|
|
return;
|
|
}
|
|
}
|
|
if (signedCRL == null) {
|
|
throw new CRLException("Uninitialized CRL");
|
|
}
|
|
Signature sigVerf = null;
|
|
if (sigProvider.length() == 0) {
|
|
sigVerf = Signature.getInstance(sigAlgId.getName());
|
|
} else {
|
|
sigVerf = Signature.getInstance(sigAlgId.getName(), sigProvider);
|
|
}
|
|
sigVerf.initVerify(key);
|
|
|
|
if (tbsCertList == null) {
|
|
throw new CRLException("Uninitialized CRL");
|
|
}
|
|
|
|
sigVerf.update(tbsCertList, 0, tbsCertList.length);
|
|
|
|
if (!sigVerf.verify(signature)) {
|
|
throw new SignatureException("Signature does not match.");
|
|
}
|
|
verifiedPublicKey = key;
|
|
verifiedProvider = sigProvider;
|
|
}
|
|
|
|
/**
|
|
* Verifies that this CRL was signed using the
|
|
* private key that corresponds to the given public key,
|
|
* and that the signature verification was computed by
|
|
* the given provider. Note that the specified Provider object
|
|
* does not have to be registered in the provider list.
|
|
*
|
|
* @param key the PublicKey used to carry out the verification.
|
|
* @param sigProvider the signature provider.
|
|
*
|
|
* @exception NoSuchAlgorithmException on unsupported signature
|
|
* algorithms.
|
|
* @exception InvalidKeyException on incorrect key.
|
|
* @exception SignatureException on signature errors.
|
|
* @exception CRLException on encoding errors.
|
|
*/
|
|
public synchronized void verify(PublicKey key, Provider sigProvider)
|
|
throws CRLException, NoSuchAlgorithmException, InvalidKeyException,
|
|
SignatureException {
|
|
|
|
if (signedCRL == null) {
|
|
throw new CRLException("Uninitialized CRL");
|
|
}
|
|
Signature sigVerf = null;
|
|
if (sigProvider == null) {
|
|
sigVerf = Signature.getInstance(sigAlgId.getName());
|
|
} else {
|
|
sigVerf = Signature.getInstance(sigAlgId.getName(), sigProvider);
|
|
}
|
|
sigVerf.initVerify(key);
|
|
|
|
if (tbsCertList == null) {
|
|
throw new CRLException("Uninitialized CRL");
|
|
}
|
|
|
|
sigVerf.update(tbsCertList, 0, tbsCertList.length);
|
|
|
|
if (!sigVerf.verify(signature)) {
|
|
throw new SignatureException("Signature does not match.");
|
|
}
|
|
verifiedPublicKey = key;
|
|
}
|
|
|
|
/**
|
|
* This static method is the default implementation of the
|
|
* verify(PublicKey key, Provider sigProvider) method in X509CRL.
|
|
* Called from java.security.cert.X509CRL.verify(PublicKey key,
|
|
* Provider sigProvider)
|
|
*/
|
|
// BEGIN Android-removed
|
|
// TODO(31294527): in its only usage (the one mentioned in the javadoc) it causes an infinite
|
|
// loop. Then, the caller has been modified to throw UnsupportedOperationException.
|
|
/*
|
|
public static void verify(X509CRL crl, PublicKey key,
|
|
Provider sigProvider) throws CRLException,
|
|
NoSuchAlgorithmException, InvalidKeyException, SignatureException {
|
|
crl.verify(key, sigProvider);
|
|
}
|
|
*/
|
|
// END Android-removed
|
|
|
|
/**
|
|
* Encodes an X.509 CRL, and signs it using the given key.
|
|
*
|
|
* @param key the private key used for signing.
|
|
* @param algorithm the name of the signature algorithm used.
|
|
*
|
|
* @exception NoSuchAlgorithmException on unsupported signature
|
|
* algorithms.
|
|
* @exception InvalidKeyException on incorrect key.
|
|
* @exception NoSuchProviderException on incorrect provider.
|
|
* @exception SignatureException on signature errors.
|
|
* @exception CRLException if any mandatory data was omitted.
|
|
*/
|
|
public void sign(PrivateKey key, String algorithm)
|
|
throws CRLException, NoSuchAlgorithmException, InvalidKeyException,
|
|
NoSuchProviderException, SignatureException {
|
|
sign(key, algorithm, null);
|
|
}
|
|
|
|
/**
|
|
* Encodes an X.509 CRL, and signs it using the given key.
|
|
*
|
|
* @param key the private key used for signing.
|
|
* @param algorithm the name of the signature algorithm used.
|
|
* @param provider the name of the provider.
|
|
*
|
|
* @exception NoSuchAlgorithmException on unsupported signature
|
|
* algorithms.
|
|
* @exception InvalidKeyException on incorrect key.
|
|
* @exception NoSuchProviderException on incorrect provider.
|
|
* @exception SignatureException on signature errors.
|
|
* @exception CRLException if any mandatory data was omitted.
|
|
*/
|
|
public void sign(PrivateKey key, String algorithm, String provider)
|
|
throws CRLException, NoSuchAlgorithmException, InvalidKeyException,
|
|
NoSuchProviderException, SignatureException {
|
|
try {
|
|
if (readOnly)
|
|
throw new CRLException("cannot over-write existing CRL");
|
|
Signature sigEngine = null;
|
|
if ((provider == null) || (provider.length() == 0))
|
|
sigEngine = Signature.getInstance(algorithm);
|
|
else
|
|
sigEngine = Signature.getInstance(algorithm, provider);
|
|
|
|
sigEngine.initSign(key);
|
|
|
|
// in case the name is reset
|
|
sigAlgId = AlgorithmId.get(sigEngine.getAlgorithm());
|
|
infoSigAlgId = sigAlgId;
|
|
|
|
DerOutputStream out = new DerOutputStream();
|
|
DerOutputStream tmp = new DerOutputStream();
|
|
|
|
// encode crl info
|
|
encodeInfo(tmp);
|
|
|
|
// encode algorithm identifier
|
|
sigAlgId.encode(tmp);
|
|
|
|
// Create and encode the signature itself.
|
|
sigEngine.update(tbsCertList, 0, tbsCertList.length);
|
|
signature = sigEngine.sign();
|
|
tmp.putBitString(signature);
|
|
|
|
// Wrap the signed data in a SEQUENCE { data, algorithm, sig }
|
|
out.write(DerValue.tag_Sequence, tmp);
|
|
signedCRL = out.toByteArray();
|
|
readOnly = true;
|
|
|
|
} catch (IOException e) {
|
|
throw new CRLException("Error while encoding data: " +
|
|
e.getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a printable string of this CRL.
|
|
*
|
|
* @return value of this CRL in a printable form.
|
|
*/
|
|
public String toString() {
|
|
StringBuffer sb = new StringBuffer();
|
|
sb.append("X.509 CRL v" + (version+1) + "\n");
|
|
if (sigAlgId != null)
|
|
sb.append("Signature Algorithm: " + sigAlgId.toString() +
|
|
", OID=" + (sigAlgId.getOID()).toString() + "\n");
|
|
if (issuer != null)
|
|
sb.append("Issuer: " + issuer.toString() + "\n");
|
|
if (thisUpdate != null)
|
|
sb.append("\nThis Update: " + thisUpdate.toString() + "\n");
|
|
if (nextUpdate != null)
|
|
sb.append("Next Update: " + nextUpdate.toString() + "\n");
|
|
if (revokedList.isEmpty())
|
|
sb.append("\nNO certificates have been revoked\n");
|
|
else {
|
|
sb.append("\nRevoked Certificates: " + revokedList.size());
|
|
int i = 1;
|
|
for (X509CRLEntry entry: revokedList) {
|
|
sb.append("\n[" + i++ + "] " + entry.toString());
|
|
}
|
|
}
|
|
if (extensions != null) {
|
|
Collection<Extension> allExts = extensions.getAllExtensions();
|
|
Object[] objs = allExts.toArray();
|
|
sb.append("\nCRL Extensions: " + objs.length);
|
|
for (int i = 0; i < objs.length; i++) {
|
|
sb.append("\n[" + (i+1) + "]: ");
|
|
Extension ext = (Extension)objs[i];
|
|
try {
|
|
if (OIDMap.getClass(ext.getExtensionId()) == null) {
|
|
sb.append(ext.toString());
|
|
byte[] extValue = ext.getExtensionValue();
|
|
if (extValue != null) {
|
|
DerOutputStream out = new DerOutputStream();
|
|
out.putOctetString(extValue);
|
|
extValue = out.toByteArray();
|
|
HexDumpEncoder enc = new HexDumpEncoder();
|
|
sb.append("Extension unknown: "
|
|
+ "DER encoded OCTET string =\n"
|
|
+ enc.encodeBuffer(extValue) + "\n");
|
|
}
|
|
} else
|
|
sb.append(ext.toString()); // sub-class exists
|
|
} catch (Exception e) {
|
|
sb.append(", Error parsing this extension");
|
|
}
|
|
}
|
|
}
|
|
if (signature != null) {
|
|
HexDumpEncoder encoder = new HexDumpEncoder();
|
|
sb.append("\nSignature:\n" + encoder.encodeBuffer(signature)
|
|
+ "\n");
|
|
} else
|
|
sb.append("NOT signed yet\n");
|
|
return sb.toString();
|
|
}
|
|
|
|
/**
|
|
* Checks whether the given certificate is on this CRL.
|
|
*
|
|
* @param cert the certificate to check for.
|
|
* @return true if the given certificate is on this CRL,
|
|
* false otherwise.
|
|
*/
|
|
public boolean isRevoked(Certificate cert) {
|
|
if (revokedMap.isEmpty() || (!(cert instanceof X509Certificate))) {
|
|
return false;
|
|
}
|
|
X509Certificate xcert = (X509Certificate) cert;
|
|
X509IssuerSerial issuerSerial = new X509IssuerSerial(xcert);
|
|
return revokedMap.containsKey(issuerSerial);
|
|
}
|
|
|
|
/**
|
|
* Gets the version number from this CRL.
|
|
* The ASN.1 definition for this is:
|
|
* <pre>
|
|
* Version ::= INTEGER { v1(0), v2(1), v3(2) }
|
|
* -- v3 does not apply to CRLs but appears for consistency
|
|
* -- with definition of Version for certs
|
|
* </pre>
|
|
* @return the version number, i.e. 1 or 2.
|
|
*/
|
|
public int getVersion() {
|
|
return version+1;
|
|
}
|
|
|
|
/**
|
|
* Gets the issuer distinguished name from this CRL.
|
|
* The issuer name identifies the entity who has signed (and
|
|
* issued the CRL). The issuer name field contains an
|
|
* X.500 distinguished name (DN).
|
|
* The ASN.1 definition for this is:
|
|
* <pre>
|
|
* issuer Name
|
|
*
|
|
* Name ::= CHOICE { RDNSequence }
|
|
* RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
|
|
* RelativeDistinguishedName ::=
|
|
* SET OF AttributeValueAssertion
|
|
*
|
|
* AttributeValueAssertion ::= SEQUENCE {
|
|
* AttributeType,
|
|
* AttributeValue }
|
|
* AttributeType ::= OBJECT IDENTIFIER
|
|
* AttributeValue ::= ANY
|
|
* </pre>
|
|
* The Name describes a hierarchical name composed of attributes,
|
|
* such as country name, and corresponding values, such as US.
|
|
* The type of the component AttributeValue is determined by the
|
|
* AttributeType; in general it will be a directoryString.
|
|
* A directoryString is usually one of PrintableString,
|
|
* TeletexString or UniversalString.
|
|
* @return the issuer name.
|
|
*/
|
|
public Principal getIssuerDN() {
|
|
return (Principal)issuer;
|
|
}
|
|
|
|
/**
|
|
* Return the issuer as X500Principal. Overrides method in X509CRL
|
|
* to provide a slightly more efficient version.
|
|
*/
|
|
public X500Principal getIssuerX500Principal() {
|
|
if (issuerPrincipal == null) {
|
|
issuerPrincipal = issuer.asX500Principal();
|
|
}
|
|
return issuerPrincipal;
|
|
}
|
|
|
|
/**
|
|
* Gets the thisUpdate date from the CRL.
|
|
* The ASN.1 definition for this is:
|
|
*
|
|
* @return the thisUpdate date from the CRL.
|
|
*/
|
|
public Date getThisUpdate() {
|
|
return (new Date(thisUpdate.getTime()));
|
|
}
|
|
|
|
/**
|
|
* Gets the nextUpdate date from the CRL.
|
|
*
|
|
* @return the nextUpdate date from the CRL, or null if
|
|
* not present.
|
|
*/
|
|
public Date getNextUpdate() {
|
|
if (nextUpdate == null)
|
|
return null;
|
|
return (new Date(nextUpdate.getTime()));
|
|
}
|
|
|
|
/**
|
|
* Gets the CRL entry with the given serial number from this CRL.
|
|
*
|
|
* @return the entry with the given serial number, or <code>null</code> if
|
|
* no such entry exists in the CRL.
|
|
* @see X509CRLEntry
|
|
*/
|
|
public X509CRLEntry getRevokedCertificate(BigInteger serialNumber) {
|
|
if (revokedMap.isEmpty()) {
|
|
return null;
|
|
}
|
|
// assume this is a direct CRL entry (cert and CRL issuer are the same)
|
|
X509IssuerSerial issuerSerial = new X509IssuerSerial
|
|
(getIssuerX500Principal(), serialNumber);
|
|
return revokedMap.get(issuerSerial);
|
|
}
|
|
|
|
/**
|
|
* Gets the CRL entry for the given certificate.
|
|
*/
|
|
public X509CRLEntry getRevokedCertificate(X509Certificate cert) {
|
|
if (revokedMap.isEmpty()) {
|
|
return null;
|
|
}
|
|
X509IssuerSerial issuerSerial = new X509IssuerSerial(cert);
|
|
return revokedMap.get(issuerSerial);
|
|
}
|
|
|
|
/**
|
|
* Gets all the revoked certificates from the CRL.
|
|
* A Set of X509CRLEntry.
|
|
*
|
|
* @return all the revoked certificates or <code>null</code> if there are
|
|
* none.
|
|
* @see X509CRLEntry
|
|
*/
|
|
public Set<X509CRLEntry> getRevokedCertificates() {
|
|
if (revokedList.isEmpty()) {
|
|
return null;
|
|
} else {
|
|
return new TreeSet<X509CRLEntry>(revokedList);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the DER encoded CRL information, the
|
|
* <code>tbsCertList</code> from this CRL.
|
|
* This can be used to verify the signature independently.
|
|
*
|
|
* @return the DER encoded CRL information.
|
|
* @exception CRLException on encoding errors.
|
|
*/
|
|
public byte[] getTBSCertList() throws CRLException {
|
|
if (tbsCertList == null)
|
|
throw new CRLException("Uninitialized CRL");
|
|
return tbsCertList.clone();
|
|
}
|
|
|
|
/**
|
|
* Gets the raw Signature bits from the CRL.
|
|
*
|
|
* @return the signature.
|
|
*/
|
|
public byte[] getSignature() {
|
|
if (signature == null)
|
|
return null;
|
|
return signature.clone();
|
|
}
|
|
|
|
/**
|
|
* Gets the signature algorithm name for the CRL
|
|
* signature algorithm. For example, the string "SHA1withDSA".
|
|
* The ASN.1 definition for this is:
|
|
* <pre>
|
|
* AlgorithmIdentifier ::= SEQUENCE {
|
|
* algorithm OBJECT IDENTIFIER,
|
|
* parameters ANY DEFINED BY algorithm OPTIONAL }
|
|
* -- contains a value of the type
|
|
* -- registered for use with the
|
|
* -- algorithm object identifier value
|
|
* </pre>
|
|
*
|
|
* @return the signature algorithm name.
|
|
*/
|
|
public String getSigAlgName() {
|
|
if (sigAlgId == null)
|
|
return null;
|
|
return sigAlgId.getName();
|
|
}
|
|
|
|
/**
|
|
* Gets the signature algorithm OID string from the CRL.
|
|
* An OID is represented by a set of positive whole number separated
|
|
* by ".", that means,<br>
|
|
* <positive whole number>.<positive whole number>.<...>
|
|
* For example, the string "1.2.840.10040.4.3" identifies the SHA-1
|
|
* with DSA signature algorithm defined in
|
|
* <a href="http://www.ietf.org/rfc/rfc3279.txt">RFC 3279: Algorithms and
|
|
* Identifiers for the Internet X.509 Public Key Infrastructure Certificate
|
|
* and CRL Profile</a>.
|
|
*
|
|
* @return the signature algorithm oid string.
|
|
*/
|
|
public String getSigAlgOID() {
|
|
if (sigAlgId == null)
|
|
return null;
|
|
ObjectIdentifier oid = sigAlgId.getOID();
|
|
return oid.toString();
|
|
}
|
|
|
|
/**
|
|
* Gets the DER encoded signature algorithm parameters from this
|
|
* CRL's signature algorithm. In most cases, the signature
|
|
* algorithm parameters are null, the parameters are usually
|
|
* supplied with the Public Key.
|
|
*
|
|
* @return the DER encoded signature algorithm parameters, or
|
|
* null if no parameters are present.
|
|
*/
|
|
public byte[] getSigAlgParams() {
|
|
if (sigAlgId == null)
|
|
return null;
|
|
try {
|
|
return sigAlgId.getEncodedParams();
|
|
} catch (IOException e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the signature AlgorithmId from the CRL.
|
|
*
|
|
* @return the signature AlgorithmId
|
|
*/
|
|
public AlgorithmId getSigAlgId() {
|
|
return sigAlgId;
|
|
}
|
|
|
|
/**
|
|
* return the AuthorityKeyIdentifier, if any.
|
|
*
|
|
* @returns AuthorityKeyIdentifier or null
|
|
* (if no AuthorityKeyIdentifierExtension)
|
|
* @throws IOException on error
|
|
*/
|
|
public KeyIdentifier getAuthKeyId() throws IOException {
|
|
AuthorityKeyIdentifierExtension aki = getAuthKeyIdExtension();
|
|
if (aki != null) {
|
|
KeyIdentifier keyId = (KeyIdentifier)aki.get(
|
|
AuthorityKeyIdentifierExtension.KEY_ID);
|
|
return keyId;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* return the AuthorityKeyIdentifierExtension, if any.
|
|
*
|
|
* @returns AuthorityKeyIdentifierExtension or null (if no such extension)
|
|
* @throws IOException on error
|
|
*/
|
|
public AuthorityKeyIdentifierExtension getAuthKeyIdExtension()
|
|
throws IOException {
|
|
Object obj = getExtension(PKIXExtensions.AuthorityKey_Id);
|
|
return (AuthorityKeyIdentifierExtension)obj;
|
|
}
|
|
|
|
/**
|
|
* return the CRLNumberExtension, if any.
|
|
*
|
|
* @returns CRLNumberExtension or null (if no such extension)
|
|
* @throws IOException on error
|
|
*/
|
|
public CRLNumberExtension getCRLNumberExtension() throws IOException {
|
|
Object obj = getExtension(PKIXExtensions.CRLNumber_Id);
|
|
return (CRLNumberExtension)obj;
|
|
}
|
|
|
|
/**
|
|
* return the CRL number from the CRLNumberExtension, if any.
|
|
*
|
|
* @returns number or null (if no such extension)
|
|
* @throws IOException on error
|
|
*/
|
|
public BigInteger getCRLNumber() throws IOException {
|
|
CRLNumberExtension numExt = getCRLNumberExtension();
|
|
if (numExt != null) {
|
|
BigInteger num = numExt.get(CRLNumberExtension.NUMBER);
|
|
return num;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* return the DeltaCRLIndicatorExtension, if any.
|
|
*
|
|
* @returns DeltaCRLIndicatorExtension or null (if no such extension)
|
|
* @throws IOException on error
|
|
*/
|
|
public DeltaCRLIndicatorExtension getDeltaCRLIndicatorExtension()
|
|
throws IOException {
|
|
|
|
Object obj = getExtension(PKIXExtensions.DeltaCRLIndicator_Id);
|
|
return (DeltaCRLIndicatorExtension)obj;
|
|
}
|
|
|
|
/**
|
|
* return the base CRL number from the DeltaCRLIndicatorExtension, if any.
|
|
*
|
|
* @returns number or null (if no such extension)
|
|
* @throws IOException on error
|
|
*/
|
|
public BigInteger getBaseCRLNumber() throws IOException {
|
|
DeltaCRLIndicatorExtension dciExt = getDeltaCRLIndicatorExtension();
|
|
if (dciExt != null) {
|
|
BigInteger num = dciExt.get(DeltaCRLIndicatorExtension.NUMBER);
|
|
return num;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* return the IssuerAlternativeNameExtension, if any.
|
|
*
|
|
* @returns IssuerAlternativeNameExtension or null (if no such extension)
|
|
* @throws IOException on error
|
|
*/
|
|
public IssuerAlternativeNameExtension getIssuerAltNameExtension()
|
|
throws IOException {
|
|
Object obj = getExtension(PKIXExtensions.IssuerAlternativeName_Id);
|
|
return (IssuerAlternativeNameExtension)obj;
|
|
}
|
|
|
|
/**
|
|
* return the IssuingDistributionPointExtension, if any.
|
|
*
|
|
* @returns IssuingDistributionPointExtension or null
|
|
* (if no such extension)
|
|
* @throws IOException on error
|
|
*/
|
|
public IssuingDistributionPointExtension
|
|
getIssuingDistributionPointExtension() throws IOException {
|
|
|
|
Object obj = getExtension(PKIXExtensions.IssuingDistributionPoint_Id);
|
|
return (IssuingDistributionPointExtension) obj;
|
|
}
|
|
|
|
/**
|
|
* Return true if a critical extension is found that is
|
|
* not supported, otherwise return false.
|
|
*/
|
|
public boolean hasUnsupportedCriticalExtension() {
|
|
if (extensions == null)
|
|
return false;
|
|
return extensions.hasUnsupportedCriticalExtension();
|
|
}
|
|
|
|
/**
|
|
* Gets a Set of the extension(s) marked CRITICAL in the
|
|
* CRL. In the returned set, each extension is represented by
|
|
* its OID string.
|
|
*
|
|
* @return a set of the extension oid strings in the
|
|
* CRL that are marked critical.
|
|
*/
|
|
public Set<String> getCriticalExtensionOIDs() {
|
|
if (extensions == null) {
|
|
return null;
|
|
}
|
|
Set<String> extSet = new TreeSet<>();
|
|
for (Extension ex : extensions.getAllExtensions()) {
|
|
if (ex.isCritical()) {
|
|
extSet.add(ex.getExtensionId().toString());
|
|
}
|
|
}
|
|
return extSet;
|
|
}
|
|
|
|
/**
|
|
* Gets a Set of the extension(s) marked NON-CRITICAL in the
|
|
* CRL. In the returned set, each extension is represented by
|
|
* its OID string.
|
|
*
|
|
* @return a set of the extension oid strings in the
|
|
* CRL that are NOT marked critical.
|
|
*/
|
|
public Set<String> getNonCriticalExtensionOIDs() {
|
|
if (extensions == null) {
|
|
return null;
|
|
}
|
|
Set<String> extSet = new TreeSet<>();
|
|
for (Extension ex : extensions.getAllExtensions()) {
|
|
if (!ex.isCritical()) {
|
|
extSet.add(ex.getExtensionId().toString());
|
|
}
|
|
}
|
|
return extSet;
|
|
}
|
|
|
|
/**
|
|
* Gets the DER encoded OCTET string for the extension value
|
|
* (<code>extnValue</code>) identified by the passed in oid String.
|
|
* The <code>oid</code> string is
|
|
* represented by a set of positive whole number separated
|
|
* by ".", that means,<br>
|
|
* <positive whole number>.<positive whole number>.<...>
|
|
*
|
|
* @param oid the Object Identifier value for the extension.
|
|
* @return the der encoded octet string of the extension value.
|
|
*/
|
|
public byte[] getExtensionValue(String oid) {
|
|
if (extensions == null)
|
|
return null;
|
|
try {
|
|
String extAlias = OIDMap.getName(new ObjectIdentifier(oid));
|
|
Extension crlExt = null;
|
|
|
|
if (extAlias == null) { // may be unknown
|
|
ObjectIdentifier findOID = new ObjectIdentifier(oid);
|
|
Extension ex = null;
|
|
ObjectIdentifier inCertOID;
|
|
for (Enumeration<Extension> e = extensions.getElements();
|
|
e.hasMoreElements();) {
|
|
ex = e.nextElement();
|
|
inCertOID = ex.getExtensionId();
|
|
if (inCertOID.equals((Object)findOID)) {
|
|
crlExt = ex;
|
|
break;
|
|
}
|
|
}
|
|
} else
|
|
crlExt = extensions.get(extAlias);
|
|
if (crlExt == null)
|
|
return null;
|
|
byte[] extData = crlExt.getExtensionValue();
|
|
if (extData == null)
|
|
return null;
|
|
DerOutputStream out = new DerOutputStream();
|
|
out.putOctetString(extData);
|
|
return out.toByteArray();
|
|
} catch (Exception e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* get an extension
|
|
*
|
|
* @param oid ObjectIdentifier of extension desired
|
|
* @returns Object of type <extension> or null, if not found
|
|
* @throws IOException on error
|
|
*/
|
|
public Object getExtension(ObjectIdentifier oid) {
|
|
if (extensions == null)
|
|
return null;
|
|
|
|
// XXX Consider cloning this
|
|
return extensions.get(OIDMap.getName(oid));
|
|
}
|
|
|
|
/*
|
|
* Parses an X.509 CRL, should be used only by constructors.
|
|
*/
|
|
private void parse(DerValue val) throws CRLException, IOException {
|
|
// check if can over write the certificate
|
|
if (readOnly)
|
|
throw new CRLException("cannot over-write existing CRL");
|
|
|
|
if ( val.getData() == null || val.tag != DerValue.tag_Sequence)
|
|
throw new CRLException("Invalid DER-encoded CRL data");
|
|
|
|
signedCRL = val.toByteArray();
|
|
DerValue seq[] = new DerValue[3];
|
|
|
|
seq[0] = val.data.getDerValue();
|
|
seq[1] = val.data.getDerValue();
|
|
seq[2] = val.data.getDerValue();
|
|
|
|
if (val.data.available() != 0)
|
|
throw new CRLException("signed overrun, bytes = "
|
|
+ val.data.available());
|
|
|
|
if (seq[0].tag != DerValue.tag_Sequence)
|
|
throw new CRLException("signed CRL fields invalid");
|
|
|
|
sigAlgId = AlgorithmId.parse(seq[1]);
|
|
signature = seq[2].getBitString();
|
|
|
|
if (seq[1].data.available() != 0)
|
|
throw new CRLException("AlgorithmId field overrun");
|
|
|
|
if (seq[2].data.available() != 0)
|
|
throw new CRLException("Signature field overrun");
|
|
|
|
// the tbsCertsList
|
|
tbsCertList = seq[0].toByteArray();
|
|
|
|
// parse the information
|
|
DerInputStream derStrm = seq[0].data;
|
|
DerValue tmp;
|
|
byte nextByte;
|
|
|
|
// version (optional if v1)
|
|
version = 0; // by default, version = v1 == 0
|
|
nextByte = (byte)derStrm.peekByte();
|
|
if (nextByte == DerValue.tag_Integer) {
|
|
version = derStrm.getInteger();
|
|
if (version != 1) // i.e. v2
|
|
throw new CRLException("Invalid version");
|
|
}
|
|
tmp = derStrm.getDerValue();
|
|
|
|
// signature
|
|
AlgorithmId tmpId = AlgorithmId.parse(tmp);
|
|
|
|
// the "inner" and "outer" signature algorithms must match
|
|
if (! tmpId.equals(sigAlgId))
|
|
throw new CRLException("Signature algorithm mismatch");
|
|
infoSigAlgId = tmpId;
|
|
|
|
// issuer
|
|
issuer = new X500Name(derStrm);
|
|
if (issuer.isEmpty()) {
|
|
throw new CRLException("Empty issuer DN not allowed in X509CRLs");
|
|
}
|
|
|
|
// thisUpdate
|
|
// check if UTCTime encoded or GeneralizedTime
|
|
|
|
nextByte = (byte)derStrm.peekByte();
|
|
if (nextByte == DerValue.tag_UtcTime) {
|
|
thisUpdate = derStrm.getUTCTime();
|
|
} else if (nextByte == DerValue.tag_GeneralizedTime) {
|
|
thisUpdate = derStrm.getGeneralizedTime();
|
|
} else {
|
|
throw new CRLException("Invalid encoding for thisUpdate"
|
|
+ " (tag=" + nextByte + ")");
|
|
}
|
|
|
|
if (derStrm.available() == 0)
|
|
return; // done parsing no more optional fields present
|
|
|
|
// nextUpdate (optional)
|
|
nextByte = (byte)derStrm.peekByte();
|
|
if (nextByte == DerValue.tag_UtcTime) {
|
|
nextUpdate = derStrm.getUTCTime();
|
|
} else if (nextByte == DerValue.tag_GeneralizedTime) {
|
|
nextUpdate = derStrm.getGeneralizedTime();
|
|
} // else it is not present
|
|
|
|
if (derStrm.available() == 0)
|
|
return; // done parsing no more optional fields present
|
|
|
|
// revokedCertificates (optional)
|
|
nextByte = (byte)derStrm.peekByte();
|
|
if ((nextByte == DerValue.tag_SequenceOf)
|
|
&& (! ((nextByte & 0x0c0) == 0x080))) {
|
|
DerValue[] badCerts = derStrm.getSequence(4);
|
|
|
|
X500Principal crlIssuer = getIssuerX500Principal();
|
|
X500Principal badCertIssuer = crlIssuer;
|
|
for (int i = 0; i < badCerts.length; i++) {
|
|
X509CRLEntryImpl entry = new X509CRLEntryImpl(badCerts[i]);
|
|
badCertIssuer = getCertIssuer(entry, badCertIssuer);
|
|
entry.setCertificateIssuer(crlIssuer, badCertIssuer);
|
|
X509IssuerSerial issuerSerial = new X509IssuerSerial
|
|
(badCertIssuer, entry.getSerialNumber());
|
|
revokedMap.put(issuerSerial, entry);
|
|
revokedList.add(entry);
|
|
}
|
|
}
|
|
|
|
if (derStrm.available() == 0)
|
|
return; // done parsing no extensions
|
|
|
|
// crlExtensions (optional)
|
|
tmp = derStrm.getDerValue();
|
|
if (tmp.isConstructed() && tmp.isContextSpecific((byte)0)) {
|
|
extensions = new CRLExtensions(tmp.data);
|
|
}
|
|
readOnly = true;
|
|
}
|
|
|
|
/**
|
|
* Extract the issuer X500Principal from an X509CRL. Parses the encoded
|
|
* form of the CRL to preserve the principal's ASN.1 encoding.
|
|
*
|
|
* Called by java.security.cert.X509CRL.getIssuerX500Principal().
|
|
*/
|
|
public static X500Principal getIssuerX500Principal(X509CRL crl) {
|
|
try {
|
|
byte[] encoded = crl.getEncoded();
|
|
DerInputStream derIn = new DerInputStream(encoded);
|
|
DerValue tbsCert = derIn.getSequence(3)[0];
|
|
DerInputStream tbsIn = tbsCert.data;
|
|
|
|
DerValue tmp;
|
|
// skip version number if present
|
|
byte nextByte = (byte)tbsIn.peekByte();
|
|
if (nextByte == DerValue.tag_Integer) {
|
|
tmp = tbsIn.getDerValue();
|
|
}
|
|
|
|
tmp = tbsIn.getDerValue(); // skip signature
|
|
tmp = tbsIn.getDerValue(); // issuer
|
|
byte[] principalBytes = tmp.toByteArray();
|
|
return new X500Principal(principalBytes);
|
|
} catch (Exception e) {
|
|
throw new RuntimeException("Could not parse issuer", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returned the encoding of the given certificate for internal use.
|
|
* Callers must guarantee that they neither modify it nor expose it
|
|
* to untrusted code. Uses getEncodedInternal() if the certificate
|
|
* is instance of X509CertImpl, getEncoded() otherwise.
|
|
*/
|
|
public static byte[] getEncodedInternal(X509CRL crl) throws CRLException {
|
|
if (crl instanceof X509CRLImpl) {
|
|
return ((X509CRLImpl)crl).getEncodedInternal();
|
|
} else {
|
|
return crl.getEncoded();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Utility method to convert an arbitrary instance of X509CRL
|
|
* to a X509CRLImpl. Does a cast if possible, otherwise reparses
|
|
* the encoding.
|
|
*/
|
|
public static X509CRLImpl toImpl(X509CRL crl)
|
|
throws CRLException {
|
|
if (crl instanceof X509CRLImpl) {
|
|
return (X509CRLImpl)crl;
|
|
} else {
|
|
return X509Factory.intern(crl);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the X500 certificate issuer DN of a CRL entry.
|
|
*
|
|
* @param entry the entry to check
|
|
* @param prevCertIssuer the previous entry's certificate issuer
|
|
* @return the X500Principal in a CertificateIssuerExtension, or
|
|
* prevCertIssuer if it does not exist
|
|
*/
|
|
private X500Principal getCertIssuer(X509CRLEntryImpl entry,
|
|
X500Principal prevCertIssuer) throws IOException {
|
|
|
|
CertificateIssuerExtension ciExt =
|
|
entry.getCertificateIssuerExtension();
|
|
if (ciExt != null) {
|
|
GeneralNames names = ciExt.get(CertificateIssuerExtension.ISSUER);
|
|
X500Name issuerDN = (X500Name) names.get(0).getName();
|
|
return issuerDN.asX500Principal();
|
|
} else {
|
|
return prevCertIssuer;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void derEncode(OutputStream out) throws IOException {
|
|
if (signedCRL == null)
|
|
throw new IOException("Null CRL to encode");
|
|
out.write(signedCRL.clone());
|
|
}
|
|
|
|
/**
|
|
* Immutable X.509 Certificate Issuer DN and serial number pair
|
|
*/
|
|
private final static class X509IssuerSerial
|
|
implements Comparable<X509IssuerSerial> {
|
|
final X500Principal issuer;
|
|
final BigInteger serial;
|
|
volatile int hashcode = 0;
|
|
|
|
/**
|
|
* Create an X509IssuerSerial.
|
|
*
|
|
* @param issuer the issuer DN
|
|
* @param serial the serial number
|
|
*/
|
|
X509IssuerSerial(X500Principal issuer, BigInteger serial) {
|
|
this.issuer = issuer;
|
|
this.serial = serial;
|
|
}
|
|
|
|
/**
|
|
* Construct an X509IssuerSerial from an X509Certificate.
|
|
*/
|
|
X509IssuerSerial(X509Certificate cert) {
|
|
this(cert.getIssuerX500Principal(), cert.getSerialNumber());
|
|
}
|
|
|
|
/**
|
|
* Returns the issuer.
|
|
*
|
|
* @return the issuer
|
|
*/
|
|
X500Principal getIssuer() {
|
|
return issuer;
|
|
}
|
|
|
|
/**
|
|
* Returns the serial number.
|
|
*
|
|
* @return the serial number
|
|
*/
|
|
BigInteger getSerial() {
|
|
return serial;
|
|
}
|
|
|
|
/**
|
|
* Compares this X509Serial with another and returns true if they
|
|
* are equivalent.
|
|
*
|
|
* @param o the other object to compare with
|
|
* @return true if equal, false otherwise
|
|
*/
|
|
public boolean equals(Object o) {
|
|
if (o == this) {
|
|
return true;
|
|
}
|
|
|
|
if (!(o instanceof X509IssuerSerial)) {
|
|
return false;
|
|
}
|
|
|
|
X509IssuerSerial other = (X509IssuerSerial) o;
|
|
if (serial.equals(other.getSerial()) &&
|
|
issuer.equals(other.getIssuer())) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns a hash code value for this X509IssuerSerial.
|
|
*
|
|
* @return the hash code value
|
|
*/
|
|
public int hashCode() {
|
|
if (hashcode == 0) {
|
|
int result = 17;
|
|
result = 37*result + issuer.hashCode();
|
|
result = 37*result + serial.hashCode();
|
|
hashcode = result;
|
|
}
|
|
return hashcode;
|
|
}
|
|
|
|
@Override
|
|
public int compareTo(X509IssuerSerial another) {
|
|
int cissuer = issuer.toString()
|
|
.compareTo(another.issuer.toString());
|
|
if (cissuer != 0) return cissuer;
|
|
return this.serial.compareTo(another.serial);
|
|
}
|
|
}
|
|
}
|