001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.geronimo.util.jce.provider; 019 020 import java.io.ByteArrayInputStream; 021 import java.io.ByteArrayOutputStream; 022 import java.io.IOException; 023 import java.math.BigInteger; 024 import java.security.InvalidKeyException; 025 import java.security.NoSuchAlgorithmException; 026 import java.security.NoSuchProviderException; 027 import java.security.Principal; 028 import java.security.Provider; 029 import java.security.PublicKey; 030 import java.security.Security; 031 import java.security.Signature; 032 import java.security.SignatureException; 033 import java.security.cert.CertificateEncodingException; 034 import java.security.cert.CertificateException; 035 import java.security.cert.CertificateExpiredException; 036 import java.security.cert.CertificateNotYetValidException; 037 import java.security.cert.CertificateParsingException; 038 import java.security.cert.X509Certificate; 039 import java.util.ArrayList; 040 import java.util.Collections; 041 import java.util.Date; 042 import java.util.Enumeration; 043 import java.util.HashSet; 044 import java.util.Hashtable; 045 import java.util.List; 046 import java.util.Set; 047 import java.util.Vector; 048 049 import javax.security.auth.x500.X500Principal; 050 051 import org.apache.geronimo.util.asn1.*; 052 import org.apache.geronimo.util.asn1.misc.MiscObjectIdentifiers; 053 import org.apache.geronimo.util.asn1.misc.NetscapeCertType; 054 import org.apache.geronimo.util.asn1.misc.NetscapeRevocationURL; 055 import org.apache.geronimo.util.asn1.misc.VerisignCzagExtension; 056 import org.apache.geronimo.util.asn1.util.ASN1Dump; 057 import org.apache.geronimo.util.asn1.x509.BasicConstraints; 058 import org.apache.geronimo.util.asn1.x509.KeyUsage; 059 import org.apache.geronimo.util.asn1.x509.X509CertificateStructure; 060 import org.apache.geronimo.util.asn1.x509.X509Extension; 061 import org.apache.geronimo.util.asn1.x509.X509Extensions; 062 import org.apache.geronimo.util.jce.X509Principal; 063 import org.apache.geronimo.util.jce.interfaces.PKCS12BagAttributeCarrier; 064 import org.apache.geronimo.util.encoders.Hex; 065 066 public class X509CertificateObject 067 extends X509Certificate 068 implements PKCS12BagAttributeCarrier 069 { 070 private X509CertificateStructure c; 071 private Hashtable pkcs12Attributes = new Hashtable(); 072 private Vector pkcs12Ordering = new Vector(); 073 074 public X509CertificateObject( 075 X509CertificateStructure c) 076 { 077 this.c = c; 078 } 079 080 public void checkValidity() 081 throws CertificateExpiredException, CertificateNotYetValidException 082 { 083 this.checkValidity(new Date()); 084 } 085 086 public void checkValidity( 087 Date date) 088 throws CertificateExpiredException, CertificateNotYetValidException 089 { 090 if (date.after(this.getNotAfter())) 091 { 092 throw new CertificateExpiredException("certificate expired on " + c.getEndDate().getTime()); 093 } 094 095 if (date.before(this.getNotBefore())) 096 { 097 throw new CertificateNotYetValidException("certificate not valid till " + c.getStartDate().getTime()); 098 } 099 } 100 101 public int getVersion() 102 { 103 return c.getVersion(); 104 } 105 106 public BigInteger getSerialNumber() 107 { 108 return c.getSerialNumber().getValue(); 109 } 110 111 public Principal getIssuerDN() 112 { 113 return new X509Principal(c.getIssuer()); 114 } 115 116 public X500Principal getIssuerX500Principal() 117 { 118 try 119 { 120 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 121 ASN1OutputStream aOut = new ASN1OutputStream(bOut); 122 123 aOut.writeObject(c.getIssuer()); 124 125 return new X500Principal(bOut.toByteArray()); 126 } 127 catch (IOException e) 128 { 129 throw new IllegalStateException("can't encode issuer DN", e); 130 } 131 } 132 133 public Principal getSubjectDN() 134 { 135 return new X509Principal(c.getSubject()); 136 } 137 138 public X500Principal getSubjectX500Principal() 139 { 140 try 141 { 142 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 143 ASN1OutputStream aOut = new ASN1OutputStream(bOut); 144 145 aOut.writeObject(c.getSubject()); 146 147 return new X500Principal(bOut.toByteArray()); 148 } 149 catch (IOException e) 150 { 151 throw new IllegalStateException("can't encode issuer DN", e); 152 } 153 } 154 155 public Date getNotBefore() 156 { 157 return c.getStartDate().getDate(); 158 } 159 160 public Date getNotAfter() 161 { 162 return c.getEndDate().getDate(); 163 } 164 165 public byte[] getTBSCertificate() 166 throws CertificateEncodingException 167 { 168 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 169 DEROutputStream dOut = new DEROutputStream(bOut); 170 171 try 172 { 173 dOut.writeObject(c.getTBSCertificate()); 174 175 return bOut.toByteArray(); 176 } 177 catch (IOException e) 178 { 179 throw (CertificateEncodingException)new CertificateEncodingException(e.getMessage()).initCause(e); 180 } 181 } 182 183 public byte[] getSignature() 184 { 185 return c.getSignature().getBytes(); 186 } 187 188 /** 189 * return a more "meaningful" representation for the signature algorithm used in 190 * the certficate. 191 */ 192 public String getSigAlgName() 193 { 194 Provider[] provs = Security.getProviders(); 195 196 // 197 // search every provider looking for a real algorithm 198 // 199 for (int i = 0; i != provs.length; i++) 200 { 201 String algName = provs[i].getProperty("Alg.Alias.Signature." + this.getSigAlgOID()); 202 if (algName != null) 203 { 204 return algName; 205 } 206 } 207 208 return this.getSigAlgOID(); 209 } 210 211 /** 212 * return the object identifier for the signature. 213 */ 214 public String getSigAlgOID() 215 { 216 return c.getSignatureAlgorithm().getObjectId().getId(); 217 } 218 219 /** 220 * return the signature parameters, or null if there aren't any. 221 */ 222 public byte[] getSigAlgParams() 223 { 224 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 225 226 if (c.getSignatureAlgorithm().getParameters() != null) 227 { 228 try 229 { 230 DEROutputStream dOut = new DEROutputStream(bOut); 231 232 dOut.writeObject(c.getSignatureAlgorithm().getParameters()); 233 } 234 catch (Exception e) 235 { 236 throw new RuntimeException("exception getting sig parameters " + e.getMessage(), e); 237 } 238 239 return bOut.toByteArray(); 240 } 241 else 242 { 243 return null; 244 } 245 } 246 247 public boolean[] getIssuerUniqueID() 248 { 249 DERBitString id = c.getTBSCertificate().getIssuerUniqueId(); 250 251 if (id != null) 252 { 253 byte[] bytes = id.getBytes(); 254 boolean[] boolId = new boolean[bytes.length * 8 - id.getPadBits()]; 255 256 for (int i = 0; i != boolId.length; i++) 257 { 258 boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0; 259 } 260 261 return boolId; 262 } 263 264 return null; 265 } 266 267 public boolean[] getSubjectUniqueID() 268 { 269 DERBitString id = c.getTBSCertificate().getSubjectUniqueId(); 270 271 if (id != null) 272 { 273 byte[] bytes = id.getBytes(); 274 boolean[] boolId = new boolean[bytes.length * 8 - id.getPadBits()]; 275 276 for (int i = 0; i != boolId.length; i++) 277 { 278 boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0; 279 } 280 281 return boolId; 282 } 283 284 return null; 285 } 286 287 public boolean[] getKeyUsage() 288 { 289 byte[] bytes = this.getExtensionBytes("2.5.29.15"); 290 int length = 0; 291 292 if (bytes != null) 293 { 294 try 295 { 296 ASN1InputStream dIn = new ASN1InputStream(new ByteArrayInputStream(bytes)); 297 DERBitString bits = (DERBitString)dIn.readObject(); 298 299 bytes = bits.getBytes(); 300 length = (bytes.length * 8) - bits.getPadBits(); 301 } 302 catch (Exception e) 303 { 304 throw new RuntimeException("error processing key usage extension", e); 305 } 306 307 boolean[] keyUsage = new boolean[(length < 9) ? 9 : length]; 308 309 for (int i = 0; i != length; i++) 310 { 311 keyUsage[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0; 312 } 313 314 return keyUsage; 315 } 316 317 return null; 318 } 319 320 public List getExtendedKeyUsage() 321 throws CertificateParsingException 322 { 323 byte[] bytes = this.getExtensionBytes("2.5.29.37"); 324 int length = 0; 325 326 if (bytes != null) 327 { 328 try 329 { 330 ASN1InputStream dIn = new ASN1InputStream(new ByteArrayInputStream(bytes)); 331 ASN1Sequence seq = (ASN1Sequence)dIn.readObject(); 332 ArrayList list = new ArrayList(); 333 334 for (int i = 0; i != seq.size(); i++) 335 { 336 list.add(((DERObjectIdentifier)seq.getObjectAt(i)).getId()); 337 } 338 339 return Collections.unmodifiableList(list); 340 } 341 catch (Exception e) 342 { 343 throw (CertificateParsingException)new CertificateParsingException("error processing extended key usage extension").initCause(e); 344 } 345 } 346 347 return null; 348 } 349 350 public int getBasicConstraints() 351 { 352 byte[] bytes = this.getExtensionBytes("2.5.29.19"); 353 354 if (bytes != null) 355 { 356 try 357 { 358 ASN1InputStream dIn = new ASN1InputStream(new ByteArrayInputStream(bytes)); 359 ASN1Sequence seq = (ASN1Sequence)dIn.readObject(); 360 361 if (seq.size() == 2) 362 { 363 if (((DERBoolean)seq.getObjectAt(0)).isTrue()) 364 { 365 return ((DERInteger)seq.getObjectAt(1)).getValue().intValue(); 366 } 367 else 368 { 369 return -1; 370 } 371 } 372 else if (seq.size() == 1) 373 { 374 if (seq.getObjectAt(0) instanceof DERBoolean) 375 { 376 if (((DERBoolean)seq.getObjectAt(0)).isTrue()) 377 { 378 return Integer.MAX_VALUE; 379 } 380 else 381 { 382 return -1; 383 } 384 } 385 else 386 { 387 return -1; 388 } 389 } 390 } 391 catch (Exception e) 392 { 393 throw new RuntimeException("error processing key usage extension", e); 394 } 395 } 396 397 return -1; 398 } 399 400 public Set getCriticalExtensionOIDs() 401 { 402 if (this.getVersion() == 3) 403 { 404 HashSet set = new HashSet(); 405 X509Extensions extensions = c.getTBSCertificate().getExtensions(); 406 407 if (extensions != null) 408 { 409 Enumeration e = extensions.oids(); 410 411 while (e.hasMoreElements()) 412 { 413 DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement(); 414 X509Extension ext = extensions.getExtension(oid); 415 416 if (ext.isCritical()) 417 { 418 set.add(oid.getId()); 419 } 420 } 421 422 return set; 423 } 424 } 425 426 return null; 427 } 428 429 private byte[] getExtensionBytes(String oid) 430 { 431 X509Extensions exts = c.getTBSCertificate().getExtensions(); 432 433 if (exts != null) 434 { 435 X509Extension ext = exts.getExtension(new DERObjectIdentifier(oid)); 436 if (ext != null) 437 { 438 return ext.getValue().getOctets(); 439 } 440 } 441 442 return null; 443 } 444 445 public byte[] getExtensionValue(String oid) 446 { 447 X509Extensions exts = c.getTBSCertificate().getExtensions(); 448 449 if (exts != null) 450 { 451 X509Extension ext = exts.getExtension(new DERObjectIdentifier(oid)); 452 453 if (ext != null) 454 { 455 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 456 DEROutputStream dOut = new DEROutputStream(bOut); 457 458 try 459 { 460 dOut.writeObject(ext.getValue()); 461 462 return bOut.toByteArray(); 463 } 464 catch (Exception e) 465 { 466 throw new RuntimeException("error encoding " + e.getMessage(), e); 467 } 468 } 469 } 470 471 return null; 472 } 473 474 public Set getNonCriticalExtensionOIDs() 475 { 476 if (this.getVersion() == 3) 477 { 478 HashSet set = new HashSet(); 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 X509Extension ext = extensions.getExtension(oid); 489 490 if (!ext.isCritical()) 491 { 492 set.add(oid.getId()); 493 } 494 } 495 496 return set; 497 } 498 } 499 500 return null; 501 } 502 503 public boolean hasUnsupportedCriticalExtension() 504 { 505 if (this.getVersion() == 3) 506 { 507 X509Extensions extensions = c.getTBSCertificate().getExtensions(); 508 509 if (extensions != null) 510 { 511 Enumeration e = extensions.oids(); 512 513 while (e.hasMoreElements()) 514 { 515 DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement(); 516 if (oid.getId().equals("2.5.29.15") 517 || oid.getId().equals("2.5.29.19")) 518 { 519 continue; 520 } 521 522 X509Extension ext = extensions.getExtension(oid); 523 524 if (ext.isCritical()) 525 { 526 return true; 527 } 528 } 529 } 530 } 531 532 return false; 533 } 534 535 public PublicKey getPublicKey() 536 { 537 return JDKKeyFactory.createPublicKeyFromPublicKeyInfo(c.getSubjectPublicKeyInfo()); 538 } 539 540 public byte[] getEncoded() 541 throws CertificateEncodingException 542 { 543 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 544 DEROutputStream dOut = new DEROutputStream(bOut); 545 546 try 547 { 548 dOut.writeObject(c); 549 550 return bOut.toByteArray(); 551 } 552 catch (IOException e) 553 { 554 throw (CertificateEncodingException)new CertificateEncodingException(e.getMessage()).initCause(e); 555 } 556 } 557 558 public void setBagAttribute( 559 DERObjectIdentifier oid, 560 DEREncodable attribute) 561 { 562 pkcs12Attributes.put(oid, attribute); 563 pkcs12Ordering.addElement(oid); 564 } 565 566 public DEREncodable getBagAttribute( 567 DERObjectIdentifier oid) 568 { 569 return (DEREncodable)pkcs12Attributes.get(oid); 570 } 571 572 public Enumeration getBagAttributeKeys() 573 { 574 return pkcs12Ordering.elements(); 575 } 576 577 public String toString() 578 { 579 StringBuffer buf = new StringBuffer(); 580 String nl = System.getProperty("line.separator"); 581 582 buf.append(" [0] Version: " + this.getVersion() + nl); 583 buf.append(" SerialNumber: " + this.getSerialNumber() + nl); 584 buf.append(" IssuerDN: " + this.getIssuerDN() + nl); 585 buf.append(" Start Date: " + this.getNotBefore() + nl); 586 buf.append(" Final Date: " + this.getNotAfter() + nl); 587 buf.append(" SubjectDN: " + this.getSubjectDN() + nl); 588 buf.append(" Public Key: " + this.getPublicKey() + nl); 589 buf.append(" Signature Algorithm: " + this.getSigAlgName() + nl); 590 591 byte[] sig = this.getSignature(); 592 593 buf.append(" Signature: " + new String(Hex.encode(sig, 0, 20)) + nl); 594 for (int i = 20; i < sig.length; i += 20) 595 { 596 if (i < sig.length - 20) 597 { 598 buf.append(" " + new String(Hex.encode(sig, i, 20)) + nl); 599 } 600 else 601 { 602 buf.append(" " + new String(Hex.encode(sig, i, sig.length - i)) + nl); 603 } 604 } 605 606 X509Extensions extensions = c.getTBSCertificate().getExtensions(); 607 608 if (extensions != null) 609 { 610 Enumeration e = extensions.oids(); 611 612 if (e.hasMoreElements()) 613 { 614 buf.append(" Extensions: \n"); 615 } 616 617 while (e.hasMoreElements()) 618 { 619 DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement(); 620 X509Extension ext = extensions.getExtension(oid); 621 622 if (ext.getValue() != null) 623 { 624 byte[] octs = ext.getValue().getOctets(); 625 ByteArrayInputStream bIn = new ByteArrayInputStream(octs); 626 ASN1InputStream dIn = new ASN1InputStream(bIn); 627 buf.append(" critical(" + ext.isCritical() + ") "); 628 try 629 { 630 if (oid.equals(X509Extensions.BasicConstraints)) 631 { 632 buf.append(new BasicConstraints((ASN1Sequence)dIn.readObject()) + nl); 633 } 634 else if (oid.equals(X509Extensions.KeyUsage)) 635 { 636 buf.append(new KeyUsage((DERBitString)dIn.readObject()) + nl); 637 } 638 else if (oid.equals(MiscObjectIdentifiers.netscapeCertType)) 639 { 640 buf.append(new NetscapeCertType((DERBitString)dIn.readObject()) + nl); 641 } 642 else if (oid.equals(MiscObjectIdentifiers.netscapeRevocationURL)) 643 { 644 buf.append(new NetscapeRevocationURL((DERIA5String)dIn.readObject()) + nl); 645 } 646 else if (oid.equals(MiscObjectIdentifiers.verisignCzagExtension)) 647 { 648 buf.append(new VerisignCzagExtension((DERIA5String)dIn.readObject()) + nl); 649 } 650 else 651 { 652 buf.append(oid.getId()); 653 buf.append(" value = " + ASN1Dump.dumpAsString(dIn.readObject()) + nl); 654 //buf.append(" value = " + "*****" + nl); 655 } 656 } 657 catch (Exception ex) 658 { 659 buf.append(oid.getId()); 660 // buf.append(" value = " + new String(Hex.encode(ext.getValue().getOctets())) + nl); 661 buf.append(" value = " + "*****" + nl); 662 } 663 } 664 else 665 { 666 buf.append(nl); 667 } 668 } 669 } 670 671 return buf.toString(); 672 } 673 674 public final void verify( 675 PublicKey key) 676 throws CertificateException, NoSuchAlgorithmException, 677 InvalidKeyException, NoSuchProviderException, SignatureException 678 { 679 Signature signature = null; 680 681 if (!c.getSignatureAlgorithm().equals(c.getTBSCertificate().getSignature())) 682 { 683 throw new CertificateException("signature algorithm in TBS cert not same as outer cert"); 684 } 685 686 try 687 { 688 signature = Signature.getInstance(c.getSignatureAlgorithm().getObjectId().getId(), "BC"); 689 } 690 catch (Exception e) 691 { 692 signature = Signature.getInstance(c.getSignatureAlgorithm().getObjectId().getId()); 693 } 694 695 signature.initVerify(key); 696 697 signature.update(this.getTBSCertificate()); 698 699 if (!signature.verify(this.getSignature())) 700 { 701 throw new InvalidKeyException("Public key presented not for certificate signature"); 702 } 703 } 704 705 public final void verify( 706 PublicKey key, 707 String sigProvider) 708 throws CertificateException, NoSuchAlgorithmException, 709 InvalidKeyException, NoSuchProviderException, SignatureException 710 { 711 Signature signature = Signature.getInstance(c.getSignatureAlgorithm().getObjectId().getId(), sigProvider); 712 713 if (!c.getSignatureAlgorithm().equals(c.getTBSCertificate().getSignature())) 714 { 715 throw new CertificateException("signature algorithm in TBS cert not same as outer cert"); 716 } 717 718 signature.initVerify(key); 719 720 signature.update(this.getTBSCertificate()); 721 722 if (!signature.verify(this.getSignature())) 723 { 724 throw new InvalidKeyException("Public key presented not for certificate signature"); 725 } 726 } 727 }