// --- BEGIN COPYRIGHT BLOCK ---
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 2 of the License.
//
// This program 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 for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// (C) 2007 Red Hat, Inc.
// All rights reserved.
// --- END COPYRIGHT BLOCK ---
package netscape.security.x509;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CRLException;
import java.security.cert.Certificate;
import java.security.cert.X509CRL;
import java.security.cert.X509CRLEntry;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.LinkedHashSet;
import java.util.Set;
import netscape.security.util.BigInt;
import netscape.security.util.DerInputStream;
import netscape.security.util.DerOutputStream;
import netscape.security.util.DerValue;
import netscape.security.util.ObjectIdentifier;
/**
*
* An implmentation for X509 CRL (Certificate Revocation List).
*
* The X.509 v2 CRL format is described below in ASN.1:
*
*
*
*
* CertificateList ::= SEQUENCE { tbsCertList TBSCertList, signatureAlgorithm AlgorithmIdentifier, signature BIT STRING
* }
*
* A good description and profiling is provided in the IETF PKIX WG draft, Part I: X.509 Certificate and CRL Profile,
* <draft-ietf-pkix-ipki-part1-06.txt>.
*
* The ASN.1 definition of tbsCertList
is:
*
*
* 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
* }
*
*
* @author Hemma Prafullchandra
* @version 1.8
* @see X509CRL
*/
public class X509CRLImpl extends X509CRL {
// 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; // sig alg in CRL
// crl information
private int version;
private AlgorithmId infoSigAlgId; // sig alg in "to-be-signed" crl
private X500Name issuer;
private Date thisUpdate = null;
private Date nextUpdate = null;
// private static final Hashtable revokedCerts = new Hashtable();
private Hashtable revokedCerts = new Hashtable();
// private static CRLExtensions extensions = null;
private CRLExtensions extensions = null;
private boolean entriesIncluded = true;
private final static boolean isExplicit = true;
private boolean readOnly = false;
/**
* 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.
* @exception X509ExtensionException on extension handling errors.
*/
public X509CRLImpl(byte[] crlData)
throws CRLException, X509ExtensionException {
try {
DerValue in = new DerValue(crlData);
parse(in);
signedCRL = crlData;
} catch (IOException e) {
throw new CRLException("Parsing error: " + e.getMessage());
}
}
public X509CRLImpl(byte[] crlData, boolean includeEntries)
throws CRLException, X509ExtensionException {
try {
entriesIncluded = includeEntries;
DerValue in = new DerValue(crlData);
parse(in, includeEntries);
signedCRL = crlData;
} catch (IOException e) {
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.
* @exception X509ExtensionException on extension handling errors.
*/
public X509CRLImpl(InputStream inStrm)
throws CRLException, X509ExtensionException {
try {
DerValue val = new DerValue(inStrm);
parse(val);
signedCRL = val.toByteArray();
} catch (IOException e) {
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 revoked certificates.
*
* @exception CRLException on parsing/construction errors.
* @exception X509ExtensionException on extension handling errors.
*/
public X509CRLImpl(X500Name issuer, Date thisDate, Date nextDate,
RevokedCertificate[] badCerts)
throws CRLException, X509ExtensionException {
this.issuer = issuer;
this.thisUpdate = thisDate;
this.nextUpdate = nextDate;
if (badCerts != null) {
for (int i = 0; i < badCerts.length; i++)
this.revokedCerts.put(badCerts[i].getSerialNumber(),
badCerts[i]);
}
}
/**
* 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 revoked certificates.
* @param crlExts the CRL extensions.
*
* @exception CRLException on parsing/construction errors.
* @exception X509ExtensionException on extension handling errors.
*/
public X509CRLImpl(X500Name issuer, Date thisDate, Date nextDate,
RevokedCertificate[] badCerts, CRLExtensions crlExts)
throws CRLException, X509ExtensionException {
this.issuer = issuer;
this.thisUpdate = thisDate;
this.nextUpdate = nextDate;
if (badCerts != null) {
for (int i = 0; i < badCerts.length; i++) {
if (badCerts[i] != null) {
this.revokedCerts.put(badCerts[i].getSerialNumber(),
badCerts[i]);
if (badCerts[i].hasExtensions())
this.version = 1;
}
}
}
if (crlExts != null) {
this.extensions = crlExts;
this.version = 1;
}
}
/**
* CRL constructor, revoked certs and extensions.
* This will be used by code that constructs CRL and uses
* encodeInfo() in order to sign it using external means
* (other than sign() method)
*
* @param issuer the name of the CA issuing this CRL.
* @param sigAlg signing algorithm id
* @param thisUpdate the Date of this issue.
* @param nextUpdate the Date of the next CRL.
* @param badCerts the array of revoked certificates.
* @param crlExts the CRL extensions.
*/
public X509CRLImpl(X500Name issuer, AlgorithmId algId, Date thisDate, Date nextDate,
RevokedCertificate[] badCerts, CRLExtensions crlExts)
throws CRLException, X509ExtensionException {
this(issuer, thisDate, nextDate, badCerts, crlExts);
infoSigAlgId = algId;
}
/**
* CRL constructor, revoked certs and extensions.
*
* @param issuer the name of the CA issuing this CRL.
* @param sigAlg signing algorithm id
* @param thisUpdate the Date of this issue.
* @param nextUpdate the Date of the next CRL.
* @param badCerts the hashtable of revoked certificates.
* @param crlExts the CRL extensions.
*
* @exception CRLException on parsing/construction errors.
* @exception X509ExtensionException on extension handling errors.
*/
public X509CRLImpl(X500Name issuer, AlgorithmId algId,
Date thisDate, Date nextDate,
Hashtable badCerts, CRLExtensions crlExts)
throws CRLException, X509ExtensionException {
this.issuer = issuer;
this.thisUpdate = thisDate;
this.nextUpdate = nextDate;
this.revokedCerts = badCerts;
if (crlExts != null) {
this.extensions = crlExts;
this.version = 1;
}
infoSigAlgId = algId;
}
/**
* Returns the ASN.1 DER encoded form of this CRL.
*
* @exception CRLException if an encoding error occurs.
*/
public byte[] getEncoded() throws CRLException {
if (signedCRL == null)
throw new CRLException("Null CRL to encode");
byte[] dup = new byte[signedCRL.length];
System.arraycopy(signedCRL, 0, dup, 0, dup.length);
return dup;
}
/**
* Returns true if signedCRL was set.
*
* @param byte array of containing signed CRL.
*/
public boolean setSignedCRL(byte[] crl) {
boolean done = false;
if (tbsCertList != null && signedCRL == null) {
signedCRL = new byte[crl.length];
System.arraycopy(crl, 0, signedCRL, 0, signedCRL.length);
done = true;
}
return done;
}
public boolean hasUnsupportedCriticalExtension() {
// XXX NOT IMPLEMENTED
return true;
}
/**
* Encodes the "to-be-signed" CRL to the OutputStream.
*
* @param out the OutputStream to write to.
* @exception CRLException on encoding errors.
* @exception X509ExtensionException on extension encoding errors.
*/
public void encodeInfo(OutputStream out)
throws CRLException, X509ExtensionException {
try {
DerOutputStream tmp = new DerOutputStream();
DerOutputStream rCerts = new DerOutputStream();
DerOutputStream seq = new DerOutputStream();
if (version != 0) // v2 crl encode version
tmp.putInteger(new BigInt(version));
infoSigAlgId.encode(tmp);
issuer.encode(tmp);
// from 2050 should encode GeneralizedTime
tmp.putUTCTime(thisUpdate);
if (nextUpdate != null)
tmp.putUTCTime(nextUpdate);
if (!revokedCerts.isEmpty()) {
for (Enumeration e = revokedCerts.elements(); e.hasMoreElements();)
((RevokedCertImpl) e.nextElement()).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 specified 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, null);
}
/**
* Verifies that this CRL was signed using the
* private key that corresponds to the specified 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 void verify(PublicKey key, String sigProvider)
throws CRLException, NoSuchAlgorithmException, InvalidKeyException,
NoSuchProviderException, SignatureException {
if (signedCRL == null) {
throw new CRLException("Uninitialized CRL");
}
Signature sigVerf = null;
String sigAlg = sigAlgId.getName();
if (sigProvider.equals("Mozilla-JSS")) {
if (sigAlg.equals("MD5withRSA")) {
sigAlg = "MD5/RSA";
} else if (sigAlg.equals("MD2withRSA")) {
sigAlg = "MD2/RSA";
} else if (sigAlg.equals("SHA1withRSA")) {
sigAlg = "SHA1/RSA";
} else if (sigAlg.equals("SHA1withDSA")) {
sigAlg = "SHA1/DSA";
}
}
sigVerf = Signature.getInstance(sigAlg, sigProvider);
sigVerf.initVerify(key);
if (tbsCertList == null)
throw new CRLException("Uninitialized CRL");
sigVerf.update(tbsCertList, 0, tbsCertList.length);
if (!sigVerf.verify(signature)) {
throw new CRLException("Signature does not match.");
}
}
/**
* Encodes an X.509 CRL, and signs it using the key
* passed.
*
* @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.
* @exception X509ExtensionException on any extension errors.
*/
public void sign(PrivateKey key, String algorithm)
throws CRLException, NoSuchAlgorithmException, InvalidKeyException,
NoSuchProviderException, SignatureException, X509ExtensionException {
sign(key, algorithm, null);
}
/**
* Encodes an X.509 CRL, and signs it using the key
* passed.
*
* @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.
* @exception X509ExtensionException on any extension errors.
*/
public void sign(PrivateKey key, String algorithm, String provider)
throws CRLException, NoSuchAlgorithmException, InvalidKeyException,
NoSuchProviderException, SignatureException, X509ExtensionException {
try {
if (readOnly)
throw new CRLException("cannot over-write existing CRL");
Signature sigEngine = null;
if (provider == null)
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");
sb.append("Signature Algorithm: " + sigAlgId.toString() +
", OID=" + (sigAlgId.getOID()).toString() + "\n");
sb.append("Issuer: " + issuer.toString() + "\n");
sb.append("\nThis Update: " + thisUpdate.toString() + "\n");
if (nextUpdate != null)
sb.append("Next Update: " + nextUpdate.toString() + "\n");
if (revokedCerts.isEmpty())
sb.append("\nNO certificates have been revoked\n");
else {
sb.append("\nRevoked Certificates:\n");
for (Enumeration e = revokedCerts.elements(); e.hasMoreElements();)
sb.append(((RevokedCertificate) e.nextElement()).toString());
}
if (extensions != null) {
for (int i = 0; i < extensions.size(); i++) {
sb.append("\nCRL Extension[" + i + "]: " +
((Extension) (extensions.elementAt(i))).toString());
}
}
netscape.security.util.PrettyPrintFormat pp =
new netscape.security.util.PrettyPrintFormat(" ", 20);
String signaturebits = pp.toHexString(signature);
sb.append("\nSignature:\n" + signaturebits);
return sb.toString();
}
/**
* Checks whether the given serial number is on this CRL.
*
* @param serialNumber the number to check for.
* @return true if the given serial number is on this CRL,
* false otherwise.
*/
public boolean isRevoked(BigInteger serialNumber) {
if (revokedCerts == null || revokedCerts.isEmpty())
return false;
return revokedCerts.containsKey(serialNumber);
}
public boolean isRevoked(Certificate cert) {
if (cert == null)
return false;
if (cert instanceof X509Certificate) {
return isRevoked(((X509Certificate) cert).getSerialNumber());
} else {
return false;
}
}
/**
* Gets the version number from the CRL.
* The ASN.1 definition for this is:
*
*
* Version ::= INTEGER { v1(0), v2(1), v3(2) }
* -- v3 does not apply to CRLs but appears for consistency
* -- with definition of Version for certs
*
*
* @return the version number.
*/
public int getVersion() {
return version;
}
/**
* 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:
*
*
* issuer Name
*
* Name ::= CHOICE { RDNSequence }
* RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
* RelativeDistinguishedName ::=
* SET OF AttributeValueAssertion
*
* AttributeValueAssertion ::= SEQUENCE {
* AttributeType,
* AttributeValue }
* AttributeType ::= OBJECT IDENTIFIER
* AttributeValue ::= ANY
*
*
* 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;
}
/**
* 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()));
}
/**
* Get the revoked certificate from the CRL by the serial
* number provided.
*
* @return the revoked certificate or null if there is
* no entry in the CRL marked with the provided serial number.
* @see RevokedCertificate
*/
public X509CRLEntry getRevokedCertificate(BigInteger serialNumber) {
if (revokedCerts == null || revokedCerts.isEmpty())
return null;
RevokedCertificate badCert =
(RevokedCertificate) revokedCerts.get(serialNumber);
return badCert;
}
/**
* Gets all the revoked certificates from the CRL.
* A Set of RevokedCertificate.
*
* @return all the revoked certificates or null if there are
* none.
* @see RevokedCertificate
*/
public Set getRevokedCertificates() {
if (revokedCerts == null || revokedCerts.isEmpty())
return null;
else {
Set certSet = new LinkedHashSet(revokedCerts.values());
return certSet;
}
}
@SuppressWarnings("unchecked")
public Hashtable getListOfRevokedCertificates() {
if (revokedCerts == null) {
return null;
} else {
return (Hashtable) revokedCerts.clone();
}
}
public int getNumberOfRevokedCertificates() {
if (revokedCerts == null)
return -1;
else
return revokedCerts.size();
}
/**
* Gets the DER encoded CRL information, the tbsCertList
from this CRL.
* This can be used to verify the signature independently.
*
* @return the DER encoded CRL information.
* @exception CRLException on parsing errors.
* @exception X509ExtensionException on extension parsing errors.
*/
public byte[] getTBSCertList()
throws CRLException {
if (tbsCertList == null)
throw new CRLException("Uninitialized CRL");
byte[] dup = new byte[tbsCertList.length];
System.arraycopy(tbsCertList, 0, dup, 0, dup.length);
return dup;
}
/**
* Gets the raw Signature bits from the CRL.
*
* @return the signature.
*/
public byte[] getSignature() {
if (signature == null)
return null;
byte[] dup = new byte[signature.length];
System.arraycopy(signature, 0, dup, 0, dup.length);
return dup;
}
/**
* Returns true if signature was set.
*
* @param byte array of containing CRL signature.
*/
public boolean setSignature(byte[] crlSignature) {
boolean done = false;
if (tbsCertList != null && signature == null) {
signature = new byte[crlSignature.length];
System.arraycopy(crlSignature, 0, signature, 0, signature.length);
done = true;
}
return done;
}
/**
* Gets the signature algorithm name for the CRL
* signature algorithm. For example, the string "SHA1withDSA".
* The ASN.1 definition for this is:
*
*
* 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
*
*
* @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,
* <positive whole number>.<positive whole number>.<...>
* For example, the string "1.2.840.10040.4.3" identifies the SHA-1
* with DSA signature algorithm, as per the PKIX part I.
*
* @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 a Set of the extension(s) marked CRITICAL in the
* CRL by OID strings.
*
* @return a set of the extension oid strings in the
* CRL that are marked critical.
*/
public Set getCriticalExtensionOIDs() {
if (extensions == null)
return null;
Set extSet = new LinkedHashSet();
Extension ex;
for (Enumeration e = extensions.getElements(); e.hasMoreElements();) {
ex = e.nextElement();
if (ex.isCritical()) {
extSet.add(((ObjectIdentifier) ex.getExtensionId()).toString());
}
}
return extSet;
}
/**
* Gets a Set of the extension(s) marked NON-CRITICAL in the
* CRL by OID strings.
*
* @return a set of the extension oid strings in the
* CRL that are NOT marked critical.
*/
public Set getNonCriticalExtensionOIDs() {
if (extensions == null)
return null;
Set extSet = new LinkedHashSet();
Extension ex;
for (Enumeration e = extensions.getElements(); e.hasMoreElements();) {
ex = e.nextElement();
if (!ex.isCritical())
extSet.add(((ObjectIdentifier) ex.getExtensionId()).toString());
}
return extSet;
}
/**
* Gets the DER encoded OCTET string for the extension value
* (extnValue
) identified by the passed in oid String.
* The oid
string is
* represented by a set of positive whole number separated
* by ".", that means,
* <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 e = extensions.getElements(); e.hasMoreElements();) {
ex = e.nextElement();
inCertOID = ex.getExtensionId();
if (inCertOID.equals(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;
}
}
public BigInteger getCRLNumber() {
try {
CRLExtensions exts = getExtensions();
if (exts == null)
return null;
Enumeration e = exts.getElements();
while (e.hasMoreElements()) {
Extension ext = (Extension) e.nextElement();
if (ext instanceof CRLNumberExtension) {
CRLNumberExtension numExt = (CRLNumberExtension) ext;
return (BigInteger) numExt.get(CRLNumberExtension.NUMBER);
}
}
} catch (Exception e) {
}
return null;
}
public BigInteger getDeltaBaseCRLNumber() {
try {
CRLExtensions exts = getExtensions();
if (exts == null)
return null;
Enumeration e = exts.getElements();
while (e.hasMoreElements()) {
Extension ext = (Extension) e.nextElement();
if (ext instanceof DeltaCRLIndicatorExtension) {
DeltaCRLIndicatorExtension numExt = (DeltaCRLIndicatorExtension) ext;
return (BigInteger) numExt.get(DeltaCRLIndicatorExtension.NUMBER);
}
}
} catch (Exception e) {
}
return null;
}
public boolean isDeltaCRL() {
try {
CRLExtensions exts = getExtensions();
if (exts == null)
return false;
Enumeration e = exts.getElements();
while (e.hasMoreElements()) {
Extension ext = (Extension) e.nextElement();
if (ext instanceof DeltaCRLIndicatorExtension) {
return true;
}
}
} catch (Exception e) {
}
return false;
}
/**
* Returns extensions for this impl.
*
* @param extn CRLExtensions
*/
public CRLExtensions getExtensions() {
return extensions;
}
public boolean areEntriesIncluded() {
return entriesIncluded;
}
/*********************************************************************/
/*
* Parses an X.509 CRL, should be used only by constructors.
*/
private void parse(DerValue val)
throws CRLException, IOException, X509ExtensionException {
parse(val, true);
}
private void parse(DerValue val, boolean includeEntries)
throws CRLException, IOException, X509ExtensionException {
// check if can over write the certificate
if (readOnly)
throw new CRLException("cannot over-write existing CRL");
readOnly = true;
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().toInt();
if (version != 1) // i.e. v2
throw new CRLException("Invalid version");
}
tmp = derStrm.getDerValue();
// signature
{
AlgorithmId tmpId = AlgorithmId.parse(tmp);
if (!tmpId.equals(sigAlgId))
throw new CRLException("Signature algorithm mismatch");
infoSigAlgId = tmpId;
}
// issuer
issuer = new X500Name(derStrm);
// 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))) {
if (includeEntries) {
DerValue[] badCerts = derStrm.getSequence(4);
for (int i = 0; i < badCerts.length; i++) {
RevokedCertImpl entry = new RevokedCertImpl(badCerts[i]);
if (entry.hasExtensions() && (version == 0))
throw new CRLException("Invalid encoding, extensions" +
" not supported in CRL v1 entries.");
revokedCerts.put(entry.getSerialNumber(),
(RevokedCertificate) entry);
}
} else {
derStrm.skipSequence(4);
}
}
if (derStrm.available() == 0)
return; // done parsing no extensions
// crlExtensions (optional)
tmp = derStrm.getDerValue();
if (tmp.isConstructed() && tmp.isContextSpecific((byte) 0)) {
if (version == 0)
throw new CRLException("Invalid encoding, extensions not" +
" supported in CRL v1.");
extensions = new CRLExtensions(tmp.data);
}
}
}