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.provider.cert; 24 25 import java.io.IOException; 26 import java.io.InputStream; 27 import java.math.BigInteger; 28 import java.security.InvalidKeyException; 29 import java.security.NoSuchAlgorithmException; 30 import java.security.NoSuchProviderException; 31 import java.security.Principal; 32 import java.security.PublicKey; 33 import java.security.Signature; 34 import java.security.SignatureException; 35 import java.security.cert.CRLException; 36 import java.security.cert.Certificate; 37 import java.security.cert.X509CRL; 38 import java.security.cert.X509CRLEntry; 39 import java.security.cert.X509Certificate; 40 import java.util.ArrayList; 41 import java.util.Date; 42 import java.util.HashSet; 43 import java.util.List; 44 import java.util.Set; 45 import javax.security.auth.x500.X500Principal; 46 import org.apache.harmony.security.utils.AlgNameMapper; 47 import org.apache.harmony.security.x509.CertificateList; 48 import org.apache.harmony.security.x509.Extension; 49 import org.apache.harmony.security.x509.Extensions; 50 import org.apache.harmony.security.x509.TBSCertList; 51 52 /** 53 * This class is an implementation of X509CRL. It wraps 54 * the instance of org.apache.harmony.security.x509.CertificateList 55 * built on the base of provided ASN.1 DER encoded form of 56 * CertificateList structure (as specified in RFC 3280 57 * http://www.ietf.org/rfc/rfc3280.txt). 58 * Implementation supports work with indirect CRLs. 59 * @see org.apache.harmony.security.x509.CertificateList 60 * @see java.security.cert.X509CRL 61 */ 62 public class X509CRLImpl extends X509CRL { 63 64 // the core object to be wrapped in X509CRL 65 private final CertificateList crl; 66 67 // To speed up access to the info, the following fields 68 // cache values retrieved from the CertificateList object 69 private final TBSCertList tbsCertList; 70 private byte[] tbsCertListEncoding; 71 private final Extensions extensions; 72 private X500Principal issuer; 73 private ArrayList entries; 74 private int entriesSize; 75 private byte[] signature; 76 private String sigAlgOID; 77 private String sigAlgName; 78 private byte[] sigAlgParams; 79 80 // encoded form of crl 81 private byte[] encoding; 82 83 // indicates whether the signature algorithm parameters are null 84 private boolean nullSigAlgParams; 85 // indicates whether the crl entries have already been retrieved 86 // from CertificateList object (crl) 87 private boolean entriesRetrieved; 88 89 // indicates whether this X.509 CRL is direct or indirect 90 // (see rfc 3280 http://www.ietf.org/rfc/rfc3280.txt, p 5.) 91 private boolean isIndirectCRL; 92 // if crl is indirect, this field holds an info about how 93 // many of the leading certificates in the list are issued 94 // by the same issuer as CRL. 95 private int nonIndirectEntriesSize; 96 97 /** 98 * Creates X.509 CRL by wrapping of the specified CertificateList object. 99 */ 100 public X509CRLImpl(CertificateList crl) { 101 this.crl = crl; 102 this.tbsCertList = crl.getTbsCertList(); 103 this.extensions = tbsCertList.getCrlExtensions(); 104 } 105 106 /** 107 * Creates X.509 CRL on the base of ASN.1 DER encoded form of 108 * the CRL (CertificateList structure described in RFC 3280) 109 * provided via input stream. 110 * @throws CRLException if decoding errors occur. 111 */ 112 public X509CRLImpl(InputStream in) throws CRLException { 113 try { 114 // decode CertificateList structure 115 this.crl = (CertificateList) CertificateList.ASN1.decode(in); 116 this.tbsCertList = crl.getTbsCertList(); 117 this.extensions = tbsCertList.getCrlExtensions(); 118 } catch (IOException e) { 119 throw new CRLException(e); 120 } 121 } 122 123 /** 124 * Creates X.509 CRL on the base of ASN.1 DER encoded form of 125 * the CRL (CertificateList structure described in RFC 3280) 126 * provided via array of bytes. 127 * @throws IOException if decoding errors occur. 128 */ 129 public X509CRLImpl(byte[] encoding) throws IOException { 130 this((CertificateList) CertificateList.ASN1.decode(encoding)); 131 } 132 133 // --------------------------------------------------------------------- 134 // ----- java.security.cert.X509CRL abstract method implementations ---- 135 // --------------------------------------------------------------------- 136 137 /** 138 * @see java.security.cert.X509CRL#getEncoded() 139 * method documentation for more info 140 */ 141 public byte[] getEncoded() throws CRLException { 142 if (encoding == null) { 143 encoding = crl.getEncoded(); 144 } 145 byte[] result = new byte[encoding.length]; 146 System.arraycopy(encoding, 0, result, 0, encoding.length); 147 return result; 148 } 149 150 /** 151 * @see java.security.cert.X509CRL#getVersion() 152 * method documentation for more info 153 */ 154 public int getVersion() { 155 return tbsCertList.getVersion(); 156 } 157 158 /** 159 * @see java.security.cert.X509CRL#getIssuerDN() 160 * method documentation for more info 161 */ 162 public Principal getIssuerDN() { 163 if (issuer == null) { 164 issuer = tbsCertList.getIssuer().getX500Principal(); 165 } 166 return issuer; 167 } 168 169 /** 170 * @see java.security.cert.X509CRL#getIssuerX500Principal() 171 * method documentation for more info 172 */ 173 public X500Principal getIssuerX500Principal() { 174 if (issuer == null) { 175 issuer = tbsCertList.getIssuer().getX500Principal(); 176 } 177 return issuer; 178 } 179 180 /** 181 * @see java.security.cert.X509CRL#getThisUpdate() 182 * method documentation for more info 183 */ 184 public Date getThisUpdate() { 185 return tbsCertList.getThisUpdate(); 186 } 187 188 /** 189 * @see java.security.cert.X509CRL#getNextUpdate() 190 * method documentation for more info 191 */ 192 public Date getNextUpdate() { 193 return tbsCertList.getNextUpdate(); 194 } 195 196 /* 197 * Retrieves the crl entries (TBSCertList.RevokedCertificate objects) 198 * from the TBSCertList structure and converts them to the 199 * X509CRLEntryImpl objects 200 */ 201 private void retrieveEntries() { 202 entriesRetrieved = true; 203 List rcerts = tbsCertList.getRevokedCertificates(); 204 if (rcerts == null) { 205 return; 206 } 207 entriesSize = rcerts.size(); 208 entries = new ArrayList(entriesSize); 209 // null means that revoked certificate issuer is the same as CRL issuer 210 X500Principal rcertIssuer = null; 211 for (int i=0; i<entriesSize; i++) { 212 TBSCertList.RevokedCertificate rcert = 213 (TBSCertList.RevokedCertificate) rcerts.get(i); 214 X500Principal iss = rcert.getIssuer(); 215 if (iss != null) { 216 // certificate issuer differs from CRL issuer 217 // and CRL is indirect. 218 rcertIssuer = iss; 219 isIndirectCRL = true; 220 // remember how many leading revoked certificates in the 221 // list are issued by the same issuer as issuer of CRL 222 // (these certificates are first in the list) 223 nonIndirectEntriesSize = i; 224 } 225 entries.add(new X509CRLEntryImpl(rcert, rcertIssuer)); 226 } 227 } 228 229 /** 230 * Searches for certificate in CRL. 231 * This method supports indirect CRLs: if CRL is indirect method takes 232 * into account serial number and issuer of the certificate, 233 * if CRL issued by CA (i.e. it is not indirect) search is done only 234 * by serial number of the specified certificate. 235 * @see java.security.cert.X509CRL#getRevokedCertificate(X509Certificate) 236 * method documentation for more info 237 */ 238 public X509CRLEntry getRevokedCertificate(X509Certificate certificate) { 239 if (certificate == null) { 240 throw new NullPointerException(); 241 } 242 if (!entriesRetrieved) { 243 retrieveEntries(); 244 } 245 if (entries == null) { 246 return null; 247 } 248 BigInteger serialN = certificate.getSerialNumber(); 249 if (isIndirectCRL) { 250 // search in indirect crl 251 X500Principal certIssuer = certificate.getIssuerX500Principal(); 252 if (certIssuer.equals(getIssuerX500Principal())) { 253 // certificate issuer is CRL issuer 254 certIssuer = null; 255 } 256 for (int i=0; i<entriesSize; i++) { 257 X509CRLEntry entry = (X509CRLEntry) entries.get(i); 258 // check the serial number of revoked certificate 259 if (serialN.equals(entry.getSerialNumber())) { 260 // revoked certificate issuer 261 X500Principal iss = entry.getCertificateIssuer(); 262 // check the issuer of revoked certificate 263 if (certIssuer != null) { 264 // certificate issuer is not a CRL issuer, so 265 // check issuers for equality 266 if (certIssuer.equals(iss)) { 267 return entry; 268 } 269 } else if (iss == null) { 270 // both certificates was issued by CRL issuer 271 return entry; 272 } 273 } 274 } 275 } else { 276 // search in CA's (non indirect) crl: just look up the serial number 277 for (int i=0; i<entriesSize; i++) { 278 X509CRLEntry entry = (X509CRLEntry) entries.get(i); 279 if (serialN.equals(entry.getSerialNumber())) { 280 return entry; 281 } 282 } 283 } 284 return null; 285 } 286 287 /** 288 * Method searches for CRL entry with specified serial number. 289 * The method will search only certificate issued by CRL's issuer. 290 * @see java.security.cert.X509CRL#getRevokedCertificate(BigInteger) 291 * method documentation for more info 292 */ 293 public X509CRLEntry getRevokedCertificate(BigInteger serialNumber) { 294 if (!entriesRetrieved) { 295 retrieveEntries(); 296 } 297 if (entries == null) { 298 return null; 299 } 300 for (int i=0; i<nonIndirectEntriesSize; i++) { 301 X509CRLEntry entry = (X509CRLEntry) entries.get(i); 302 if (serialNumber.equals(entry.getSerialNumber())) { 303 return entry; 304 } 305 } 306 return null; 307 } 308 309 /** 310 * @see java.security.cert.X509CRL#getRevokedCertificates() 311 * method documentation for more info 312 */ 313 public Set<? extends X509CRLEntry> getRevokedCertificates() { 314 if (!entriesRetrieved) { 315 retrieveEntries(); 316 } 317 if (entries == null) { 318 return null; 319 } 320 return new HashSet(entries); 321 } 322 323 /** 324 * @see java.security.cert.X509CRL#getTBSCertList() 325 * method documentation for more info 326 */ 327 public byte[] getTBSCertList() throws CRLException { 328 if (tbsCertListEncoding == null) { 329 tbsCertListEncoding = tbsCertList.getEncoded(); 330 } 331 byte[] result = new byte[tbsCertListEncoding.length]; 332 System.arraycopy(tbsCertListEncoding, 0, 333 result, 0, tbsCertListEncoding.length); 334 return result; 335 } 336 337 /** 338 * @see java.security.cert.X509CRL#getSignature() 339 * method documentation for more info 340 */ 341 public byte[] getSignature() { 342 if (signature == null) { 343 signature = crl.getSignatureValue(); 344 } 345 byte[] result = new byte[signature.length]; 346 System.arraycopy(signature, 0, result, 0, signature.length); 347 return result; 348 } 349 350 /** 351 * @see java.security.cert.X509CRL#getSigAlgName() 352 * method documentation for more info 353 */ 354 public String getSigAlgName() { 355 if (sigAlgOID == null) { 356 sigAlgOID = tbsCertList.getSignature().getAlgorithm(); 357 sigAlgName = AlgNameMapper.map2AlgName(sigAlgOID); 358 if (sigAlgName == null) { 359 sigAlgName = sigAlgOID; 360 } 361 } 362 return sigAlgName; 363 } 364 365 /** 366 * @see java.security.cert.X509CRL#getSigAlgOID() 367 * method documentation for more info 368 */ 369 public String getSigAlgOID() { 370 if (sigAlgOID == null) { 371 sigAlgOID = tbsCertList.getSignature().getAlgorithm(); 372 sigAlgName = AlgNameMapper.map2AlgName(sigAlgOID); 373 if (sigAlgName == null) { 374 sigAlgName = sigAlgOID; 375 } 376 } 377 return sigAlgOID; 378 } 379 380 /** 381 * @see java.security.cert.X509CRL#getSigAlgParams() 382 * method documentation for more info 383 */ 384 public byte[] getSigAlgParams() { 385 if (nullSigAlgParams) { 386 return null; 387 } 388 if (sigAlgParams == null) { 389 sigAlgParams = tbsCertList.getSignature().getParameters(); 390 if (sigAlgParams == null) { 391 nullSigAlgParams = true; 392 return null; 393 } 394 } 395 return sigAlgParams; 396 } 397 398 /** 399 * @see java.security.cert.X509CRL#verify(PublicKey key) 400 * method documentation for more info 401 */ 402 public void verify(PublicKey key) 403 throws CRLException, NoSuchAlgorithmException, 404 InvalidKeyException, NoSuchProviderException, 405 SignatureException { 406 Signature signature = Signature.getInstance(getSigAlgName()); 407 signature.initVerify(key); 408 byte[] tbsEncoding = tbsCertList.getEncoded(); 409 signature.update(tbsEncoding, 0, tbsEncoding.length); 410 if (!signature.verify(crl.getSignatureValue())) { 411 throw new SignatureException("Signature was not verified"); 412 } 413 } 414 415 /** 416 * @see java.security.cert.X509CRL#verify(PublicKey key, String sigProvider) 417 * method documentation for more info 418 */ 419 public void verify(PublicKey key, String sigProvider) 420 throws CRLException, NoSuchAlgorithmException, 421 InvalidKeyException, NoSuchProviderException, 422 SignatureException { 423 Signature signature = Signature.getInstance( 424 getSigAlgName(), sigProvider); 425 signature.initVerify(key); 426 byte[] tbsEncoding = tbsCertList.getEncoded(); 427 signature.update(tbsEncoding, 0, tbsEncoding.length); 428 if (!signature.verify(crl.getSignatureValue())) { 429 throw new SignatureException("Signature was not verified"); 430 } 431 } 432 433 // --------------------------------------------------------------------- 434 // ------ java.security.cert.CRL abstract method implementations ------- 435 // --------------------------------------------------------------------- 436 437 /** 438 * @see java.security.cert.CRL#isRevoked(Certificate) 439 * method documentation for more info 440 */ 441 public boolean isRevoked(Certificate cert) { 442 if (!(cert instanceof X509Certificate)) { 443 return false; 444 } 445 return getRevokedCertificate((X509Certificate) cert) != null; 446 } 447 448 /** 449 * @see java.security.cert.CRL#toString() 450 * method documentation for more info 451 */ 452 public String toString() { 453 return crl.toString(); 454 } 455 456 // --------------------------------------------------------------------- 457 // ------ java.security.cert.X509Extension method implementations ------ 458 // --------------------------------------------------------------------- 459 460 /** 461 * @see java.security.cert.X509Extension#getNonCriticalExtensionOIDs() 462 * method documentation for more info 463 */ 464 public Set getNonCriticalExtensionOIDs() { 465 if (extensions == null) { 466 return null; 467 } 468 return extensions.getNonCriticalExtensions(); 469 } 470 471 /** 472 * @see java.security.cert.X509Extension#getCriticalExtensionOIDs() 473 * method documentation for more info 474 */ 475 public Set getCriticalExtensionOIDs() { 476 if (extensions == null) { 477 return null; 478 } 479 return extensions.getCriticalExtensions(); 480 } 481 482 /** 483 * @see java.security.cert.X509Extension#getExtensionValue(String) 484 * method documentation for more info 485 */ 486 public byte[] getExtensionValue(String oid) { 487 if (extensions == null) { 488 return null; 489 } 490 Extension ext = extensions.getExtensionByOID(oid); 491 return (ext == null) ? null : ext.getRawExtnValue(); 492 } 493 494 /** 495 * @see java.security.cert.X509Extension#hasUnsupportedCriticalExtension() 496 * method documentation for more info 497 */ 498 public boolean hasUnsupportedCriticalExtension() { 499 if (extensions == null) { 500 return false; 501 } 502 return extensions.hasUnsupportedCritical(); 503 } 504 } 505