1 package org.eparapher.core.crypto.cert;
2
3 import java.io.ByteArrayInputStream;
4 import java.io.IOException;
5 import java.io.UnsupportedEncodingException;
6 import java.security.Principal;
7 import java.security.PublicKey;
8 import java.security.cert.CertificateParsingException;
9 import java.security.cert.X509Certificate;
10 import java.security.interfaces.RSAPublicKey;
11 import java.text.DateFormat;
12 import java.text.SimpleDateFormat;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.HashMap;
17 import java.util.Iterator;
18 import java.util.List;
19
20 import org.apache.log4j.Logger;
21 import org.bouncycastle.asn1.ASN1InputStream;
22 import org.bouncycastle.asn1.ASN1OctetString;
23 import org.bouncycastle.asn1.ASN1Sequence;
24 import org.bouncycastle.asn1.ASN1TaggedObject;
25 import org.bouncycastle.asn1.DEREncodable;
26 import org.bouncycastle.asn1.DERIA5String;
27 import org.bouncycastle.asn1.DERObject;
28 import org.bouncycastle.asn1.DERObjectIdentifier;
29 import org.bouncycastle.asn1.DERSequence;
30 import org.bouncycastle.asn1.DERTaggedObject;
31 import org.bouncycastle.asn1.DERUTF8String;
32 import org.bouncycastle.asn1.x509.DistributionPoint;
33 import org.bouncycastle.asn1.x509.GeneralName;
34 import org.bouncycastle.asn1.x509.GeneralNames;
35 import org.bouncycastle.asn1.x509.X509Name;
36 import org.bouncycastle.jce.X509Principal;
37 import org.bouncycastle.jce.provider.JCEECPublicKey;
38 import org.bouncycastle.jce.provider.JDKDSAPublicKey;
39 import org.bouncycastle.util.encoders.Hex;
40
41
42
43
44 public class CertificateInfo {
45
46 private static Logger log = Logger.getLogger(CertificateInfo.class);
47
48
49 public static final int DIGITALSIGNATURE = 0;
50 public static final int NONREPUDIATION = 1;
51 public static final int KEYENCIPHERMENT = 2;
52 public static final int DATAENCIPHERMENT = 3;
53 public static final int KEYAGREEMENT = 4;
54 public static final int KEYCERTSIGN = 5;
55 public static final int CRLSIGN = 6;
56 public static final int ENCIPHERONLY = 7;
57 public static final int DECIPHERONLY = 8;
58
59 public static final String[] KEYUSAGETEXTS = {
60 "DIGITALSIGNATURE",
61 "NONREPUDIATION",
62 "KEYENCIPHERMENT",
63 "DATAENCIPHERMENT",
64 "KEYAGREEMENT",
65 "KEYCERTSIGN",
66 "CRLSIGN",
67 "ENCIPHERONLY",
68 "DECIPHERONLY" };
69
70
71 public static final int ANYEXTENDEDKEYUSAGE = 0;
72 public static final int SERVERAUTH = 1;
73 public static final int CLIENTAUTH = 2;
74 public static final int CODESIGNING = 3;
75 public static final int EMAILPROTECTION = 4;
76 public static final int IPSECENDSYSTEM = 5;
77 public static final int IPSECTUNNEL = 6;
78 public static final int IPSECUSER = 7;
79 public static final int TIMESTAMPING = 8;
80 public static final int SMARTCARDLOGON = 9;
81 public static final int OCSPSIGNING = 10;
82
83 public static final String[] EXTENDEDKEYUSAGEOIDSTRINGS = {
84 "1.3.6.1.5.5.7.3.0",
85 "1.3.6.1.5.5.7.3.1",
86 "1.3.6.1.5.5.7.3.2",
87 "1.3.6.1.5.5.7.3.3",
88 "1.3.6.1.5.5.7.3.4",
89 "1.3.6.1.5.5.7.3.5",
90 "1.3.6.1.5.5.7.3.6",
91 "1.3.6.1.5.5.7.3.7",
92 "1.3.6.1.5.5.7.3.8",
93 "1.3.6.1.4.1.311.20.2.2",
94 "1.3.6.1.5.5.7.3.9" };
95
96 public static final String[] EXTENDEDKEYUSAGETEXTS = {
97 "ANYEXTENDEDKEYUSAGE",
98 "SERVERAUTH",
99 "CLIENTAUTH",
100 "CODESIGNING",
101 "EMAILPROTECTION",
102 "IPSECENDSYSTEM",
103 "IPSECTUNNEL",
104 "IPSECUSER",
105 "TIMESTAMPING",
106 "SMARTCARDLOGON",
107 "OCSPSIGNER" };
108
109 private static final int SUBALTNAME_OTHERNAME = 0;
110 private static final int SUBALTNAME_RFC822NAME = 1;
111 private static final int SUBALTNAME_DNSNAME = 2;
112 private static final int SUBALTNAME_X400ADDRESS = 3;
113 private static final int SUBALTNAME_DIRECTORYNAME = 4;
114 private static final int SUBALTNAME_EDIPARTYNAME = 5;
115 private static final int SUBALTNAME_URI = 6;
116 private static final int SUBALTNAME_IPADDRESS = 7;
117 private static final int SUBALTNAME_REGISTREDID = 8;
118
119
120 public static final String UPN = "upn";
121
122 public static final String UPN_OBJECTID = "1.3.6.1.4.1.311.20.2.3";
123
124
125 public static final String GUID = "guid";
126
127 public static final String GUID_OBJECTID = "1.3.6.1.4.1.311.25.1";
128
129 private static DateFormat completedateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
130 private static DateFormat simpledateFormat = new SimpleDateFormat("MM/dd/yyyy");
131
132 private X509Certificate certificate;
133 private X509Principal subjectdnfieldextractor, issuerdnfieldextractor;
134 private String subjectaltnamestring;
135 private String subjectdirattrstring;
136 private static HashMap<String, String> extendedkeyusageoidtotextmap;
137
138 public CertificateInfo(X509Certificate certificate) {
139 this.certificate = certificate;
140
141 subjectdnfieldextractor = new X509Principal(certificate.getSubjectDN().getName());
142 issuerdnfieldextractor = new X509Principal(certificate.getIssuerDN().getName());
143
144
145 if (extendedkeyusageoidtotextmap == null) {
146 extendedkeyusageoidtotextmap = new HashMap<String, String>();
147 for (int i = 0; i < EXTENDEDKEYUSAGETEXTS.length; i++)
148 extendedkeyusageoidtotextmap.put(EXTENDEDKEYUSAGEOIDSTRINGS[i],
149 EXTENDEDKEYUSAGETEXTS[i]);
150 }
151 }
152
153 public String getSubjectAltName() {
154 if (subjectaltnamestring == null)
155 try {
156 if (certificate.getSubjectAlternativeNames() != null) {
157 subjectaltnamestring = "";
158
159 String separator = "";
160 String guid = null;
161 try {
162 guid = getGuidAltName(certificate);
163 } catch (IOException e) {
164 subjectaltnamestring = e.getMessage();
165 }
166 if (guid != null) {
167 subjectaltnamestring += separator + "GUID=" + guid;
168 separator = ", ";
169 }
170 String upn = null;
171 try {
172 upn = getUPNAltName(certificate);
173 } catch (IOException e) {
174 subjectaltnamestring = e.getMessage();
175 }
176 if (upn != null) {
177 subjectaltnamestring += separator + "UPN=" + upn;
178 separator = ", ";
179 }
180
181 Iterator iter = certificate.getSubjectAlternativeNames()
182 .iterator();
183 while (iter.hasNext()) {
184 List next = (List) iter.next();
185 int OID = ((Integer) next.get(0)).intValue();
186
187 switch (OID) {
188 case SUBALTNAME_OTHERNAME:
189
190 Object obj = next.get(1);
191 if (obj!=null) {
192 subjectaltnamestring += separator + "OtherName=" + obj.toString();
193 separator = ", ";
194 }
195 break;
196 case SUBALTNAME_RFC822NAME:
197 subjectaltnamestring += separator + "RFC822Name="
198 + (String) next.get(1);
199 separator = ", ";
200 break;
201 case SUBALTNAME_DNSNAME:
202 subjectaltnamestring += separator + "DNSName="
203 + (String) next.get(1);
204 separator = ", ";
205 break;
206 case SUBALTNAME_X400ADDRESS:
207
208 break;
209 case SUBALTNAME_EDIPARTYNAME:
210
211 break;
212 case SUBALTNAME_DIRECTORYNAME:
213
214 break;
215 case SUBALTNAME_URI:
216 if (!subjectaltnamestring.equals(""))
217 subjectaltnamestring += ", ";
218 subjectaltnamestring += separator + "URI="
219 + (String) next.get(1);
220 separator = ", ";
221 break;
222 case SUBALTNAME_IPADDRESS:
223 subjectaltnamestring += separator + "IPAddress="
224 + (String) next.get(1);
225 separator = ", ";
226 break;
227 case SUBALTNAME_REGISTREDID:
228
229 break;
230 }
231
232 }
233 }
234 } catch (CertificateParsingException e) {
235 subjectaltnamestring = e.getMessage();
236 }
237
238 return subjectaltnamestring;
239 }
240
241
242
243
244
245
246
247 public static String getGuidAltName(X509Certificate cert)
248 throws IOException, CertificateParsingException {
249 Collection altNames = cert.getSubjectAlternativeNames();
250 if (altNames != null) {
251 Iterator i = altNames.iterator();
252 while (i.hasNext()) {
253 ASN1Sequence seq = getAltnameSequence((List) i.next());
254 if (seq != null) {
255
256 DERObjectIdentifier id = DERObjectIdentifier
257 .getInstance(seq.getObjectAt(0));
258 if (id.getId().equals(GUID_OBJECTID)) {
259 ASN1TaggedObject obj = (ASN1TaggedObject) seq
260 .getObjectAt(1);
261 ASN1OctetString str = ASN1OctetString.getInstance(obj
262 .getObject());
263 return new String(Hex.encode(str.getOctets()));
264 }
265 }
266 }
267 }
268 return null;
269 }
270
271
272
273
274
275
276
277 public static String getUPNAltName(X509Certificate cert)
278 throws IOException, CertificateParsingException {
279 Collection altNames = cert.getSubjectAlternativeNames();
280 if (altNames != null) {
281 Iterator i = altNames.iterator();
282 while (i.hasNext()) {
283 ASN1Sequence seq = getAltnameSequence((List) i.next());
284 String ret = getUPNStringFromSequence(seq);
285 if (ret != null)
286 return ret;
287 }
288 }
289 return null;
290 }
291
292
293
294 private static String getUPNStringFromSequence(ASN1Sequence seq) {
295 if (seq != null) {
296
297 DERObjectIdentifier id = DERObjectIdentifier.getInstance(seq
298 .getObjectAt(0));
299 if (id.getId().equals(UPN_OBJECTID)) {
300 ASN1TaggedObject obj = (ASN1TaggedObject) seq.getObjectAt(1);
301 DERUTF8String str = DERUTF8String.getInstance(obj.getObject());
302 return str.getString();
303 }
304 }
305 return null;
306 }
307
308
309
310 private static ASN1Sequence getAltnameSequence(List listitem)
311 throws IOException {
312 Integer no = (Integer) listitem.get(0);
313 if (no.intValue() == 0) {
314 byte[] altName = (byte[]) listitem.get(1);
315 return getAltnameSequence(altName);
316 }
317 return null;
318 }
319
320 private static ASN1Sequence getAltnameSequence(byte[] value)
321 throws IOException {
322 DERObject oct = null;
323 try {
324 oct = (new ASN1InputStream(new ByteArrayInputStream(value))
325 .readObject());
326 } catch (java.io.IOException e) {
327 log.error("Error on getting Alt Name as a DERSEquence : " + e.getLocalizedMessage(),e);
328 }
329 ASN1Sequence seq = ASN1Sequence.getInstance(oct);
330 return seq;
331 }
332
333 public static String getKeyUsageAsText(X509Certificate certificate){
334 if (certificate == null)
335 return null;
336
337 String kuText = "";
338 boolean[] keyusage = certificate.getKeyUsage();
339 if (keyusage == null) return "";
340 if (keyusage[0]) kuText += "digitalSignature";
341 if (keyusage[1]) kuText += (kuText.equals("")?"":", ") + "nonRepudiation";
342 if (keyusage[2]) kuText += (kuText.equals("")?"":", ") + "keyEncipherment";
343 if (keyusage[3]) kuText += (kuText.equals("")?"":", ") + "dataEncipherment";
344 if (keyusage[4]) kuText += (kuText.equals("")?"":", ") + "keyAgreement";
345 if (keyusage[5]) kuText += (kuText.equals("")?"":", ") + "keyCertSign";
346 if (keyusage[6]) kuText += (kuText.equals("")?"":", ") + "cRLSign";
347 if (keyusage[7]) kuText += (kuText.equals("")?"":", ") + "encipherOnly";
348 if (keyusage[8]) kuText += (kuText.equals("")?"":", ") + "decipherOnly";
349 return kuText;
350 }
351
352 @SuppressWarnings("unchecked")
353 public static String getExtendedKeyUsageAsText(X509Certificate certificate){
354 java.util.List extendedkeyusage = null;
355
356 HashMap<String, String> extendedkeyusageoidtotextmap = null;
357 String[] EXTENDEDKEYUSAGEOIDSTRINGS = { "2.5.29.37.0",
358 "1.3.6.1.5.5.7.3.0",
359 "1.3.6.1.5.5.7.3.1",
360 "1.3.6.1.5.5.7.3.2",
361 "1.3.6.1.5.5.7.3.3",
362 "1.3.6.1.5.5.7.3.4",
363 "1.3.6.1.5.5.7.3.5",
364 "1.3.6.1.5.5.7.3.6",
365 "1.3.6.1.5.5.7.3.7",
366 "1.3.6.1.5.5.7.3.8",
367 "1.3.6.1.4.1.311.20.2.2",
368 "1.3.6.1.5.5.7.3.9"};
369
370 String[] EXTENDEDKEYUSAGETEXTS = { "All usages",
371 "All usages",
372 "Server authentication",
373 "Client authentication",
374 "Code signing",
375 "Email protection",
376 "IPSec end system",
377 "IPSec tunnel",
378 "IPSec user",
379 "Timestamping",
380 "Smartcard Logon",
381 "OCSP signer"};
382
383
384 extendedkeyusageoidtotextmap = new HashMap<String, String>();
385 for(int i=0; i < EXTENDEDKEYUSAGETEXTS.length; i++)
386 extendedkeyusageoidtotextmap.put(EXTENDEDKEYUSAGEOIDSTRINGS[i], EXTENDEDKEYUSAGETEXTS[i]);
387 try{
388 extendedkeyusage = certificate.getExtendedKeyUsage();
389 } catch(java.security.cert.CertificateParsingException e){
390 log.error("certificate parsing exception" + e.getLocalizedMessage(),e);
391 return null;
392 }
393 if(extendedkeyusage == null)
394 extendedkeyusage = new java.util.ArrayList();
395
396
397
398
399
400
401 String returnval = "";
402 for(int i=0; i < extendedkeyusage.size(); i++)
403 returnval += (returnval.equals("")?"":", ") + extendedkeyusageoidtotextmap.get(extendedkeyusage.get(i));
404 return returnval;
405 }
406
407 public static String getSubjectAsShortText(X509Certificate certificate) {
408 certificate = X509Util.getBCCertificate(certificate);
409 return getDNAsShortText(certificate.getSubjectDN());
410 }
411
412 public static String getIssuerAsShortText(X509Certificate certificate) {
413 certificate = X509Util.getBCCertificate(certificate);
414 return getDNAsShortText(certificate.getIssuerDN());
415 }
416
417
418 public static String getDNAsShortText(Principal dn) {
419 X509Principal X509dn = new X509Principal(dn.getName());
420
421 if (X509dn!=null && X509dn.getValues(X509Principal.CN).size()>0)
422 return X509dn.getValues(X509Principal.CN).get(0).toString();
423 else {
424 String str_dn = dn.getName();
425 int last_equal = str_dn.lastIndexOf("=");
426 if (last_equal>=0)
427 return str_dn.substring( last_equal+1, str_dn.length());
428 return str_dn;
429 }
430 }
431
432
433
434 public static String getNotBeforeAsText(X509Certificate certificate) {
435 return simpledateFormat.format( certificate.getNotBefore() );
436 }
437 public static String getNotBeforeAsFullText(X509Certificate certificate) {
438 return completedateFormat.format( certificate.getNotBefore() );
439 }
440 public static String getNotAfterAsText(X509Certificate certificate) {
441 return simpledateFormat.format( certificate.getNotAfter() );
442 }
443 public static String getNotAfterAsFullText(X509Certificate certificate) {
444 return completedateFormat.format( certificate.getNotAfter() );
445 }
446
447 public static String getPublicKeyInfo(PublicKey pk) {
448 int keysize = 0;
449 String format = pk.getAlgorithm();
450 if (pk instanceof RSAPublicKey) {
451 RSAPublicKey rsapk = (RSAPublicKey) pk;
452 keysize = (rsapk.getModulus().toByteArray().length -1) * 8;
453 }
454 if (pk instanceof JCEECPublicKey) {
455 JCEECPublicKey ecpubkey = (JCEECPublicKey) pk;
456 keysize = ecpubkey.getQ().getX().getFieldSize();
457
458 format = "ECDSA";
459
460
461
462
463 }
464 if (pk instanceof JDKDSAPublicKey) {
465 JDKDSAPublicKey dsapubkey = (JDKDSAPublicKey) pk;
466 keysize = dsapubkey.getY().bitLength();
467 }
468
469 return format + " " + keysize + "bits";
470 }
471
472 public static List<String> getSubjectAlternativeNames(X509Certificate certificate) {
473 List<String> identities = new ArrayList<String>();
474 try {
475 Collection<List<?>> altNames = certificate.getSubjectAlternativeNames();
476
477 if (altNames == null)
478 return Collections.emptyList();
479
480 for (List item : altNames) {
481 Integer type = (Integer) item.get(0);
482 if (type == 0)
483
484 try {
485
486 ASN1InputStream decoder = new ASN1InputStream((byte[]) item.toArray()[1]);
487 DEREncodable encoded = decoder.readObject();
488 encoded = ((DERSequence) encoded).getObjectAt(1);
489 encoded = ((DERTaggedObject) encoded).getObject();
490 encoded = ((DERTaggedObject) encoded).getObject();
491 String identity = ((DERUTF8String) encoded).getString();
492
493 identities.add(identity);
494 }
495 catch (UnsupportedEncodingException e) {
496 log.error("Error decoding subjectAltName" + e.getLocalizedMessage(),e);
497 }
498 catch (Exception e) {
499 log.error("Error decoding subjectAltName" + e.getLocalizedMessage(),e);
500 }
501
502 log.warn("SubjectAltName of invalid type found: " + certificate);
503 }
504 }
505 catch (CertificateParsingException e) {
506 log.error("Error parsing SubjectAltName in certificate: " + certificate + "\r\nerror:" + e.getLocalizedMessage(),e);
507 }
508 return identities;
509 }
510
511 public String getCDPAsText() {
512
513 DistributionPoint[] cdp = null;
514 try {
515 cdp = X509Util.getCrlDistributionPoint(certificate);
516 } catch (CertificateParsingException e) {
517 log.error("Error while parsing CDP");
518 }
519 String returnvalue = "";
520 for (DistributionPoint distributionPoint : cdp) {
521 if (!returnvalue.equals(""))
522 returnvalue += System.getProperty("line.separator");
523 if (distributionPoint.getCRLIssuer()!=null)
524 returnvalue += distributionPoint.getCRLIssuer() + "=";
525
526 GeneralNames cdpgns = GeneralNames.getInstance(distributionPoint.getDistributionPoint().getName());
527 GeneralName[] cdpgn = cdpgns.getNames();
528 for (GeneralName element : cdpgn)
529 returnvalue += GeneralNameAsText(element);
530 }
531 return returnvalue;
532 }
533 public static String GeneralNameAsText(GeneralName gn) {
534
535 StringBuffer buf = new StringBuffer();
536
537 int tag = gn.getTagNo();
538 DEREncodable obj = gn.getName();
539
540 switch (tag) {
541 case GeneralName.rfc822Name:
542 buf.append("rfc822Name=");
543 buf.append(DERIA5String.getInstance(obj).getString());
544 break;
545 case GeneralName.dNSName:
546 buf.append("dNSName=");
547 buf.append(DERIA5String.getInstance(obj).getString());
548 break;
549 case GeneralName.uniformResourceIdentifier:
550 buf.append("URI=");
551 buf.append(DERIA5String.getInstance(obj).getString());
552 break;
553 case GeneralName.directoryName:
554 buf.append("directoryName=");
555 buf.append(X509Name.getInstance(obj).toString());
556 break;
557 case GeneralName.ediPartyName:
558 buf.append("ediPartyName=");
559 buf.append(obj.toString());
560 break;
561 case GeneralName.iPAddress:
562 buf.append("IP=");
563 buf.append(obj.toString());
564 break;
565 case GeneralName.otherName:
566 buf.append("otherName=");
567 buf.append(obj.toString());
568 break;
569 case GeneralName.registeredID:
570 buf.append("registeredID=");
571 buf.append(obj.toString());
572 break;
573 case GeneralName.x400Address:
574 buf.append("x400Address=");
575 buf.append(obj.toString());
576 break;
577 default:
578 buf.append(gn.getTagNo()+"=");
579 }
580
581 return buf.toString();
582 }
583 }