/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Alexander Y. Kleymenov
* @version $Revision$
*/
package org.apache.harmony.security.x509;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import javax.security.auth.x500.X500Principal;
import org.apache.harmony.security.asn1.ASN1Explicit;
import org.apache.harmony.security.asn1.ASN1Integer;
import org.apache.harmony.security.asn1.ASN1Sequence;
import org.apache.harmony.security.asn1.ASN1SequenceOf;
import org.apache.harmony.security.asn1.ASN1Type;
import org.apache.harmony.security.asn1.BerInputStream;
import org.apache.harmony.security.x501.Name;
/**
* The class encapsulates the ASN.1 DER encoding/decoding work
* with TBSCertList structure which is the part of X.509 CRL
* (as specified in RFC 3280 -
* Internet X.509 Public Key Infrastructure.
* Certificate and Certificate Revocation List (CRL) Profile.
* http://www.ietf.org/rfc/rfc3280.txt):
*
* <pre>
* TBSCertList ::= SEQUENCE {
* version Version OPTIONAL,
* -- if present, MUST be v2
* signature AlgorithmIdentifier,
* issuer Name,
* thisUpdate Time,
* nextUpdate Time OPTIONAL,
* revokedCertificates SEQUENCE OF SEQUENCE {
* userCertificate CertificateSerialNumber,
* revocationDate Time,
* crlEntryExtensions Extensions OPTIONAL
* -- if present, MUST be v2
* } OPTIONAL,
* crlExtensions [0] EXPLICIT Extensions OPTIONAL
* -- if present, MUST be v2
* }
* </pre>
*/
public final class TBSCertList {
/** the value of version field of the structure */
private final int version;
/** the value of signature field of the structure */
private final AlgorithmIdentifier signature;
/** the value of issuer field of the structure */
private final Name issuer;
/** the value of thisUpdate of the structure */
private final Date thisUpdate;
/** the value of nextUpdate of the structure */
private final Date nextUpdate;
/** the value of revokedCertificates of the structure */
private final List<RevokedCertificate> revokedCertificates;
/** the value of crlExtensions field of the structure */
private final Extensions crlExtensions;
/** the ASN.1 encoded form of TBSCertList */
private byte[] encoding;
public static class RevokedCertificate {
private final BigInteger userCertificate;
private final Date revocationDate;
private final Extensions crlEntryExtensions;
private boolean issuerRetrieved;
private X500Principal issuer;
private byte[] encoding;
public RevokedCertificate(BigInteger userCertificate,
Date revocationDate, Extensions crlEntryExtensions) {
this.userCertificate = userCertificate;
this.revocationDate = revocationDate;
this.crlEntryExtensions = crlEntryExtensions;
}
public Extensions getCrlEntryExtensions() {
return crlEntryExtensions;
}
public BigInteger getUserCertificate() {
return userCertificate;
}
public Date getRevocationDate() {
return revocationDate;
}
/**
* Returns the value of Certificate Issuer Extension, if it is
* presented.
*/
public X500Principal getIssuer() {
if (crlEntryExtensions == null) {
return null;
}
if (!issuerRetrieved) {
try {
issuer =
crlEntryExtensions.valueOfCertificateIssuerExtension();
} catch (IOException e) {
e.printStackTrace();
}
issuerRetrieved = true;
}
return issuer;
}
public byte[] getEncoded() {
if (encoding == null) {
encoding = ASN1.encode(this);
}
return encoding;
}
public boolean equals(Object rc) {
if (!(rc instanceof RevokedCertificate)) {
return false;
}
RevokedCertificate rcert = (RevokedCertificate) rc;
return userCertificate.equals(rcert.userCertificate)
&& ((revocationDate.getTime() / 1000)
== (rcert.revocationDate.getTime() / 1000))
&& ((crlEntryExtensions == null)
? rcert.crlEntryExtensions == null
: crlEntryExtensions.equals(rcert.crlEntryExtensions));
}
public int hashCode() {
return userCertificate.hashCode() * 37 + (int)revocationDate.getTime() / 1000
+ (crlEntryExtensions == null ? 0 : crlEntryExtensions.hashCode());
}
public void dumpValue(StringBuilder sb, String prefix) {
sb.append(prefix).append("Certificate Serial Number: ").append(userCertificate).append('\n');
sb.append(prefix).append("Revocation Date: ").append(revocationDate);
if (crlEntryExtensions != null) {
sb.append('\n').append(prefix).append("CRL Entry Extensions: [");
crlEntryExtensions.dumpValue(sb, prefix + " ");
sb.append(prefix).append(']');
}
}
public static final ASN1Sequence ASN1 = new ASN1Sequence(
new ASN1Type[] {ASN1Integer.getInstance(), Time.ASN1,
Extensions.ASN1}) {
{
setOptional(2);
}
@Override protected Object getDecodedObject(BerInputStream in) {
Object[] values = (Object[]) in.content;
return new RevokedCertificate(
new BigInteger((byte[]) values[0]),
(Date) values[1],
(Extensions) values[2]
);
}
@Override protected void getValues(Object object, Object[] values) {
RevokedCertificate rcert = (RevokedCertificate) object;
values[0] = rcert.userCertificate.toByteArray();
values[1] = rcert.revocationDate;
values[2] = rcert.crlEntryExtensions;
}
};
}
/** Constructs the object with associated ASN.1 encoding */
private TBSCertList(int version, AlgorithmIdentifier signature,
Name issuer, Date thisUpdate, Date nextUpdate,
List<RevokedCertificate> revokedCertificates, Extensions crlExtensions,
byte[] encoding) {
this.version = version;
this.signature = signature;
this.issuer = issuer;
this.thisUpdate = thisUpdate;
this.nextUpdate = nextUpdate;
this.revokedCertificates = revokedCertificates;
this.crlExtensions = crlExtensions;
this.encoding = encoding;
}
/**
* Returns the value of version field of the structure.
*/
public int getVersion() {
return version;
}
/**
* Returns the value of signature field of the structure.
*/
public AlgorithmIdentifier getSignature() {
return signature;
}
/**
* Returns the value of issuer field of the structure.
*/
public Name getIssuer() {
return issuer;
}
/**
* Returns the value of thisUpdate field of the structure.
*/
public Date getThisUpdate() {
return thisUpdate;
}
/**
* Returns the value of nextUpdate field of the structure.
*/
public Date getNextUpdate() {
return nextUpdate;
}
/**
* Returns the value of revokedCertificates field of the structure.
*/
public List<RevokedCertificate> getRevokedCertificates() {
return revokedCertificates;
}
/**
* Returns the value of crlExtensions field of the structure.
*/
public Extensions getCrlExtensions() {
return crlExtensions;
}
/**
* Returns ASN.1 encoded form of this X.509 TBSCertList value.
*/
public byte[] getEncoded() {
if (encoding == null) {
encoding = ASN1.encode(this);
}
return encoding;
}
@Override public boolean equals(Object other) {
if (!(other instanceof TBSCertList)) {
return false;
}
TBSCertList that = (TBSCertList) other;
return version == that.version
&& signature.equals(that.signature)
&& Arrays.equals(issuer.getEncoded(), that.issuer.getEncoded())
&& thisUpdate.getTime() / 1000
== that.thisUpdate.getTime() / 1000
&& (nextUpdate == null
? that.nextUpdate == null
: nextUpdate.getTime() / 1000
== that.nextUpdate.getTime() / 1000)
&& ((revokedCertificates == null || that.revokedCertificates == null)
&& revokedCertificates == that.revokedCertificates
|| revokedCertificates.equals(that.revokedCertificates))
&& (crlExtensions == null
? that.crlExtensions == null
: crlExtensions.equals(that.crlExtensions));
}
@Override public int hashCode() {
return ((version * 37 + signature.hashCode()) * 37
+ Arrays.hashCode(issuer.getEncoded())) * 37
+ (int)thisUpdate.getTime() / 1000;
}
public void dumpValue(StringBuilder sb) {
sb.append("X.509 CRL v").append(version);
sb.append("\nSignature Algorithm: [");
signature.dumpValue(sb);
sb.append(']');
sb.append("\nIssuer: ").append(issuer.getName(X500Principal.RFC2253));
sb.append("\n\nThis Update: ").append(thisUpdate);
sb.append("\nNext Update: ").append(nextUpdate).append('\n');
if (revokedCertificates != null) {
sb.append("\nRevoked Certificates: ").append(revokedCertificates.size()).append(" [");
int number = 1;
for (RevokedCertificate revokedCertificate : revokedCertificates) {
sb.append("\n [").append(number++).append(']');
revokedCertificate.dumpValue(sb, " ");
sb.append('\n');
}
sb.append("]\n");
}
if (crlExtensions != null) {
sb.append("\nCRL Extensions: ").append(crlExtensions.size()).append(" [");
crlExtensions.dumpValue(sb, " ");
sb.append("]\n");
}
}
/**
* X.509 TBSCertList encoder/decoder.
*/
public static final ASN1Sequence ASN1 = new ASN1Sequence(new ASN1Type[] {
ASN1Integer.getInstance(), // version
AlgorithmIdentifier.ASN1, // signature
Name.ASN1, // issuer
Time.ASN1, // thisUpdate
Time.ASN1, // nextUpdate
new ASN1SequenceOf(RevokedCertificate.ASN1), // revokedCertificates
new ASN1Explicit(0, Extensions.ASN1) // crlExtensions
}) {
{
setOptional(0);
setOptional(4);
setOptional(5);
setOptional(6);
}
@Override protected Object getDecodedObject(BerInputStream in) throws IOException {
Object[] values = (Object[]) in.content;
return new TBSCertList(
(values[0] == null)
? 1
: ASN1Integer.toIntValue(values[0])+1,
(AlgorithmIdentifier) values[1],
(Name) values[2],
(Date) values[3],
(Date) values[4],
(List<RevokedCertificate>) values[5],
(Extensions) values[6],
in.getEncoded()
);
}
@Override protected void getValues(Object object, Object[] values) {
TBSCertList tbs = (TBSCertList) object;
values[0] = (tbs.version > 1)
? ASN1Integer.fromIntValue(tbs.version - 1) : null;
values[1] = tbs.signature;
values[2] = tbs.issuer;
values[3] = tbs.thisUpdate;
values[4] = tbs.nextUpdate;
values[5] = tbs.revokedCertificates;
values[6] = tbs.crlExtensions;
}
};
}