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.Iterator; 30 import java.util.List; 31 import javax.security.auth.x500.X500Principal; 32 import org.apache.harmony.security.asn1.ASN1Explicit; 33 import org.apache.harmony.security.asn1.ASN1Integer; 34 import org.apache.harmony.security.asn1.ASN1Sequence; 35 import org.apache.harmony.security.asn1.ASN1SequenceOf; 36 import org.apache.harmony.security.asn1.ASN1Type; 37 import org.apache.harmony.security.asn1.BerInputStream; 38 import org.apache.harmony.security.x501.Name; 39 40 41 /** 42 * The class encapsulates the ASN.1 DER encoding/decoding work 43 * with TBSCertList structure which is the part of X.509 CRL 44 * (as specified in RFC 3280 - 45 * Internet X.509 Public Key Infrastructure. 46 * Certificate and Certificate Revocation List (CRL) Profile. 47 * http://www.ietf.org/rfc/rfc3280.txt): 48 * 49 * <pre> 50 * TBSCertList ::= SEQUENCE { 51 * version Version OPTIONAL, 52 * -- if present, MUST be v2 53 * signature AlgorithmIdentifier, 54 * issuer Name, 55 * thisUpdate Time, 56 * nextUpdate Time OPTIONAL, 57 * revokedCertificates SEQUENCE OF SEQUENCE { 58 * userCertificate CertificateSerialNumber, 59 * revocationDate Time, 60 * crlEntryExtensions Extensions OPTIONAL 61 * -- if present, MUST be v2 62 * } OPTIONAL, 63 * crlExtensions [0] EXPLICIT Extensions OPTIONAL 64 * -- if present, MUST be v2 65 * } 66 * </pre> 67 */ 68 public class TBSCertList { 69 70 // the value of version field of the structure 71 private final int version; 72 // the value of signature field of the structure 73 private final AlgorithmIdentifier signature; 74 // the value of issuer field of the structure 75 private final Name issuer; 76 // the value of thisUpdate of the structure 77 private final Date thisUpdate; 78 // the value of nextUpdate of the structure 79 private final Date nextUpdate; 80 // the value of revokedCertificates of the structure 81 private final List revokedCertificates; 82 // the value of crlExtensions field of the structure 83 private final Extensions crlExtensions; 84 // the ASN.1 encoded form of TBSCertList 85 private byte[] encoding; 86 87 public static class RevokedCertificate { 88 private final BigInteger userCertificate; 89 private final Date revocationDate; 90 private final Extensions crlEntryExtensions; 91 92 private boolean issuerRetrieved; 93 private X500Principal issuer; 94 private byte[] encoding; 95 96 public RevokedCertificate(BigInteger userCertificate, 97 Date revocationDate, Extensions crlEntryExtensions) { 98 this.userCertificate = userCertificate; 99 this.revocationDate = revocationDate; 100 this.crlEntryExtensions = crlEntryExtensions; 101 } 102 103 public Extensions getCrlEntryExtensions() { 104 return crlEntryExtensions; 105 } 106 107 public BigInteger getUserCertificate() { 108 return userCertificate; 109 } 110 111 public Date getRevocationDate() { 112 return revocationDate; 113 } 114 115 /** 116 * Returns the value of Certificate Issuer Extension, if it is 117 * presented. 118 */ 119 public X500Principal getIssuer() { 120 if (crlEntryExtensions == null) { 121 return null; 122 } 123 if (!issuerRetrieved) { 124 try { 125 issuer = 126 crlEntryExtensions.valueOfCertificateIssuerExtension(); 127 } catch (IOException e) { 128 e.printStackTrace(); 129 } 130 issuerRetrieved = true; 131 } 132 return issuer; 133 } 134 135 public byte[] getEncoded() { 136 if (encoding == null) { 137 encoding = ASN1.encode(this); 138 } 139 return encoding; 140 } 141 142 public boolean equals(Object rc) { 143 if (!(rc instanceof RevokedCertificate)) { 144 return false; 145 } 146 RevokedCertificate rcert = (RevokedCertificate) rc; 147 return userCertificate.equals(rcert.userCertificate) 148 && ((revocationDate.getTime() / 1000) 149 == (rcert.revocationDate.getTime() / 1000)) 150 && ((crlEntryExtensions == null) 151 ? rcert.crlEntryExtensions == null 152 : crlEntryExtensions.equals(rcert.crlEntryExtensions)); 153 } 154 155 public int hashCode() { 156 return userCertificate.hashCode() * 37 + (int)revocationDate.getTime() / 1000 157 + (crlEntryExtensions == null ? 0 : crlEntryExtensions.hashCode()); 158 } 159 160 /** 161 * Places the string representation of extension value 162 * into the StringBuffer object. 163 */ 164 public void dumpValue(StringBuffer buffer, String prefix) { 165 buffer.append(prefix).append("Certificate Serial Number: ") 166 .append(userCertificate).append('\n'); 167 buffer.append(prefix).append("Revocation Date: ") 168 .append(revocationDate); 169 if (crlEntryExtensions != null) { 170 buffer.append('\n').append(prefix) 171 .append("CRL Entry Extensions: ["); 172 crlEntryExtensions.dumpValue(buffer, prefix + " "); 173 buffer.append(prefix).append(']'); 174 } 175 } 176 177 public static final ASN1Sequence ASN1 = new ASN1Sequence( 178 new ASN1Type[] {ASN1Integer.getInstance(), Time.ASN1, 179 Extensions.ASN1}) { 180 { 181 setOptional(2); 182 } 183 184 protected Object getDecodedObject(BerInputStream in) { 185 Object[] values = (Object[]) in.content; 186 187 return new RevokedCertificate( 188 new BigInteger((byte[]) values[0]), 189 (Date) values[1], 190 (Extensions) values[2] 191 ); 192 } 193 194 protected void getValues(Object object, Object[] values) { 195 RevokedCertificate rcert = (RevokedCertificate) object; 196 197 values[0] = rcert.userCertificate.toByteArray(); 198 values[1] = rcert.revocationDate; 199 values[2] = rcert.crlEntryExtensions; 200 } 201 }; 202 } 203 204 /** 205 * Constructs the instance of TBSCertList without optional fields. 206 * Take a note, that regarding to the rfc 3280 (p. 49): 207 * "When CRLs are issued, the CRLs MUST be version 2 CRLs, include the date 208 * by which the next CRL will be issued in the nextUpdate field (section 209 * 5.1.2.5), include the CRL number extension (section 5.2.3), and include 210 * the authority key identifier extension (section 5.2.1). Conforming 211 * applications that support CRLs are REQUIRED to process both version 1 and 212 * version 2 complete CRLs that provide revocation information for all 213 * certificates issued by one CA. Conforming applications are NOT REQUIRED 214 * to support processing of delta CRLs, indirect CRLs, or CRLs with a scope 215 * other than all certificates issued by one CA." 216 * @param signature: AlgorithmIdentifier 217 * @param issuer: Name 218 * @param thisUpdate: Time 219 */ 220 public TBSCertList(AlgorithmIdentifier signature, 221 Name issuer, Date thisUpdate) { 222 this.version = 1; 223 this.signature = signature; 224 this.issuer = issuer; 225 this.thisUpdate = thisUpdate; 226 this.nextUpdate = null; 227 this.revokedCertificates = null; 228 this.crlExtensions = null; 229 } 230 231 /** 232 * Constructs the instance of TBSCertList with all optional fields 233 * @param version: version of the CRL. Should be 1 or 2. 234 * Note that if the version of CRL is 1, then nextUpdate, 235 * crlExtensions fields of CRL and crlEntryExtensions field 236 * of CRL entry must not be presented in CRL. 237 * FIXME: do check for it. 238 * @param signature: AlgorithmIdentifier 239 * @param issuer: Name 240 * @param thisUpdate: Time 241 * @param nextUpdate: Time 242 * @param revokedCertificates: List 243 * @param crlExtensions: Extensions 244 */ 245 public TBSCertList(int version, AlgorithmIdentifier signature, 246 Name issuer, Date thisUpdate, Date nextUpdate, 247 List revokedCertificates, Extensions crlExtensions) { 248 this.version = version; 249 this.signature = signature; 250 this.issuer = issuer; 251 this.thisUpdate = thisUpdate; 252 this.nextUpdate = nextUpdate; 253 this.revokedCertificates = revokedCertificates; 254 this.crlExtensions = crlExtensions; 255 } 256 257 // Constructs the object with associated ASN.1 encoding 258 private TBSCertList(int version, AlgorithmIdentifier signature, 259 Name issuer, Date thisUpdate, Date nextUpdate, 260 List revokedCertificates, Extensions crlExtensions, 261 byte[] encoding) { 262 this.version = version; 263 this.signature = signature; 264 this.issuer = issuer; 265 this.thisUpdate = thisUpdate; 266 this.nextUpdate = nextUpdate; 267 this.revokedCertificates = revokedCertificates; 268 this.crlExtensions = crlExtensions; 269 this.encoding = encoding; 270 } 271 272 /** 273 * Returns the value of version field of the structure. 274 * @return version 275 */ 276 public int getVersion() { 277 return version; 278 } 279 280 /** 281 * Returns the value of signature field of the structure. 282 * @return signature 283 */ 284 public AlgorithmIdentifier getSignature() { 285 return signature; 286 } 287 288 /** 289 * Returns the value of issuer field of the structure. 290 * @return issuer 291 */ 292 public Name getIssuer() { 293 return issuer; 294 } 295 296 /** 297 * Returns the value of thisUpdate field of the structure. 298 * @return thisUpdate 299 */ 300 public Date getThisUpdate() { 301 return thisUpdate; 302 } 303 304 /** 305 * Returns the value of nextUpdate field of the structure. 306 * @return nextUpdate 307 */ 308 public Date getNextUpdate() { 309 return nextUpdate; 310 } 311 312 /** 313 * Returns the value of revokedCertificates field of the structure. 314 * @return revokedCertificates 315 */ 316 public List getRevokedCertificates() { 317 return revokedCertificates; 318 } 319 320 /** 321 * Returns the value of crlExtensions field of the structure. 322 * @return extensions 323 */ 324 public Extensions getCrlExtensions() { 325 return crlExtensions; 326 } 327 328 /** 329 * Returns ASN.1 encoded form of this X.509 TBSCertList value. 330 * @return a byte array containing ASN.1 encode form. 331 */ 332 public byte[] getEncoded() { 333 if (encoding == null) { 334 encoding = ASN1.encode(this); 335 } 336 return encoding; 337 } 338 339 public boolean equals(Object tbs) { 340 if (!(tbs instanceof TBSCertList)) { 341 return false; 342 } 343 TBSCertList tbscert = (TBSCertList) tbs; 344 return (version == tbscert.version) 345 && (signature.equals(tbscert.signature)) 346 // FIXME use Name.equals when it will be implemented 347 && (Arrays.equals(issuer.getEncoded(), tbscert.issuer.getEncoded())) 348 && ((thisUpdate.getTime() / 1000) 349 == (tbscert.thisUpdate.getTime() / 1000)) 350 && ((nextUpdate == null) 351 ? tbscert.nextUpdate == null 352 : ((nextUpdate.getTime() / 1000) 353 == (tbscert.nextUpdate.getTime() / 1000))) 354 && ((((revokedCertificates == null) 355 || (tbscert.revokedCertificates == null)) 356 && (revokedCertificates == tbscert.revokedCertificates)) 357 || (revokedCertificates.containsAll(tbscert.revokedCertificates) 358 && (revokedCertificates.size() 359 == tbscert.revokedCertificates.size()))) 360 && ((crlExtensions == null) 361 ? tbscert.crlExtensions == null 362 : crlExtensions.equals(tbscert.crlExtensions)); 363 } 364 365 public int hashCode() { 366 return ((version * 37 + signature.hashCode()) * 37 367 + issuer.getEncoded().hashCode()) * 37 368 + (int)thisUpdate.getTime() / 1000; 369 } 370 371 /** 372 * Places the string representation of extension value 373 * into the StringBuffer object. 374 */ 375 public void dumpValue(StringBuffer buffer) { 376 buffer.append("X.509 CRL v").append(version); 377 buffer.append("\nSignature Algorithm: ["); 378 signature.dumpValue(buffer); 379 buffer.append(']'); 380 buffer.append("\nIssuer: ").append(issuer.getName(X500Principal.RFC2253)); 381 buffer.append("\n\nThis Update: ").append(thisUpdate); 382 buffer.append("\nNext Update: ").append(nextUpdate).append('\n'); 383 if (revokedCertificates != null) { 384 buffer.append("\nRevoked Certificates: ") 385 .append(revokedCertificates.size()).append(" ["); 386 int number = 1; 387 for (Iterator it = revokedCertificates.iterator();it.hasNext();) { 388 buffer.append("\n [").append(number++).append(']'); 389 ((RevokedCertificate) it.next()).dumpValue(buffer, " "); 390 buffer.append('\n'); 391 } 392 buffer.append("]\n"); 393 } 394 if (crlExtensions != null) { 395 buffer.append("\nCRL Extensions: ") 396 .append(crlExtensions.size()).append(" ["); 397 crlExtensions.dumpValue(buffer, " "); 398 buffer.append("]\n"); 399 } 400 } 401 402 /** 403 * X.509 TBSCertList encoder/decoder. 404 */ 405 public static final ASN1Sequence ASN1 = new ASN1Sequence(new ASN1Type[] { 406 ASN1Integer.getInstance(), // version 407 AlgorithmIdentifier.ASN1, // signature 408 Name.ASN1, // issuer 409 Time.ASN1, // thisUpdate 410 Time.ASN1, // nextUpdate 411 new ASN1SequenceOf(RevokedCertificate.ASN1), // revokedCertificates 412 new ASN1Explicit(0, Extensions.ASN1) // crlExtensions 413 }) { 414 { 415 setOptional(0); 416 setOptional(4); 417 setOptional(5); 418 setOptional(6); 419 } 420 421 protected Object getDecodedObject(BerInputStream in) 422 throws IOException { 423 Object[] values = (Object[]) in.content; 424 return new TBSCertList( 425 (values[0] == null) 426 ? 1 427 : ASN1Integer.toIntValue(values[0])+1, 428 (AlgorithmIdentifier) values[1], 429 (Name) values[2], 430 (Date) values[3], 431 (Date) values[4], 432 (List) values[5], 433 (Extensions) values[6], 434 in.getEncoded() 435 ); 436 } 437 438 protected void getValues(Object object, Object[] values) { 439 TBSCertList tbs = (TBSCertList) object; 440 values[0] = (tbs.version > 1) 441 ? ASN1Integer.fromIntValue(tbs.version - 1) : null; 442 values[1] = tbs.signature; 443 values[2] = tbs.issuer; 444 values[3] = tbs.thisUpdate; 445 values[4] = tbs.nextUpdate; 446 values[5] = tbs.revokedCertificates; 447 values[6] = tbs.crlExtensions; 448 } 449 }; 450 } 451 452