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