1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /** 19 * @author Alexander Y. Kleymenov 20 * @version $Revision$ 21 */ 22 23 package org.apache.harmony.security.x509; 24 25 import java.io.IOException; 26 import java.math.BigInteger; 27 import java.util.Arrays; 28 import java.util.Date; 29 import java.util.List; 30 import javax.security.auth.x500.X500Principal; 31 import org.apache.harmony.security.asn1.ASN1Explicit; 32 import org.apache.harmony.security.asn1.ASN1Integer; 33 import org.apache.harmony.security.asn1.ASN1Sequence; 34 import org.apache.harmony.security.asn1.ASN1SequenceOf; 35 import org.apache.harmony.security.asn1.ASN1Type; 36 import org.apache.harmony.security.asn1.BerInputStream; 37 import org.apache.harmony.security.x501.Name; 38 39 40 /** 41 * The class encapsulates the ASN.1 DER encoding/decoding work 42 * with TBSCertList structure which is the part of X.509 CRL 43 * (as specified in RFC 3280 - 44 * Internet X.509 Public Key Infrastructure. 45 * Certificate and Certificate Revocation List (CRL) Profile. 46 * http://www.ietf.org/rfc/rfc3280.txt): 47 * 48 * <pre> 49 * TBSCertList ::= SEQUENCE { 50 * version Version OPTIONAL, 51 * -- if present, MUST be v2 52 * signature AlgorithmIdentifier, 53 * issuer Name, 54 * thisUpdate Time, 55 * nextUpdate Time OPTIONAL, 56 * revokedCertificates SEQUENCE OF SEQUENCE { 57 * userCertificate CertificateSerialNumber, 58 * revocationDate Time, 59 * crlEntryExtensions Extensions OPTIONAL 60 * -- if present, MUST be v2 61 * } OPTIONAL, 62 * crlExtensions [0] EXPLICIT Extensions OPTIONAL 63 * -- if present, MUST be v2 64 * } 65 * </pre> 66 */ 67 public final class TBSCertList { 68 /** the value of version field of the structure */ 69 private final int version; 70 /** the value of signature field of the structure */ 71 private final AlgorithmIdentifier signature; 72 /** the value of issuer field of the structure */ 73 private final Name issuer; 74 /** the value of thisUpdate of the structure */ 75 private final Date thisUpdate; 76 /** the value of nextUpdate of the structure */ 77 private final Date nextUpdate; 78 /** the value of revokedCertificates of the structure */ 79 private final List<RevokedCertificate> revokedCertificates; 80 /** the value of crlExtensions field of the structure */ 81 private final Extensions crlExtensions; 82 /** the ASN.1 encoded form of TBSCertList */ 83 private byte[] encoding; 84 85 public static class RevokedCertificate { 86 private final BigInteger userCertificate; 87 private final Date revocationDate; 88 private final Extensions crlEntryExtensions; 89 90 private boolean issuerRetrieved; 91 private X500Principal issuer; 92 private byte[] encoding; 93 94 public RevokedCertificate(BigInteger userCertificate, 95 Date revocationDate, Extensions crlEntryExtensions) { 96 this.userCertificate = userCertificate; 97 this.revocationDate = revocationDate; 98 this.crlEntryExtensions = crlEntryExtensions; 99 } 100 101 public Extensions getCrlEntryExtensions() { 102 return crlEntryExtensions; 103 } 104 105 public BigInteger getUserCertificate() { 106 return userCertificate; 107 } 108 109 public Date getRevocationDate() { 110 return revocationDate; 111 } 112 113 /** 114 * Returns the value of Certificate Issuer Extension, if it is 115 * presented. 116 */ 117 public X500Principal getIssuer() { 118 if (crlEntryExtensions == null) { 119 return null; 120 } 121 if (!issuerRetrieved) { 122 try { 123 issuer = 124 crlEntryExtensions.valueOfCertificateIssuerExtension(); 125 } catch (IOException e) { 126 e.printStackTrace(); 127 } 128 issuerRetrieved = true; 129 } 130 return issuer; 131 } 132 133 public byte[] getEncoded() { 134 if (encoding == null) { 135 encoding = ASN1.encode(this); 136 } 137 return encoding; 138 } 139 140 public boolean equals(Object rc) { 141 if (!(rc instanceof RevokedCertificate)) { 142 return false; 143 } 144 RevokedCertificate rcert = (RevokedCertificate) rc; 145 return userCertificate.equals(rcert.userCertificate) 146 && ((revocationDate.getTime() / 1000) 147 == (rcert.revocationDate.getTime() / 1000)) 148 && ((crlEntryExtensions == null) 149 ? rcert.crlEntryExtensions == null 150 : crlEntryExtensions.equals(rcert.crlEntryExtensions)); 151 } 152 153 public int hashCode() { 154 return userCertificate.hashCode() * 37 + (int)revocationDate.getTime() / 1000 155 + (crlEntryExtensions == null ? 0 : crlEntryExtensions.hashCode()); 156 } 157 158 public void dumpValue(StringBuilder sb, String prefix) { 159 sb.append(prefix).append("Certificate Serial Number: ").append(userCertificate).append('\n'); 160 sb.append(prefix).append("Revocation Date: ").append(revocationDate); 161 if (crlEntryExtensions != null) { 162 sb.append('\n').append(prefix).append("CRL Entry Extensions: ["); 163 crlEntryExtensions.dumpValue(sb, prefix + " "); 164 sb.append(prefix).append(']'); 165 } 166 } 167 168 public static final ASN1Sequence ASN1 = new ASN1Sequence( 169 new ASN1Type[] {ASN1Integer.getInstance(), Time.ASN1, 170 Extensions.ASN1}) { 171 { 172 setOptional(2); 173 } 174 175 @Override protected Object getDecodedObject(BerInputStream in) { 176 Object[] values = (Object[]) in.content; 177 return new RevokedCertificate( 178 new BigInteger((byte[]) values[0]), 179 (Date) values[1], 180 (Extensions) values[2] 181 ); 182 } 183 184 @Override protected void getValues(Object object, Object[] values) { 185 RevokedCertificate rcert = (RevokedCertificate) object; 186 values[0] = rcert.userCertificate.toByteArray(); 187 values[1] = rcert.revocationDate; 188 values[2] = rcert.crlEntryExtensions; 189 } 190 }; 191 } 192 193 /** Constructs the object with associated ASN.1 encoding */ 194 private TBSCertList(int version, AlgorithmIdentifier signature, 195 Name issuer, Date thisUpdate, Date nextUpdate, 196 List<RevokedCertificate> revokedCertificates, Extensions crlExtensions, 197 byte[] encoding) { 198 this.version = version; 199 this.signature = signature; 200 this.issuer = issuer; 201 this.thisUpdate = thisUpdate; 202 this.nextUpdate = nextUpdate; 203 this.revokedCertificates = revokedCertificates; 204 this.crlExtensions = crlExtensions; 205 this.encoding = encoding; 206 } 207 208 /** 209 * Returns the value of version field of the structure. 210 */ 211 public int getVersion() { 212 return version; 213 } 214 215 /** 216 * Returns the value of signature field of the structure. 217 */ 218 public AlgorithmIdentifier getSignature() { 219 return signature; 220 } 221 222 /** 223 * Returns the value of issuer field of the structure. 224 */ 225 public Name getIssuer() { 226 return issuer; 227 } 228 229 /** 230 * Returns the value of thisUpdate field of the structure. 231 */ 232 public Date getThisUpdate() { 233 return thisUpdate; 234 } 235 236 /** 237 * Returns the value of nextUpdate field of the structure. 238 */ 239 public Date getNextUpdate() { 240 return nextUpdate; 241 } 242 243 /** 244 * Returns the value of revokedCertificates field of the structure. 245 */ 246 public List<RevokedCertificate> getRevokedCertificates() { 247 return revokedCertificates; 248 } 249 250 /** 251 * Returns the value of crlExtensions field of the structure. 252 */ 253 public Extensions getCrlExtensions() { 254 return crlExtensions; 255 } 256 257 /** 258 * Returns ASN.1 encoded form of this X.509 TBSCertList value. 259 */ 260 public byte[] getEncoded() { 261 if (encoding == null) { 262 encoding = ASN1.encode(this); 263 } 264 return encoding; 265 } 266 267 @Override public boolean equals(Object other) { 268 if (!(other instanceof TBSCertList)) { 269 return false; 270 } 271 TBSCertList that = (TBSCertList) other; 272 return version == that.version 273 && signature.equals(that.signature) 274 && Arrays.equals(issuer.getEncoded(), that.issuer.getEncoded()) 275 && thisUpdate.getTime() / 1000 276 == that.thisUpdate.getTime() / 1000 277 && (nextUpdate == null 278 ? that.nextUpdate == null 279 : nextUpdate.getTime() / 1000 280 == that.nextUpdate.getTime() / 1000) 281 && ((revokedCertificates == null || that.revokedCertificates == null) 282 && revokedCertificates == that.revokedCertificates 283 || revokedCertificates.equals(that.revokedCertificates)) 284 && (crlExtensions == null 285 ? that.crlExtensions == null 286 : crlExtensions.equals(that.crlExtensions)); 287 } 288 289 @Override public int hashCode() { 290 return ((version * 37 + signature.hashCode()) * 37 291 + Arrays.hashCode(issuer.getEncoded())) * 37 292 + (int)thisUpdate.getTime() / 1000; 293 } 294 295 public void dumpValue(StringBuilder sb) { 296 sb.append("X.509 CRL v").append(version); 297 sb.append("\nSignature Algorithm: ["); 298 signature.dumpValue(sb); 299 sb.append(']'); 300 sb.append("\nIssuer: ").append(issuer.getName(X500Principal.RFC2253)); 301 sb.append("\n\nThis Update: ").append(thisUpdate); 302 sb.append("\nNext Update: ").append(nextUpdate).append('\n'); 303 if (revokedCertificates != null) { 304 sb.append("\nRevoked Certificates: ").append(revokedCertificates.size()).append(" ["); 305 int number = 1; 306 for (RevokedCertificate revokedCertificate : revokedCertificates) { 307 sb.append("\n [").append(number++).append(']'); 308 revokedCertificate.dumpValue(sb, " "); 309 sb.append('\n'); 310 } 311 sb.append("]\n"); 312 } 313 if (crlExtensions != null) { 314 sb.append("\nCRL Extensions: ").append(crlExtensions.size()).append(" ["); 315 crlExtensions.dumpValue(sb, " "); 316 sb.append("]\n"); 317 } 318 } 319 320 /** 321 * X.509 TBSCertList encoder/decoder. 322 */ 323 public static final ASN1Sequence ASN1 = new ASN1Sequence(new ASN1Type[] { 324 ASN1Integer.getInstance(), // version 325 AlgorithmIdentifier.ASN1, // signature 326 Name.ASN1, // issuer 327 Time.ASN1, // thisUpdate 328 Time.ASN1, // nextUpdate 329 new ASN1SequenceOf(RevokedCertificate.ASN1), // revokedCertificates 330 new ASN1Explicit(0, Extensions.ASN1) // crlExtensions 331 }) { 332 { 333 setOptional(0); 334 setOptional(4); 335 setOptional(5); 336 setOptional(6); 337 } 338 339 @Override protected Object getDecodedObject(BerInputStream in) throws IOException { 340 Object[] values = (Object[]) in.content; 341 return new TBSCertList( 342 (values[0] == null) 343 ? 1 344 : ASN1Integer.toIntValue(values[0])+1, 345 (AlgorithmIdentifier) values[1], 346 (Name) values[2], 347 (Date) values[3], 348 (Date) values[4], 349 (List<RevokedCertificate>) values[5], 350 (Extensions) values[6], 351 in.getEncoded() 352 ); 353 } 354 355 @Override protected void getValues(Object object, Object[] values) { 356 TBSCertList tbs = (TBSCertList) object; 357 values[0] = (tbs.version > 1) 358 ? ASN1Integer.fromIntValue(tbs.version - 1) : null; 359 values[1] = tbs.signature; 360 values[2] = tbs.issuer; 361 values[3] = tbs.thisUpdate; 362 values[4] = tbs.nextUpdate; 363 values[5] = tbs.revokedCertificates; 364 values[6] = tbs.crlExtensions; 365 } 366 }; 367 } 368