|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.x509; |
|
|
|
import java.io.IOException; |
|
import java.security.cert.CRLException; |
|
import java.security.cert.CRLReason; |
|
import java.security.cert.X509CRLEntry; |
|
import java.math.BigInteger; |
|
import java.util.*; |
|
|
|
import javax.security.auth.x500.X500Principal; |
|
|
|
import sun.security.util.*; |
|
import sun.security.util.HexDumpEncoder; |
|
|
|
/** |
|
* <p>Abstract class for a revoked certificate in a CRL. |
|
* This class is for each entry in the <code>revokedCertificates</code>, |
|
* so it deals with the inner <em>SEQUENCE</em>. |
|
* The ASN.1 definition for this is: |
|
* <pre> |
|
* revokedCertificates SEQUENCE OF SEQUENCE { |
|
* userCertificate CertificateSerialNumber, |
|
* revocationDate ChoiceOfTime, |
|
* crlEntryExtensions Extensions OPTIONAL |
|
* -- if present, must be v2 |
|
* } OPTIONAL |
|
* |
|
* CertificateSerialNumber ::= INTEGER |
|
* |
|
* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension |
|
* |
|
* Extension ::= SEQUENCE { |
|
* extnId OBJECT IDENTIFIER, |
|
* critical BOOLEAN DEFAULT FALSE, |
|
* extnValue OCTET STRING |
|
* -- contains a DER encoding of a value |
|
* -- of the type registered for use with |
|
* -- the extnId object identifier value |
|
* } |
|
* </pre> |
|
* |
|
* @author Hemma Prafullchandra |
|
*/ |
|
|
|
public class X509CRLEntryImpl extends X509CRLEntry |
|
implements Comparable<X509CRLEntryImpl> { |
|
|
|
private SerialNumber serialNumber = null; |
|
private Date revocationDate = null; |
|
private CRLExtensions extensions = null; |
|
private byte[] revokedCert = null; |
|
private X500Principal certIssuer; |
|
|
|
private static final boolean isExplicit = false; |
|
private static final long YR_2050 = 2524636800000L; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public X509CRLEntryImpl(BigInteger num, Date date) { |
|
this.serialNumber = new SerialNumber(num); |
|
this.revocationDate = date; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public X509CRLEntryImpl(BigInteger num, Date date, |
|
CRLExtensions crlEntryExts) { |
|
this.serialNumber = new SerialNumber(num); |
|
this.revocationDate = date; |
|
this.extensions = crlEntryExts; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public X509CRLEntryImpl(byte[] revokedCert) throws CRLException { |
|
try { |
|
parse(new DerValue(revokedCert)); |
|
} catch (IOException e) { |
|
this.revokedCert = null; |
|
throw new CRLException("Parsing error: " + e.toString()); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public X509CRLEntryImpl(DerValue derValue) throws CRLException { |
|
try { |
|
parse(derValue); |
|
} catch (IOException e) { |
|
revokedCert = null; |
|
throw new CRLException("Parsing error: " + e.toString()); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean hasExtensions() { |
|
return (extensions != null); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void encode(DerOutputStream outStrm) throws CRLException { |
|
try { |
|
if (revokedCert == null) { |
|
DerOutputStream tmp = new DerOutputStream(); |
|
|
|
serialNumber.encode(tmp); |
|
|
|
if (revocationDate.getTime() < YR_2050) { |
|
tmp.putUTCTime(revocationDate); |
|
} else { |
|
tmp.putGeneralizedTime(revocationDate); |
|
} |
|
|
|
if (extensions != null) |
|
extensions.encode(tmp, isExplicit); |
|
|
|
DerOutputStream seq = new DerOutputStream(); |
|
seq.write(DerValue.tag_Sequence, tmp); |
|
|
|
revokedCert = seq.toByteArray(); |
|
} |
|
outStrm.write(revokedCert); |
|
} catch (IOException e) { |
|
throw new CRLException("Encoding error: " + e.toString()); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public byte[] getEncoded() throws CRLException { |
|
return getEncoded0().clone(); |
|
} |
|
|
|
|
|
private byte[] getEncoded0() throws CRLException { |
|
if (revokedCert == null) |
|
this.encode(new DerOutputStream()); |
|
return revokedCert; |
|
} |
|
|
|
@Override |
|
public X500Principal getCertificateIssuer() { |
|
return certIssuer; |
|
} |
|
|
|
void setCertificateIssuer(X500Principal crlIssuer, X500Principal certIssuer) { |
|
if (crlIssuer.equals(certIssuer)) { |
|
this.certIssuer = null; |
|
} else { |
|
this.certIssuer = certIssuer; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public BigInteger getSerialNumber() { |
|
return serialNumber.getNumber(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Date getRevocationDate() { |
|
return new Date(revocationDate.getTime()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public CRLReason getRevocationReason() { |
|
Extension ext = getExtension(PKIXExtensions.ReasonCode_Id); |
|
if (ext == null) { |
|
return null; |
|
} |
|
CRLReasonCodeExtension rcExt = (CRLReasonCodeExtension) ext; |
|
return rcExt.getReasonCode(); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public static CRLReason getRevocationReason(X509CRLEntry crlEntry) { |
|
try { |
|
byte[] ext = crlEntry.getExtensionValue("2.5.29.21"); |
|
if (ext == null) { |
|
return null; |
|
} |
|
DerValue val = new DerValue(ext); |
|
byte[] data = val.getOctetString(); |
|
|
|
CRLReasonCodeExtension rcExt = |
|
new CRLReasonCodeExtension(Boolean.FALSE, data); |
|
return rcExt.getReasonCode(); |
|
} catch (IOException ioe) { |
|
return null; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Integer getReasonCode() throws IOException { |
|
Object obj = getExtension(PKIXExtensions.ReasonCode_Id); |
|
if (obj == null) |
|
return null; |
|
CRLReasonCodeExtension reasonCode = (CRLReasonCodeExtension)obj; |
|
return reasonCode.get(CRLReasonCodeExtension.REASON); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public String toString() { |
|
StringBuilder sb = new StringBuilder(); |
|
|
|
sb.append(serialNumber) |
|
.append(" On: ") |
|
.append(revocationDate); |
|
if (certIssuer != null) { |
|
sb.append("\n Certificate issuer: ") |
|
.append(certIssuer); |
|
} |
|
if (extensions != null) { |
|
Collection<Extension> allEntryExts = extensions.getAllExtensions(); |
|
Extension[] exts = allEntryExts.toArray(new Extension[0]); |
|
|
|
sb.append("\n CRL Entry Extensions: ") |
|
.append(exts.length); |
|
for (int i = 0; i < exts.length; i++) { |
|
sb.append("\n [") |
|
.append(i+1) |
|
.append("]: "); |
|
Extension ext = exts[i]; |
|
try { |
|
if (OIDMap.getClass(ext.getExtensionId()) == null) { |
|
sb.append(ext); |
|
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: ") |
|
.append("DER encoded OCTET string =\n") |
|
.append(enc.encodeBuffer(extValue)) |
|
.append('\n'); |
|
} |
|
} else { |
|
sb.append(ext); |
|
} |
|
} catch (Exception e) { |
|
sb.append(", Error parsing this extension"); |
|
} |
|
} |
|
} |
|
sb.append('\n'); |
|
return sb.toString(); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean hasUnsupportedCriticalExtension() { |
|
if (extensions == null) |
|
return false; |
|
return extensions.hasUnsupportedCriticalExtension(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public byte[] getExtensionValue(String oid) { |
|
if (extensions == null) |
|
return null; |
|
try { |
|
String extAlias = OIDMap.getName(new ObjectIdentifier(oid)); |
|
Extension crlExt = null; |
|
|
|
if (extAlias == null) { |
|
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(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 Extension getExtension(ObjectIdentifier oid) { |
|
if (extensions == null) |
|
return null; |
|
|
|
// following returns null if no such OID in map |
|
|
|
return extensions.get(OIDMap.getName(oid)); |
|
} |
|
|
|
private void parse(DerValue derVal) |
|
throws CRLException, IOException { |
|
|
|
if (derVal.tag != DerValue.tag_Sequence) { |
|
throw new CRLException("Invalid encoded RevokedCertificate, " + |
|
"starting sequence tag missing."); |
|
} |
|
if (derVal.data.available() == 0) |
|
throw new CRLException("No data encoded for RevokedCertificates"); |
|
|
|
revokedCert = derVal.toByteArray(); |
|
|
|
DerInputStream in = derVal.toDerInputStream(); |
|
DerValue val = in.getDerValue(); |
|
this.serialNumber = new SerialNumber(val); |
|
|
|
|
|
int nextByte = derVal.data.peekByte(); |
|
if ((byte)nextByte == DerValue.tag_UtcTime) { |
|
this.revocationDate = derVal.data.getUTCTime(); |
|
} else if ((byte)nextByte == DerValue.tag_GeneralizedTime) { |
|
this.revocationDate = derVal.data.getGeneralizedTime(); |
|
} else |
|
throw new CRLException("Invalid encoding for revocation date"); |
|
|
|
if (derVal.data.available() == 0) |
|
return; |
|
|
|
|
|
this.extensions = new CRLExtensions(derVal.toDerInputStream()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static X509CRLEntryImpl toImpl(X509CRLEntry entry) |
|
throws CRLException { |
|
if (entry instanceof X509CRLEntryImpl) { |
|
return (X509CRLEntryImpl)entry; |
|
} else { |
|
return new X509CRLEntryImpl(entry.getEncoded()); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
CertificateIssuerExtension getCertificateIssuerExtension() { |
|
return (CertificateIssuerExtension) |
|
getExtension(PKIXExtensions.CertificateIssuer_Id); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public Map<String, java.security.cert.Extension> getExtensions() { |
|
if (extensions == null) { |
|
return Collections.emptyMap(); |
|
} |
|
Collection<Extension> exts = extensions.getAllExtensions(); |
|
Map<String, java.security.cert.Extension> map = new TreeMap<>(); |
|
for (Extension ext : exts) { |
|
map.put(ext.getId(), ext); |
|
} |
|
return map; |
|
} |
|
|
|
@Override |
|
public int compareTo(X509CRLEntryImpl that) { |
|
int compSerial = getSerialNumber().compareTo(that.getSerialNumber()); |
|
if (compSerial != 0) { |
|
return compSerial; |
|
} |
|
try { |
|
byte[] thisEncoded = this.getEncoded0(); |
|
byte[] thatEncoded = that.getEncoded0(); |
|
for (int i=0; i<thisEncoded.length && i<thatEncoded.length; i++) { |
|
int a = thisEncoded[i] & 0xff; |
|
int b = thatEncoded[i] & 0xff; |
|
if (a != b) return a-b; |
|
} |
|
return thisEncoded.length -thatEncoded.length; |
|
} catch (CRLException ce) { |
|
return -1; |
|
} |
|
} |
|
} |