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