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.CertificateEncodingException; 36 import java.security.cert.CertificateException; 37 import java.security.cert.CertificateExpiredException; 38 import java.security.cert.CertificateNotYetValidException; 39 import java.security.cert.CertificateParsingException; 40 import java.security.cert.X509Certificate; 41 import java.security.interfaces.RSAPublicKey; 42 import java.util.Collection; 43 import java.util.Date; 44 import java.util.List; 45 import java.util.Set; 46 import javax.security.auth.x500.X500Principal; 47 import org.apache.harmony.security.utils.AlgNameMapper; 48 import org.apache.harmony.security.x509.Certificate; 49 import org.apache.harmony.security.x509.Extension; 50 import org.apache.harmony.security.x509.Extensions; 51 import org.apache.harmony.security.x509.TBSCertificate; 52 import org.apache.harmony.xnet.provider.jsse.NativeCrypto; 53 54 /** 55 * This class is an implementation of X509Certificate. It wraps 56 * the instance of org.apache.harmony.security.x509.Certificate 57 * built on the base of provided ASN.1 DER encoded form of 58 * Certificate structure (as specified in RFC 3280 59 * http://www.ietf.org/rfc/rfc3280.txt). 60 * @see org.apache.harmony.security.x509.Certificate 61 * @see java.security.cert.X509Certificate 62 */ 63 public class X509CertImpl extends X509Certificate { 64 65 /** 66 * @serial 67 */ 68 private static final long serialVersionUID = 2972248729446736154L; 69 70 // the core object to be wrapped in X509Certificate 71 private final Certificate certificate; 72 73 // to speed up access to the info, the following fields 74 // cache values retrieved from the certificate object, 75 // initialized using the "single-check idiom". 76 private final TBSCertificate tbsCert; 77 private final Extensions extensions; 78 private volatile long notBefore = -1; 79 private volatile long notAfter = -1; 80 private volatile BigInteger serialNumber; 81 private volatile X500Principal issuer; 82 private volatile X500Principal subject; 83 private volatile byte[] tbsCertificate; 84 private volatile byte[] signature; 85 private volatile String sigAlgName; 86 private volatile String sigAlgOID; 87 private volatile byte[] sigAlgParams; 88 // indicates whether the signature algorithm parameters are null 89 private volatile boolean nullSigAlgParams; 90 private volatile PublicKey publicKey; 91 92 // encoding of the certificate 93 private volatile byte[] encoding; 94 95 // 96 // ---------------------- Constructors ------------------------------- 97 // 98 99 /** 100 * Constructs the instance on the base of ASN.1 encoded 101 * form of X.509 certificate provided via stream parameter. 102 * @param in input stream containing ASN.1 encoded form of certificate. 103 * @throws CertificateException if some decoding problems occur. 104 */ 105 public X509CertImpl(InputStream in) throws CertificateException { 106 try { 107 // decode the Certificate object 108 this.certificate = (Certificate) Certificate.ASN1.decode(in); 109 // cache the values of TBSCertificate and Extensions 110 this.tbsCert = certificate.getTbsCertificate(); 111 this.extensions = tbsCert.getExtensions(); 112 } catch (IOException e) { 113 throw new CertificateException(e); 114 } 115 } 116 117 /** 118 * Constructs the instance on the base of existing Certificate object to 119 * be wrapped. 120 */ 121 public X509CertImpl(Certificate certificate) { 122 this.certificate = certificate; 123 // cache the values of TBSCertificate and Extensions 124 this.tbsCert = certificate.getTbsCertificate(); 125 this.extensions = tbsCert.getExtensions(); 126 } 127 128 /** 129 * Constructs the instance on the base of ASN.1 encoded 130 * form of X.509 certificate provided via array of bytes. 131 * @param encoding byte array containing ASN.1 encoded form of certificate. 132 * @throws IOException if some decoding problems occur. 133 */ 134 public X509CertImpl(byte[] encoding) throws IOException { 135 this((Certificate) Certificate.ASN1.decode(encoding)); 136 } 137 138 // 139 // ----------------- Public methods implementations ------------------ 140 // 141 142 /** 143 * @see java.security.cert.X509Certificate#checkValidity() 144 * method documentation for more information. 145 */ 146 public void checkValidity() 147 throws CertificateExpiredException, CertificateNotYetValidException { 148 checkValidity(System.currentTimeMillis()); 149 } 150 151 /** 152 * @see java.security.cert.X509Certificate#checkValidity(Date) 153 * method documentation for more information. 154 */ 155 public void checkValidity(Date date) 156 throws CertificateExpiredException, CertificateNotYetValidException { 157 checkValidity(date.getTime()); 158 } 159 160 private void checkValidity(long time) 161 throws CertificateExpiredException, CertificateNotYetValidException { 162 if (time < getNotBeforeInternal()) { 163 throw new CertificateNotYetValidException("current time: " + new Date(time) 164 + ", validation time: " + new Date(getNotBeforeInternal())); 165 } 166 if (time > getNotAfterInternal()) { 167 throw new CertificateExpiredException("current time: " + new Date(time) 168 + ", expiration time: " + new Date(getNotAfterInternal())); 169 } 170 } 171 172 /** 173 * @see java.security.cert.X509Certificate#getVersion() 174 * method documentation for more information. 175 */ 176 public int getVersion() { 177 return tbsCert.getVersion() + 1; 178 } 179 180 /** 181 * @see java.security.cert.X509Certificate#getSerialNumber() 182 * method documentation for more information. 183 */ 184 public BigInteger getSerialNumber() { 185 BigInteger result = serialNumber; 186 if (result == null) { 187 serialNumber = result = tbsCert.getSerialNumber(); 188 } 189 return result; 190 } 191 192 /** 193 * @see java.security.cert.X509Certificate#getIssuerDN() 194 * method documentation for more information. 195 */ 196 public Principal getIssuerDN() { 197 return getIssuerX500Principal(); 198 } 199 200 /** 201 * @see java.security.cert.X509Certificate#getIssuerX500Principal() 202 * method documentation for more information. 203 */ 204 public X500Principal getIssuerX500Principal() { 205 X500Principal result = issuer; 206 if (result == null) { 207 // retrieve the issuer's principal 208 issuer = result = tbsCert.getIssuer().getX500Principal(); 209 } 210 return result; 211 } 212 213 /** 214 * @see java.security.cert.X509Certificate#getSubjectDN() 215 * method documentation for more information. 216 */ 217 public Principal getSubjectDN() { 218 return getSubjectX500Principal(); 219 } 220 221 /** 222 * @see java.security.cert.X509Certificate#getSubjectX500Principal() 223 * method documentation for more information. 224 */ 225 public X500Principal getSubjectX500Principal() { 226 X500Principal result = subject; 227 if (result == null) { 228 // retrieve the subject's principal 229 subject = result = tbsCert.getSubject().getX500Principal(); 230 } 231 return result; 232 } 233 234 /** 235 * @see java.security.cert.X509Certificate#getNotBefore() 236 * method documentation for more information. 237 */ 238 public Date getNotBefore() { 239 return new Date(getNotBeforeInternal()); 240 } 241 private long getNotBeforeInternal() { 242 long result = notBefore; 243 if (result == -1) { 244 notBefore = result = tbsCert.getValidity().getNotBefore().getTime(); 245 } 246 return result; 247 } 248 249 /** 250 * @see java.security.cert.X509Certificate#getNotAfter() 251 * method documentation for more information. 252 */ 253 public Date getNotAfter() { 254 return new Date(getNotAfterInternal()); 255 } 256 private long getNotAfterInternal() { 257 long result = notAfter; 258 if (result == -1) { 259 notAfter = result = tbsCert.getValidity().getNotAfter().getTime(); 260 } 261 return result; 262 } 263 264 /** 265 * @see java.security.cert.X509Certificate#getTBSCertificate() 266 * method documentation for more information. 267 */ 268 public byte[] getTBSCertificate() throws CertificateEncodingException { 269 return getTbsCertificateInternal().clone(); 270 } 271 private byte[] getTbsCertificateInternal() { 272 byte[] result = tbsCertificate; 273 if (result == null) { 274 tbsCertificate = result = tbsCert.getEncoded(); 275 } 276 return result; 277 } 278 279 /** 280 * @see java.security.cert.X509Certificate#getSignature() 281 * method documentation for more information. 282 */ 283 public byte[] getSignature() { 284 return getSignatureInternal().clone(); 285 } 286 private byte[] getSignatureInternal() { 287 byte[] result = signature; 288 if (result == null) { 289 signature = result = certificate.getSignatureValue(); 290 } 291 return result; 292 } 293 294 /** 295 * @see java.security.cert.X509Certificate#getSigAlgName() 296 * method documentation for more information. 297 */ 298 public String getSigAlgName() { 299 String result = sigAlgName; 300 if (result == null) { 301 String sigAlgOIDLocal = getSigAlgOID(); 302 // retrieve the name of the signing algorithm 303 result = AlgNameMapper.map2AlgName(sigAlgOIDLocal); 304 if (result == null) { 305 // if could not be found, use OID as a name 306 result = sigAlgOIDLocal; 307 } 308 sigAlgName = result; 309 } 310 return result; 311 } 312 313 /** 314 * @see java.security.cert.X509Certificate#getSigAlgOID() 315 * method documentation for more information. 316 */ 317 public String getSigAlgOID() { 318 String result = sigAlgOID; 319 if (result == null) { 320 // if info was not retrieved (and cached), do it: 321 sigAlgOID = result = tbsCert.getSignature().getAlgorithm(); 322 } 323 return result; 324 } 325 326 /** 327 * @see java.security.cert.X509Certificate#getSigAlgParams() 328 * method documentation for more information. 329 */ 330 public byte[] getSigAlgParams() { 331 if (nullSigAlgParams) { 332 return null; 333 } 334 byte[] result = sigAlgParams; 335 if (result == null) { 336 result = tbsCert.getSignature().getParameters(); 337 if (result == null) { 338 nullSigAlgParams = true; 339 return null; 340 } 341 sigAlgParams = result; 342 } 343 return result; 344 } 345 346 /** 347 * @see java.security.cert.X509Certificate#getIssuerUniqueID() 348 * method documentation for more information. 349 */ 350 public boolean[] getIssuerUniqueID() { 351 return tbsCert.getIssuerUniqueID(); 352 } 353 354 /** 355 * @see java.security.cert.X509Certificate#getSubjectUniqueID() 356 * method documentation for more information. 357 */ 358 public boolean[] getSubjectUniqueID() { 359 return tbsCert.getSubjectUniqueID(); 360 } 361 362 /** 363 * @see java.security.cert.X509Certificate#getKeyUsage() 364 * method documentation for more information. 365 */ 366 public boolean[] getKeyUsage() { 367 if (extensions == null) { 368 return null; 369 } 370 return extensions.valueOfKeyUsage(); 371 } 372 373 /** 374 * @see java.security.cert.X509Certificate#getExtendedKeyUsage() 375 * method documentation for more information. 376 */ 377 public List/*<String>*/ getExtendedKeyUsage() 378 throws CertificateParsingException { 379 if (extensions == null) { 380 return null; 381 } 382 try { 383 return extensions.valueOfExtendedKeyUsage(); 384 } catch (IOException e) { 385 throw new CertificateParsingException(e); 386 } 387 } 388 389 /** 390 * @see java.security.cert.X509Certificate#getBasicConstraints() 391 * method documentation for more information. 392 */ 393 public int getBasicConstraints() { 394 if (extensions == null) { 395 return Integer.MAX_VALUE; 396 } 397 return extensions.valueOfBasicConstrains(); 398 } 399 400 /** 401 * @see java.security.cert.X509Certificate#getSubjectAlternativeNames() 402 * method documentation for more information. 403 */ 404 public Collection/*<List<?>>*/ getSubjectAlternativeNames() 405 throws CertificateParsingException { 406 if (extensions == null) { 407 return null; 408 } 409 try { 410 // Retrieve the extension value from the cached extensions object 411 // This extension is not checked for correctness during 412 // certificate generation, so now it can throw exception 413 return extensions.valueOfSubjectAlternativeName(); 414 } catch (IOException e) { 415 throw new CertificateParsingException(e); 416 } 417 } 418 419 /** 420 * @see java.security.cert.X509Certificate#getIssuerAlternativeNames() 421 * method documentation for more information. 422 */ 423 public Collection/*FIXME <List<?>>*/ getIssuerAlternativeNames() 424 throws CertificateParsingException { 425 if (extensions == null) { 426 return null; 427 } 428 try { 429 // Retrieve the extension value from the cached extensions object 430 // This extension is not checked for correctness during 431 // certificate generation, so now it can throw exception 432 return extensions.valueOfIssuerAlternativeName(); 433 } catch (IOException e) { 434 throw new CertificateParsingException(e); 435 } 436 } 437 438 // 439 // ----- java.security.cert.Certificate methods implementations ------ 440 // 441 442 /** 443 * @see java.security.cert.Certificate#getEncoded() 444 * method documentation for more information. 445 */ 446 public byte[] getEncoded() throws CertificateEncodingException { 447 return getEncodedInternal().clone(); 448 } 449 private byte[] getEncodedInternal() throws CertificateEncodingException { 450 byte[] result = encoding; 451 if (encoding == null) { 452 encoding = result = certificate.getEncoded(); 453 } 454 return result; 455 } 456 457 /** 458 * @see java.security.cert.Certificate#getPublicKey() 459 * method documentation for more information. 460 */ 461 public PublicKey getPublicKey() { 462 PublicKey result = publicKey; 463 if (result == null) { 464 publicKey = result = tbsCert.getSubjectPublicKeyInfo().getPublicKey(); 465 } 466 return result; 467 } 468 469 /** 470 * @see java.security.cert.Certificate#toString() 471 * method documentation for more information. 472 */ 473 public String toString() { 474 return certificate.toString(); 475 } 476 477 /** 478 * Verifies the signature of the certificate. 479 * @see java.security.cert.Certificate#verify(PublicKey) 480 * method documentation for more information. 481 */ 482 public void verify(PublicKey key) 483 throws CertificateException, NoSuchAlgorithmException, 484 InvalidKeyException, NoSuchProviderException, 485 SignatureException { 486 if (getSigAlgName().endsWith("withRSA")) { 487 fastVerify(key); 488 return; 489 } 490 491 Signature signature = Signature.getInstance(getSigAlgName()); 492 signature.initVerify(key); 493 // retrieve the encoding of the TBSCertificate structure 494 byte[] tbsCertificateLocal = getTbsCertificateInternal(); 495 // compute and verify the signature 496 signature.update(tbsCertificateLocal, 0, tbsCertificateLocal.length); 497 if (!signature.verify(certificate.getSignatureValue())) { 498 throw new SignatureException("Signature was not verified"); 499 } 500 } 501 502 /** 503 * Verifies the signature of the certificate. 504 * @see java.security.cert.Certificate#verify(PublicKey,String) 505 * method documentation for more information. 506 */ 507 public void verify(PublicKey key, String sigProvider) 508 throws CertificateException, NoSuchAlgorithmException, 509 InvalidKeyException, NoSuchProviderException, 510 SignatureException { 511 if (getSigAlgName().endsWith("withRSA")) { 512 fastVerify(key); 513 return; 514 } 515 516 Signature signature = 517 Signature.getInstance(getSigAlgName(), sigProvider); 518 signature.initVerify(key); 519 // retrieve the encoding of the TBSCertificate structure 520 byte[] tbsCertificateLocal = getTbsCertificateInternal(); 521 // compute and verify the signature 522 signature.update(tbsCertificateLocal, 0, tbsCertificateLocal.length); 523 if (!signature.verify(certificate.getSignatureValue())) { 524 throw new SignatureException("Signature was not verified"); 525 } 526 } 527 528 /** 529 * Implements a faster RSA verification method that delegates to OpenSSL 530 * native code. In all other aspects it behaves just like the ordinary 531 * {@link verify} method. 532 * 533 * @param key The RSA public key to use 534 * 535 * @throws SignatureException If the verification fails. 536 * @throws InvalidKeyException 537 */ 538 private void fastVerify(PublicKey key) throws SignatureException, 539 InvalidKeyException, NoSuchAlgorithmException { 540 if (!(key instanceof RSAPublicKey)) { 541 throw new InvalidKeyException("key is not an instance of RSAPublicKey"); 542 } 543 RSAPublicKey rsaKey = (RSAPublicKey) key; 544 545 String algorithm = getSigAlgName(); 546 547 // We don't support MD2 anymore. This needs to also check for aliases 548 // and OIDs. 549 if ("MD2withRSA".equalsIgnoreCase(algorithm) || 550 "MD2withRSAEncryption".equalsIgnoreCase(algorithm) || 551 "1.2.840.113549.1.1.2".equalsIgnoreCase(algorithm) || 552 "MD2/RSA".equalsIgnoreCase(algorithm)) { 553 throw new NoSuchAlgorithmException(algorithm); 554 } 555 556 int i = algorithm.indexOf("with"); 557 algorithm = algorithm.substring(i + 4) + "-" + algorithm.substring(0, i); 558 559 byte[] tbsCertificateLocal = getTbsCertificateInternal(); 560 byte[] sig = certificate.getSignatureValue(); 561 if (!NativeCrypto.verifySignature(tbsCertificateLocal, sig, algorithm, rsaKey)) { 562 throw new SignatureException("Signature was not verified"); 563 } 564 } 565 566 // 567 // ----- java.security.cert.X509Extension methods implementations ---- 568 // 569 570 /** 571 * @see java.security.cert.X509Extension#getNonCriticalExtensionOIDs() 572 * method documentation for more information. 573 */ 574 public Set getNonCriticalExtensionOIDs() { 575 if (extensions == null) { 576 return null; 577 } 578 // retrieve the info from the cached extensions object 579 return extensions.getNonCriticalExtensions(); 580 } 581 582 /** 583 * @see java.security.cert.X509Extension#getCriticalExtensionOIDs() 584 * method documentation for more information. 585 */ 586 public Set getCriticalExtensionOIDs() { 587 if (extensions == null) { 588 return null; 589 } 590 // retrieve the info from the cached extensions object 591 return extensions.getCriticalExtensions(); 592 } 593 594 /** 595 * @see java.security.cert.X509Extension#getExtensionValue(String) 596 * method documentation for more information. 597 */ 598 public byte[] getExtensionValue(String oid) { 599 if (extensions == null) { 600 return null; 601 } 602 // retrieve the info from the cached extensions object 603 Extension ext = extensions.getExtensionByOID(oid); 604 return (ext == null) ? null : ext.getRawExtnValue(); 605 } 606 607 /** 608 * @see java.security.cert.X509Extension#hasUnsupportedCriticalExtension() 609 * method documentation for more information. 610 */ 611 public boolean hasUnsupportedCriticalExtension() { 612 if (extensions == null) { 613 return false; 614 } 615 // retrieve the info from the cached extensions object 616 return extensions.hasUnsupportedCritical(); 617 } 618 619 } 620