| 1 13 14 package org.ejbca.util; 15 16 import java.io.BufferedReader ; 17 import java.io.ByteArrayInputStream ; 18 import java.io.ByteArrayOutputStream ; 19 import java.io.FileInputStream ; 20 import java.io.IOException ; 21 import java.io.InputStream ; 22 import java.io.InputStreamReader ; 23 import java.io.PrintStream ; 24 import java.math.BigInteger ; 25 import java.net.URL ; 26 import java.security.InvalidKeyException ; 27 import java.security.MessageDigest ; 28 import java.security.NoSuchAlgorithmException ; 29 import java.security.NoSuchProviderException ; 30 import java.security.PrivateKey ; 31 import java.security.PublicKey ; 32 import java.security.SecureRandom ; 33 import java.security.Security ; 34 import java.security.SignatureException ; 35 import java.security.cert.CRLException ; 36 import java.security.cert.Certificate ; 37 import java.security.cert.CertificateEncodingException ; 38 import java.security.cert.CertificateException ; 39 import java.security.cert.CertificateFactory ; 40 import java.security.cert.CertificateParsingException ; 41 import java.security.cert.X509CRL ; 42 import java.security.cert.X509Certificate ; 43 import java.util.ArrayList ; 44 import java.util.Collection ; 45 import java.util.Date ; 46 import java.util.Hashtable ; 47 import java.util.Iterator ; 48 import java.util.List ; 49 import java.util.Vector ; 50 51 import org.apache.commons.lang.BooleanUtils; 52 import org.apache.commons.lang.StringUtils; 53 import org.apache.log4j.Logger; 54 import org.bouncycastle.asn1.ASN1EncodableVector; 55 import org.bouncycastle.asn1.ASN1InputStream; 56 import org.bouncycastle.asn1.ASN1OctetString; 57 import org.bouncycastle.asn1.ASN1Sequence; 58 import org.bouncycastle.asn1.ASN1TaggedObject; 59 import org.bouncycastle.asn1.DERBitString; 60 import org.bouncycastle.asn1.DEREncodable; 61 import org.bouncycastle.asn1.DERIA5String; 62 import org.bouncycastle.asn1.DERObject; 63 import org.bouncycastle.asn1.DERObjectIdentifier; 64 import org.bouncycastle.asn1.DEROctetString; 65 import org.bouncycastle.asn1.DERSequence; 66 import org.bouncycastle.asn1.DERTaggedObject; 67 import org.bouncycastle.asn1.DERUTF8String; 68 import org.bouncycastle.asn1.x509.AccessDescription; 69 import org.bouncycastle.asn1.x509.AuthorityInformationAccess; 70 import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; 71 import org.bouncycastle.asn1.x509.BasicConstraints; 72 import org.bouncycastle.asn1.x509.GeneralName; 73 import org.bouncycastle.asn1.x509.GeneralNames; 74 import org.bouncycastle.asn1.x509.PolicyInformation; 75 import org.bouncycastle.asn1.x509.ReasonFlags; 76 import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; 77 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; 78 import org.bouncycastle.asn1.x509.X509DefaultEntryConverter; 79 import org.bouncycastle.asn1.x509.X509Extensions; 80 import org.bouncycastle.asn1.x509.X509Name; 81 import org.bouncycastle.asn1.x509.X509NameEntryConverter; 82 import org.bouncycastle.asn1.x509.X509NameTokenizer; 83 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; 84 import org.bouncycastle.jce.X509KeyUsage; 85 import org.bouncycastle.jce.interfaces.ConfigurableProvider; 86 import org.bouncycastle.jce.provider.BouncyCastleProvider; 87 import org.bouncycastle.math.ec.ECCurve; 88 import org.bouncycastle.util.encoders.Hex; 89 import org.bouncycastle.x509.X509V3CertificateGenerator; 90 import org.ejbca.core.model.ca.crl.RevokedCertInfo; 91 import org.ejbca.util.dn.DNFieldExtractor; 92 import org.ejbca.util.dn.DnComponents; 93 94 95 100 public class CertTools { 101 private static Logger log = Logger.getLogger(CertTools.class); 102 103 static { 105 DnComponents.getDnObjects(); 106 } 107 public static final String EMAIL = "rfc822name"; 108 public static final String EMAIL1 = "email"; 109 public static final String EMAIL2 = "EmailAddress"; 110 public static final String EMAIL3 = "E"; 111 public static final String DNS = "dNSName"; 112 public static final String URI = "uniformResourceIdentifier"; 113 public static final String URI1 = "uri"; 114 public static final String URI2 = "uniformResourceId"; 115 public static final String IPADDR = "iPAddress"; 116 public static final String DIRECTORYNAME = "directoryName"; 117 118 119 public static final String UPN = "upn"; 120 121 public static final String UPN_OBJECTID = "1.3.6.1.4.1.311.20.2.3"; 122 123 public static final String GUID = "guid"; 124 125 public static final String GUID_OBJECTID = "1.3.6.1.4.1.311.25.1"; 126 127 public static final String id_pkix = "1.3.6.1.5.5.7"; 128 129 public static final String id_pda = id_pkix + ".9"; 130 133 public static final String id_pda_dateOfBirth = id_pda + ".1"; 134 137 public static final String id_pda_placeOfBirth = id_pda + ".2"; 138 142 public static final String id_pda_gender = id_pda + ".3"; 143 147 public static final String id_pda_countryOfCitizenship = id_pda + ".4"; 148 152 public static final String id_pda_countryOfResidence = id_pda + ".5"; 153 154 public static final String QCSTATEMENTS_OBJECTID = "1.3.6.1.5.5.7.1.3"; 155 156 public static final String OID_MSTEMPLATE = "1.3.6.1.4.1.311.20.2"; 157 158 159 private static final String [] EMAILIDS = { EMAIL, EMAIL1, EMAIL2, EMAIL3 }; 160 161 163 165 168 private static String IMPLICITLYCA_Q = "@ecdsa.implicitlyca.q@"; 169 private static String IMPLICITLYCA_A = "@ecdsa.implicitlyca.a@"; 170 private static String IMPLICITLYCA_B = "@ecdsa.implicitlyca.b@"; 171 private static String IMPLICITLYCA_G = "@ecdsa.implicitlyca.g@"; 172 private static String IMPLICITLYCA_N = "@ecdsa.implicitlyca.n@"; 173 174 179 private static final boolean developmentProviderInstallation = BooleanUtils.toBoolean("@development.provider.installation@"); 180 181 184 protected CertTools() { 185 } 186 187 197 public static X509Name stringToBcX509Name(String dn) { 198 X509NameEntryConverter converter = new X509DefaultEntryConverter(); 199 return stringToBcX509Name(dn, converter); 200 201 202 } 203 219 public static X509Name stringToBcX509Name(String dn, X509NameEntryConverter converter) { 220 if (dn == null) 222 return null; 223 224 Vector defaultOrdering = new Vector (); 225 Vector values = new Vector (); 226 X509NameTokenizer xt = new X509NameTokenizer(dn); 227 228 while (xt.hasMoreTokens()) { 229 String pair = xt.nextToken(); 231 int ix = pair.indexOf("="); 232 233 if (ix != -1) { 234 String key = pair.substring(0, ix).toLowerCase(); 235 String val = pair.substring(ix + 1); 236 237 DERObjectIdentifier oid = DnComponents.getOid(key); 239 240 try { 241 if (oid == null) { 243 oid = new DERObjectIdentifier(key); 244 } 245 defaultOrdering.add(oid); 246 values.add(val); 247 } catch (IllegalArgumentException e) { 248 log.warn("Unknown DN component ignored and silently dropped: " + key); 250 } 251 252 } else { 253 log.warn("Huh, what's this? DN: " + dn+" PAIR: "+pair); 254 } 255 } 256 257 X509Name x509Name = new X509Name(defaultOrdering, values, converter); 258 259 X509Name orderedX509Name = getOrderedX509Name(x509Name, getDefaultX509FieldOrder(), converter); 261 262 return orderedX509Name; 264 } 266 267 268 276 public static String stringToBCDNString(String dn) { 277 if (isDNReversed(dn)) { 279 dn = reverseDN(dn); 280 } 281 String ret = null; 282 X509Name name = stringToBcX509Name(dn); 283 if (name != null) { 284 ret = name.toString(); 285 } 286 return ret; 288 } 289 290 300 public static ArrayList getEmailFromDN(String dn) { 301 log.debug(">getEmailFromDN(" + dn + ")"); 302 ArrayList ret = new ArrayList (); 303 for (int i = 0; i < EMAILIDS.length ; i++) { 304 ArrayList emails = getPartsFromDN(dn, EMAILIDS[i]); 305 if (emails.size() > 0) { 306 ret.addAll(emails); 307 } 308 309 } 310 log.debug("<getEmailFromDN(" + dn + "): " + ret.size()); 311 return ret; 312 } 313 314 322 public static String getEMailAddress(X509Certificate certificate) { 323 log.debug("Searching for EMail Address in SubjectAltName"); 324 if (certificate == null) { 325 return null; 326 } 327 try { 328 if (certificate.getSubjectAlternativeNames() != null) { 329 java.util.Collection altNames = certificate.getSubjectAlternativeNames(); 330 Iterator iter = altNames.iterator(); 331 while (iter.hasNext()) { 332 java.util.List item = (java.util.List )iter.next(); 333 Integer type = (Integer )item.get(0); 334 if (type.intValue() == 1) { 335 return (String )item.get(1); 336 } 337 } 338 } 339 } catch (CertificateParsingException e) { 340 log.error("Error parsing certificate: ", e); 341 } 342 log.debug("Searching for EMail Address in Subject DN"); 343 ArrayList emails = CertTools.getEmailFromDN(certificate.getSubjectDN().getName()); 344 if (emails.size() > 0) { 345 return (String )emails.get(0); 346 } 347 return null; 348 } 349 350 358 public static String reverseDN(String dn) { 359 log.debug(">reverseDN: dn: " + dn); 360 String ret = null; 361 if (dn != null) { 362 String o; 363 BasicX509NameTokenizer xt = new BasicX509NameTokenizer(dn); 364 StringBuffer buf = new StringBuffer (); 365 boolean first = true; 366 while (xt.hasMoreTokens()) { 367 o = xt.nextToken(); 368 if (!first) { 370 buf.insert(0,","); 371 } else { 372 first = false; 373 } 374 buf.insert(0,o); 375 } 376 if (buf.length() > 0) { 377 ret = buf.toString(); 378 } 379 } 380 381 log.debug("<reverseDN: resulting dn: " + ret); 382 return ret; 383 } 385 400 protected static boolean isDNReversed(String dn) { 401 boolean ret = false; 403 if (dn != null) { 404 String first = null; 405 String last = null; 406 X509NameTokenizer xt = new X509NameTokenizer(dn); 407 if (xt.hasMoreTokens()) { 408 first = xt.nextToken(); 409 } 410 while (xt.hasMoreTokens()) { 411 last = xt.nextToken(); 412 } 413 String [] dNObjects = DnComponents.getDnObjects(); 414 if ( (first != null) && (last != null) ) { 415 first = first.substring(0,first.indexOf('=')); 416 last = last.substring(0,last.indexOf('=')); 417 int firsti = 0, lasti = 0; 418 for (int i = 0; i < dNObjects.length; i++) { 419 if (first.toLowerCase().equals(dNObjects[i])) { 420 firsti = i; 421 } 422 if (last.toLowerCase().equals(dNObjects[i])) { 423 lasti = i; 424 } 425 } 426 if (lasti < firsti) { 427 ret = true; 428 } 429 430 } 431 } 432 return ret; 434 } 436 445 public static String getPartFromDN(String dn, String dnpart) { 446 log.debug(">getPartFromDN: dn:'" + dn + "', dnpart=" + dnpart); 447 String part = null; 448 if ((dn != null) && (dnpart != null)) { 449 String o; 450 dnpart += "="; X509NameTokenizer xt = new X509NameTokenizer(dn); 452 while (xt.hasMoreTokens()) { 453 o = xt.nextToken(); 454 if ((o.length() > dnpart.length()) && 456 o.substring(0, dnpart.length()).equalsIgnoreCase(dnpart)) { 457 part = o.substring(dnpart.length()); 458 459 break; 460 } 461 } 462 } 463 log.debug("<getpartFromDN: resulting DN part=" + part); 464 return part; 465 } 467 476 public static ArrayList getPartsFromDN(String dn, String dnpart) { 477 log.debug(">getPartsFromDN: dn:'" + dn + "', dnpart=" + dnpart); 478 ArrayList parts = new ArrayList (); 479 if ((dn != null) && (dnpart != null)) { 480 String o; 481 dnpart += "="; X509NameTokenizer xt = new X509NameTokenizer(dn); 483 while (xt.hasMoreTokens()) { 484 o = xt.nextToken(); 485 if ((o.length() > dnpart.length()) && 486 o.substring(0, dnpart.length()).equalsIgnoreCase(dnpart)) { 487 parts.add(o.substring(dnpart.length())); 488 } 489 } 490 } 491 log.debug("<getpartsFromDN: resulting DN part=" + parts.toString()); 492 return parts; 493 } 495 503 public static ArrayList getCustomOids(String dn) { 504 log.debug(">getCustomOids: dn:'" + dn); 505 ArrayList parts = new ArrayList (); 506 if (dn != null) { 507 String o; 508 X509NameTokenizer xt = new X509NameTokenizer(dn); 509 while (xt.hasMoreTokens()) { 510 o = xt.nextToken(); 511 try { 513 int i = o.indexOf('='); 514 if ( (i > 2) && (o.charAt(1) == '.') ) { 516 String oid = o.substring(0, i); 517 new DERObjectIdentifier(oid); 518 parts.add(oid); 519 } 520 } catch (IllegalArgumentException e) { 521 } 523 } 524 } 525 log.debug("<getpartsFromDN: resulting DN part=" + parts.toString()); 526 return parts; 527 } 529 536 public static String getSubjectDN(X509Certificate cert) { 537 return getDN(cert, 1); 538 } 539 540 547 public static String getIssuerDN(X509Certificate cert) { 548 return getDN(cert, 2); 549 } 550 551 559 private static String getDN(X509Certificate cert, int which) { 560 String dn = null; 562 if (cert == null) { 563 return dn; 564 } 565 try { 566 CertificateFactory cf = CertTools.getCertificateFactory(); 567 X509Certificate x509cert = (X509Certificate ) cf.generateCertificate(new ByteArrayInputStream ( 568 cert.getEncoded())); 569 571 if (which == 1) { 572 dn = x509cert.getSubjectDN().toString(); 573 } else { 574 dn = x509cert.getIssuerDN().toString(); 575 } 576 } catch (CertificateException ce) { 577 log.error("CertificateException: ", ce); 578 return null; 579 } 580 return stringToBCDNString(dn); 582 } 584 591 public static String getIssuerDN(X509CRL crl) { 592 String dn = null; 594 try { 595 CertificateFactory cf = CertTools.getCertificateFactory(); 596 X509CRL x509crl = (X509CRL ) cf.generateCRL(new ByteArrayInputStream (crl.getEncoded())); 597 dn = x509crl.getIssuerDN().toString(); 599 } catch (CRLException ce) { 600 log.error("CRLException: ", ce); 601 return null; 602 } 603 return stringToBCDNString(dn); 605 } 607 public static CertificateFactory getCertificateFactory() { 608 try { 609 return CertificateFactory.getInstance("X.509", "BC"); 610 } catch (NoSuchProviderException nspe) { 611 log.error("NoSuchProvider: ", nspe); 612 } catch (CertificateException ce) { 613 log.error("CertificateException: ", ce); 614 } 615 return null; 616 } 617 618 public static synchronized void removeBCProvider() { 619 Security.removeProvider("BC"); 620 } 621 public static synchronized void installBCProvider() { 622 boolean installImplicitlyCA = false; 624 if (Security.addProvider(new BouncyCastleProvider()) < 0) { 625 if (developmentProviderInstallation) { 630 removeBCProvider(); 631 if (Security.addProvider(new BouncyCastleProvider()) < 0) { 632 log.error("Cannot even install BC provider again!"); 633 } else { 634 installImplicitlyCA = true; 635 } 636 } 637 } else { 638 installImplicitlyCA = true; 639 } 640 if (installImplicitlyCA) { 641 checkImplicitParams(); 644 ECCurve curve = new ECCurve.Fp( 645 new BigInteger (IMPLICITLYCA_Q), new BigInteger (IMPLICITLYCA_A, 16), new BigInteger (IMPLICITLYCA_B, 16)); org.bouncycastle.jce.spec.ECParameterSpec implicitSpec = new org.bouncycastle.jce.spec.ECParameterSpec( 649 curve, 650 curve.decodePoint(Hex.decode(IMPLICITLYCA_G)), new BigInteger (IMPLICITLYCA_N)); ConfigurableProvider config = (ConfigurableProvider)Security.getProvider("BC"); 653 if (config != null) { 654 config.setParameter(ConfigurableProvider.EC_IMPLICITLY_CA, implicitSpec); 655 } else { 656 log.error("Can not get ConfigurableProvider, implicitlyCA EC parameters NOT set!"); 657 } 658 } 659 660 X509Name.DefaultSymbols.put(X509Name.SN, "SN"); 665 } 666 667 670 private static void checkImplicitParams() { 671 if (StringUtils.contains(IMPLICITLYCA_Q, "ecdsa.implicitlyca.q")) { 672 log.error("IMPLICITLYCA_Q not set!"); 673 IMPLICITLYCA_Q = "883423532389192164791648750360308885314476597252960362792450860609699839"; 674 } 675 if (StringUtils.contains(IMPLICITLYCA_A, "ecdsa.implicitlyca.a")) { 676 log.error("IMPLICITLYCA_A not set!"); 677 IMPLICITLYCA_A = "7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc"; 678 } 679 if (StringUtils.contains(IMPLICITLYCA_B, "ecdsa.implicitlyca.b")) { 680 log.error("IMPLICITLYCA_B not set!"); 681 IMPLICITLYCA_B = "6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a"; 682 } 683 if (StringUtils.contains(IMPLICITLYCA_G, "ecdsa.implicitlyca.g")) { 684 log.error("IMPLICITLYCA_G not set!"); 685 IMPLICITLYCA_G = "020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf"; 686 } 687 if (StringUtils.contains(IMPLICITLYCA_N, "ecdsa.implicitlyca.n")) { 688 log.error("IMPLICITLYCA_N not set!"); 689 IMPLICITLYCA_N = "883423532389192164791648750360308884807550341691627752275345424702807307"; 690 } 691 } 692 693 702 public static Collection getCertsFromPEM(String certFile) throws IOException , CertificateException { 703 log.debug(">getCertfromPEM: certFile=" + certFile); 704 InputStream inStrm = null; 705 Collection certs; 706 try { 707 inStrm = new FileInputStream (certFile); 708 certs = getCertsFromPEM(inStrm); 709 } finally { 710 if (inStrm != null) inStrm.close(); 711 } 712 log.debug("<getCertfromPEM: certFile=" + certFile); 713 return certs; 714 } 715 716 725 public static Collection getCertsFromPEM(InputStream
|