1package org.bouncycastle.jce.provider; 2 3import java.io.ByteArrayOutputStream; 4import java.io.IOException; 5import java.math.BigInteger; 6import java.security.InvalidKeyException; 7import java.security.NoSuchAlgorithmException; 8import java.security.NoSuchProviderException; 9import java.security.Principal; 10import java.security.Provider; 11import java.security.PublicKey; 12import java.security.Security; 13import java.security.Signature; 14import java.security.SignatureException; 15import java.security.cert.Certificate; 16import java.security.cert.CertificateEncodingException; 17import java.security.cert.CertificateException; 18import java.security.cert.CertificateExpiredException; 19import java.security.cert.CertificateNotYetValidException; 20import java.security.cert.CertificateParsingException; 21import java.security.cert.X509Certificate; 22import java.util.ArrayList; 23import java.util.Collections; 24import java.util.Date; 25import java.util.Enumeration; 26import java.util.HashSet; 27import java.util.List; 28import java.util.Set; 29 30import javax.security.auth.x500.X500Principal; 31 32import org.bouncycastle.asn1.ASN1Encodable; 33import org.bouncycastle.asn1.ASN1InputStream; 34import org.bouncycastle.asn1.ASN1Object; 35import org.bouncycastle.asn1.ASN1OutputStream; 36import org.bouncycastle.asn1.ASN1Sequence; 37import org.bouncycastle.asn1.DERBitString; 38import org.bouncycastle.asn1.DEREncodable; 39import org.bouncycastle.asn1.DERIA5String; 40import org.bouncycastle.asn1.DERNull; 41import org.bouncycastle.asn1.DERObjectIdentifier; 42import org.bouncycastle.asn1.misc.MiscObjectIdentifiers; 43import org.bouncycastle.asn1.misc.NetscapeCertType; 44import org.bouncycastle.asn1.misc.NetscapeRevocationURL; 45import org.bouncycastle.asn1.misc.VerisignCzagExtension; 46import org.bouncycastle.asn1.util.ASN1Dump; 47import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 48import org.bouncycastle.asn1.x509.BasicConstraints; 49import org.bouncycastle.asn1.x509.KeyUsage; 50import org.bouncycastle.asn1.x509.X509CertificateStructure; 51import org.bouncycastle.asn1.x509.X509Extension; 52import org.bouncycastle.asn1.x509.X509Extensions; 53import org.bouncycastle.jce.X509Principal; 54import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier; 55import org.bouncycastle.util.Arrays; 56import org.bouncycastle.util.encoders.Hex; 57 58public class X509CertificateObject 59 extends X509Certificate 60 implements PKCS12BagAttributeCarrier 61{ 62 private X509CertificateStructure c; 63 private BasicConstraints basicConstraints; 64 private boolean[] keyUsage; 65 private boolean hashValueSet; 66 private int hashValue; 67 68 private PKCS12BagAttributeCarrier attrCarrier = new PKCS12BagAttributeCarrierImpl(); 69 70 public X509CertificateObject( 71 X509CertificateStructure c) 72 throws CertificateParsingException 73 { 74 this.c = c; 75 76 try 77 { 78 byte[] bytes = this.getExtensionBytes("2.5.29.19"); 79 80 if (bytes != null) 81 { 82 basicConstraints = BasicConstraints.getInstance(ASN1Object.fromByteArray(bytes)); 83 } 84 } 85 catch (Exception e) 86 { 87 throw new CertificateParsingException("cannot construct BasicConstraints: " + e); 88 } 89 90 try 91 { 92 byte[] bytes = this.getExtensionBytes("2.5.29.15"); 93 if (bytes != null) 94 { 95 DERBitString bits = DERBitString.getInstance(ASN1Object.fromByteArray(bytes)); 96 97 bytes = bits.getBytes(); 98 int length = (bytes.length * 8) - bits.getPadBits(); 99 100 keyUsage = new boolean[(length < 9) ? 9 : length]; 101 102 for (int i = 0; i != length; i++) 103 { 104 keyUsage[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0; 105 } 106 } 107 else 108 { 109 keyUsage = null; 110 } 111 } 112 catch (Exception e) 113 { 114 throw new CertificateParsingException("cannot construct KeyUsage: " + e); 115 } 116 } 117 118 public void checkValidity() 119 throws CertificateExpiredException, CertificateNotYetValidException 120 { 121 this.checkValidity(new Date()); 122 } 123 124 public void checkValidity( 125 Date date) 126 throws CertificateExpiredException, CertificateNotYetValidException 127 { 128 if (date.getTime() > this.getNotAfter().getTime()) // for other VM compatibility 129 { 130 throw new CertificateExpiredException("certificate expired on " + c.getEndDate().getTime()); 131 } 132 133 if (date.getTime() < this.getNotBefore().getTime()) 134 { 135 throw new CertificateNotYetValidException("certificate not valid till " + c.getStartDate().getTime()); 136 } 137 } 138 139 public int getVersion() 140 { 141 return c.getVersion(); 142 } 143 144 public BigInteger getSerialNumber() 145 { 146 return c.getSerialNumber().getValue(); 147 } 148 149 public Principal getIssuerDN() 150 { 151 return new X509Principal(c.getIssuer()); 152 } 153 154 public X500Principal getIssuerX500Principal() 155 { 156 try 157 { 158 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 159 ASN1OutputStream aOut = new ASN1OutputStream(bOut); 160 161 aOut.writeObject(c.getIssuer()); 162 163 return new X500Principal(bOut.toByteArray()); 164 } 165 catch (IOException e) 166 { 167 throw new IllegalStateException("can't encode issuer DN"); 168 } 169 } 170 171 public Principal getSubjectDN() 172 { 173 return new X509Principal(c.getSubject()); 174 } 175 176 public X500Principal getSubjectX500Principal() 177 { 178 try 179 { 180 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 181 ASN1OutputStream aOut = new ASN1OutputStream(bOut); 182 183 aOut.writeObject(c.getSubject()); 184 185 return new X500Principal(bOut.toByteArray()); 186 } 187 catch (IOException e) 188 { 189 throw new IllegalStateException("can't encode issuer DN"); 190 } 191 } 192 193 public Date getNotBefore() 194 { 195 return c.getStartDate().getDate(); 196 } 197 198 public Date getNotAfter() 199 { 200 return c.getEndDate().getDate(); 201 } 202 203 public byte[] getTBSCertificate() 204 throws CertificateEncodingException 205 { 206 try 207 { 208 return c.getTBSCertificate().getEncoded(ASN1Encodable.DER); 209 } 210 catch (IOException e) 211 { 212 throw new CertificateEncodingException(e.toString()); 213 } 214 } 215 216 public byte[] getSignature() 217 { 218 return c.getSignature().getBytes(); 219 } 220 221 /** 222 * return a more "meaningful" representation for the signature algorithm used in 223 * the certficate. 224 */ 225 public String getSigAlgName() 226 { 227 Provider prov = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME); 228 229 if (prov != null) 230 { 231 String algName = prov.getProperty("Alg.Alias.Signature." + this.getSigAlgOID()); 232 233 if (algName != null) 234 { 235 return algName; 236 } 237 } 238 239 Provider[] provs = Security.getProviders(); 240 241 // 242 // search every provider looking for a real algorithm 243 // 244 for (int i = 0; i != provs.length; i++) 245 { 246 String algName = provs[i].getProperty("Alg.Alias.Signature." + this.getSigAlgOID()); 247 if (algName != null) 248 { 249 return algName; 250 } 251 } 252 253 return this.getSigAlgOID(); 254 } 255 256 /** 257 * return the object identifier for the signature. 258 */ 259 public String getSigAlgOID() 260 { 261 return c.getSignatureAlgorithm().getObjectId().getId(); 262 } 263 264 /** 265 * return the signature parameters, or null if there aren't any. 266 */ 267 public byte[] getSigAlgParams() 268 { 269 if (c.getSignatureAlgorithm().getParameters() != null) 270 { 271 return c.getSignatureAlgorithm().getParameters().getDERObject().getDEREncoded(); 272 } 273 else 274 { 275 return null; 276 } 277 } 278 279 public boolean[] getIssuerUniqueID() 280 { 281 DERBitString id = c.getTBSCertificate().getIssuerUniqueId(); 282 283 if (id != null) 284 { 285 byte[] bytes = id.getBytes(); 286 boolean[] boolId = new boolean[bytes.length * 8 - id.getPadBits()]; 287 288 for (int i = 0; i != boolId.length; i++) 289 { 290 boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0; 291 } 292 293 return boolId; 294 } 295 296 return null; 297 } 298 299 public boolean[] getSubjectUniqueID() 300 { 301 DERBitString id = c.getTBSCertificate().getSubjectUniqueId(); 302 303 if (id != null) 304 { 305 byte[] bytes = id.getBytes(); 306 boolean[] boolId = new boolean[bytes.length * 8 - id.getPadBits()]; 307 308 for (int i = 0; i != boolId.length; i++) 309 { 310 boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0; 311 } 312 313 return boolId; 314 } 315 316 return null; 317 } 318 319 public boolean[] getKeyUsage() 320 { 321 return keyUsage; 322 } 323 324 public List getExtendedKeyUsage() 325 throws CertificateParsingException 326 { 327 byte[] bytes = this.getExtensionBytes("2.5.29.37"); 328 329 if (bytes != null) 330 { 331 try 332 { 333 ASN1InputStream dIn = new ASN1InputStream(bytes); 334 ASN1Sequence seq = (ASN1Sequence)dIn.readObject(); 335 List list = new ArrayList(); 336 337 for (int i = 0; i != seq.size(); i++) 338 { 339 list.add(((DERObjectIdentifier)seq.getObjectAt(i)).getId()); 340 } 341 342 return Collections.unmodifiableList(list); 343 } 344 catch (Exception e) 345 { 346 throw new CertificateParsingException("error processing extended key usage extension"); 347 } 348 } 349 350 return null; 351 } 352 353 public int getBasicConstraints() 354 { 355 if (basicConstraints != null) 356 { 357 if (basicConstraints.isCA()) 358 { 359 if (basicConstraints.getPathLenConstraint() == null) 360 { 361 return Integer.MAX_VALUE; 362 } 363 else 364 { 365 return basicConstraints.getPathLenConstraint().intValue(); 366 } 367 } 368 else 369 { 370 return -1; 371 } 372 } 373 374 return -1; 375 } 376 377 public Set getCriticalExtensionOIDs() 378 { 379 if (this.getVersion() == 3) 380 { 381 Set set = new HashSet(); 382 X509Extensions extensions = c.getTBSCertificate().getExtensions(); 383 384 if (extensions != null) 385 { 386 Enumeration e = extensions.oids(); 387 388 while (e.hasMoreElements()) 389 { 390 DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement(); 391 X509Extension ext = extensions.getExtension(oid); 392 393 if (ext.isCritical()) 394 { 395 set.add(oid.getId()); 396 } 397 } 398 399 return set; 400 } 401 } 402 403 return null; 404 } 405 406 private byte[] getExtensionBytes(String oid) 407 { 408 X509Extensions exts = c.getTBSCertificate().getExtensions(); 409 410 if (exts != null) 411 { 412 X509Extension ext = exts.getExtension(new DERObjectIdentifier(oid)); 413 if (ext != null) 414 { 415 return ext.getValue().getOctets(); 416 } 417 } 418 419 return null; 420 } 421 422 public byte[] getExtensionValue(String oid) 423 { 424 X509Extensions exts = c.getTBSCertificate().getExtensions(); 425 426 if (exts != null) 427 { 428 X509Extension ext = exts.getExtension(new DERObjectIdentifier(oid)); 429 430 if (ext != null) 431 { 432 try 433 { 434 return ext.getValue().getEncoded(); 435 } 436 catch (Exception e) 437 { 438 throw new IllegalStateException("error parsing " + e.toString()); 439 } 440 } 441 } 442 443 return null; 444 } 445 446 public Set getNonCriticalExtensionOIDs() 447 { 448 if (this.getVersion() == 3) 449 { 450 Set set = new HashSet(); 451 X509Extensions extensions = c.getTBSCertificate().getExtensions(); 452 453 if (extensions != null) 454 { 455 Enumeration e = extensions.oids(); 456 457 while (e.hasMoreElements()) 458 { 459 DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement(); 460 X509Extension ext = extensions.getExtension(oid); 461 462 if (!ext.isCritical()) 463 { 464 set.add(oid.getId()); 465 } 466 } 467 468 return set; 469 } 470 } 471 472 return null; 473 } 474 475 public boolean hasUnsupportedCriticalExtension() 476 { 477 if (this.getVersion() == 3) 478 { 479 X509Extensions extensions = c.getTBSCertificate().getExtensions(); 480 481 if (extensions != null) 482 { 483 Enumeration e = extensions.oids(); 484 485 while (e.hasMoreElements()) 486 { 487 DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement(); 488 String oidId = oid.getId(); 489 490 if (oidId.equals(RFC3280CertPathUtilities.KEY_USAGE) 491 || oidId.equals(RFC3280CertPathUtilities.CERTIFICATE_POLICIES) 492 || oidId.equals(RFC3280CertPathUtilities.POLICY_MAPPINGS) 493 || oidId.equals(RFC3280CertPathUtilities.INHIBIT_ANY_POLICY) 494 || oidId.equals(RFC3280CertPathUtilities.CRL_DISTRIBUTION_POINTS) 495 || oidId.equals(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT) 496 || oidId.equals(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR) 497 || oidId.equals(RFC3280CertPathUtilities.POLICY_CONSTRAINTS) 498 || oidId.equals(RFC3280CertPathUtilities.BASIC_CONSTRAINTS) 499 || oidId.equals(RFC3280CertPathUtilities.SUBJECT_ALTERNATIVE_NAME) 500 || oidId.equals(RFC3280CertPathUtilities.NAME_CONSTRAINTS)) 501 { 502 continue; 503 } 504 505 X509Extension ext = extensions.getExtension(oid); 506 507 if (ext.isCritical()) 508 { 509 return true; 510 } 511 } 512 } 513 } 514 515 return false; 516 } 517 518 public PublicKey getPublicKey() 519 { 520 return JDKKeyFactory.createPublicKeyFromPublicKeyInfo(c.getSubjectPublicKeyInfo()); 521 } 522 523 // BEGIN android-changed 524 private byte[] encoded; 525 // END android-changed 526 public byte[] getEncoded() 527 throws CertificateEncodingException 528 { 529 try 530 { 531 // BEGIN android-changed 532 if (encoded == null) { 533 encoded = c.getEncoded(ASN1Encodable.DER); 534 } 535 return encoded; 536 // END android-changed 537 } 538 catch (IOException e) 539 { 540 throw new CertificateEncodingException(e.toString()); 541 } 542 } 543 544 public boolean equals( 545 Object o) 546 { 547 if (o == this) 548 { 549 return true; 550 } 551 552 if (!(o instanceof Certificate)) 553 { 554 return false; 555 } 556 557 Certificate other = (Certificate)o; 558 559 try 560 { 561 byte[] b1 = this.getEncoded(); 562 byte[] b2 = other.getEncoded(); 563 564 return Arrays.areEqual(b1, b2); 565 } 566 catch (CertificateEncodingException e) 567 { 568 return false; 569 } 570 } 571 572 public synchronized int hashCode() 573 { 574 if (!hashValueSet) 575 { 576 hashValue = calculateHashCode(); 577 hashValueSet = true; 578 } 579 580 return hashValue; 581 } 582 583 private int calculateHashCode() 584 { 585 try 586 { 587 int hashCode = 0; 588 byte[] certData = this.getEncoded(); 589 for (int i = 1; i < certData.length; i++) 590 { 591 hashCode += certData[i] * i; 592 } 593 return hashCode; 594 } 595 catch (CertificateEncodingException e) 596 { 597 return 0; 598 } 599 } 600 601 public void setBagAttribute( 602 DERObjectIdentifier oid, 603 DEREncodable attribute) 604 { 605 attrCarrier.setBagAttribute(oid, attribute); 606 } 607 608 public DEREncodable getBagAttribute( 609 DERObjectIdentifier oid) 610 { 611 return attrCarrier.getBagAttribute(oid); 612 } 613 614 public Enumeration getBagAttributeKeys() 615 { 616 return attrCarrier.getBagAttributeKeys(); 617 } 618 619 public String toString() 620 { 621 StringBuffer buf = new StringBuffer(); 622 String nl = System.getProperty("line.separator"); 623 624 buf.append(" [0] Version: ").append(this.getVersion()).append(nl); 625 buf.append(" SerialNumber: ").append(this.getSerialNumber()).append(nl); 626 buf.append(" IssuerDN: ").append(this.getIssuerDN()).append(nl); 627 buf.append(" Start Date: ").append(this.getNotBefore()).append(nl); 628 buf.append(" Final Date: ").append(this.getNotAfter()).append(nl); 629 buf.append(" SubjectDN: ").append(this.getSubjectDN()).append(nl); 630 buf.append(" Public Key: ").append(this.getPublicKey()).append(nl); 631 buf.append(" Signature Algorithm: ").append(this.getSigAlgName()).append(nl); 632 633 byte[] sig = this.getSignature(); 634 635 buf.append(" Signature: ").append(new String(Hex.encode(sig, 0, 20))).append(nl); 636 for (int i = 20; i < sig.length; i += 20) 637 { 638 if (i < sig.length - 20) 639 { 640 buf.append(" ").append(new String(Hex.encode(sig, i, 20))).append(nl); 641 } 642 else 643 { 644 buf.append(" ").append(new String(Hex.encode(sig, i, sig.length - i))).append(nl); 645 } 646 } 647 648 X509Extensions extensions = c.getTBSCertificate().getExtensions(); 649 650 if (extensions != null) 651 { 652 Enumeration e = extensions.oids(); 653 654 if (e.hasMoreElements()) 655 { 656 buf.append(" Extensions: \n"); 657 } 658 659 while (e.hasMoreElements()) 660 { 661 DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement(); 662 X509Extension ext = extensions.getExtension(oid); 663 664 if (ext.getValue() != null) 665 { 666 byte[] octs = ext.getValue().getOctets(); 667 ASN1InputStream dIn = new ASN1InputStream(octs); 668 buf.append(" critical(").append(ext.isCritical()).append(") "); 669 try 670 { 671 if (oid.equals(X509Extensions.BasicConstraints)) 672 { 673 buf.append(new BasicConstraints((ASN1Sequence)dIn.readObject())).append(nl); 674 } 675 else if (oid.equals(X509Extensions.KeyUsage)) 676 { 677 buf.append(new KeyUsage((DERBitString)dIn.readObject())).append(nl); 678 } 679 else if (oid.equals(MiscObjectIdentifiers.netscapeCertType)) 680 { 681 buf.append(new NetscapeCertType((DERBitString)dIn.readObject())).append(nl); 682 } 683 else if (oid.equals(MiscObjectIdentifiers.netscapeRevocationURL)) 684 { 685 buf.append(new NetscapeRevocationURL((DERIA5String)dIn.readObject())).append(nl); 686 } 687 else if (oid.equals(MiscObjectIdentifiers.verisignCzagExtension)) 688 { 689 buf.append(new VerisignCzagExtension((DERIA5String)dIn.readObject())).append(nl); 690 } 691 else 692 { 693 buf.append(oid.getId()); 694 buf.append(" value = ").append(ASN1Dump.dumpAsString(dIn.readObject())).append(nl); 695 //buf.append(" value = ").append("*****").append(nl); 696 } 697 } 698 catch (Exception ex) 699 { 700 buf.append(oid.getId()); 701 // buf.append(" value = ").append(new String(Hex.encode(ext.getValue().getOctets()))).append(nl); 702 buf.append(" value = ").append("*****").append(nl); 703 } 704 } 705 else 706 { 707 buf.append(nl); 708 } 709 } 710 } 711 712 return buf.toString(); 713 } 714 715 public final void verify( 716 PublicKey key) 717 throws CertificateException, NoSuchAlgorithmException, 718 InvalidKeyException, NoSuchProviderException, SignatureException 719 { 720 Signature signature; 721 String sigName = X509SignatureUtil.getSignatureName(c.getSignatureAlgorithm()); 722 723 try 724 { 725 signature = Signature.getInstance(sigName, BouncyCastleProvider.PROVIDER_NAME); 726 } 727 catch (Exception e) 728 { 729 signature = Signature.getInstance(sigName); 730 } 731 732 checkSignature(key, signature); 733 } 734 735 public final void verify( 736 PublicKey key, 737 String sigProvider) 738 throws CertificateException, NoSuchAlgorithmException, 739 InvalidKeyException, NoSuchProviderException, SignatureException 740 { 741 String sigName = X509SignatureUtil.getSignatureName(c.getSignatureAlgorithm()); 742 Signature signature = Signature.getInstance(sigName, sigProvider); 743 744 checkSignature(key, signature); 745 } 746 747 private void checkSignature( 748 PublicKey key, 749 Signature signature) 750 throws CertificateException, NoSuchAlgorithmException, 751 SignatureException, InvalidKeyException 752 { 753 if (!isAlgIdEqual(c.getSignatureAlgorithm(), c.getTBSCertificate().getSignature())) 754 { 755 throw new CertificateException("signature algorithm in TBS cert not same as outer cert"); 756 } 757 758 DEREncodable params = c.getSignatureAlgorithm().getParameters(); 759 760 // TODO This should go after the initVerify? 761 X509SignatureUtil.setSignatureParameters(signature, params); 762 763 signature.initVerify(key); 764 765 signature.update(this.getTBSCertificate()); 766 767 if (!signature.verify(this.getSignature())) 768 { 769 throw new InvalidKeyException("Public key presented not for certificate signature"); 770 } 771 } 772 773 private boolean isAlgIdEqual(AlgorithmIdentifier id1, AlgorithmIdentifier id2) 774 { 775 if (!id1.getObjectId().equals(id2.getObjectId())) 776 { 777 return false; 778 } 779 780 if (id1.getParameters() == null) 781 { 782 if (id2.getParameters() != null && !id2.getParameters().equals(DERNull.INSTANCE)) 783 { 784 return false; 785 } 786 787 return true; 788 } 789 790 if (id2.getParameters() == null) 791 { 792 if (id1.getParameters() != null && !id1.getParameters().equals(DERNull.INSTANCE)) 793 { 794 return false; 795 } 796 797 return true; 798 } 799 800 return id1.getParameters().equals(id2.getParameters()); 801 } 802} 803