KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > lowagie > text > pdf > PdfPKCS7


1 /*
2  * Copyright 2004 by Paulo Soares.
3  *
4  * The contents of this file are subject to the Mozilla Public License Version 1.1
5  * (the "License"); you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at http://www.mozilla.org/MPL/
7  *
8  * Software distributed under the License is distributed on an "AS IS" basis,
9  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
10  * for the specific language governing rights and limitations under the License.
11  *
12  * The Original Code is 'iText, a free JAVA-PDF library'.
13  *
14  * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
15  * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
16  * All Rights Reserved.
17  * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
18  * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
19  *
20  * Contributor(s): all the names of the contributors are added in the source code
21  * where applicable.
22  *
23  * Alternatively, the contents of this file may be used under the terms of the
24  * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
25  * provisions of LGPL are applicable instead of those above. If you wish to
26  * allow use of your version of this file only under the terms of the LGPL
27  * License and not to allow others to use your version of this file under
28  * the MPL, indicate your decision by deleting the provisions above and
29  * replace them with the notice and other provisions required by the LGPL.
30  * If you do not delete the provisions above, a recipient may use your version
31  * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
32  *
33  * This library is free software; you can redistribute it and/or modify it
34  * under the terms of the MPL as stated above or under the terms of the GNU
35  * Library General Public License as published by the Free Software Foundation;
36  * either version 2 of the License, or any later version.
37  *
38  * This library is distributed in the hope that it will be useful, but WITHOUT
39  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
40  * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
41  * details.
42  *
43  * If you didn't download this code from the following link, you should check if
44  * you aren't using an obsolete version:
45  * http://www.lowagie.com/iText/
46  */

47 package com.lowagie.text.pdf;
48
49 import java.io.ByteArrayInputStream JavaDoc;
50 import java.io.ByteArrayOutputStream JavaDoc;
51 import java.io.File JavaDoc;
52 import java.io.FileInputStream JavaDoc;
53 import java.io.IOException JavaDoc;
54 import java.security.InvalidKeyException JavaDoc;
55 import java.security.KeyStore JavaDoc;
56 import java.security.MessageDigest JavaDoc;
57 import java.security.NoSuchAlgorithmException JavaDoc;
58 import java.security.NoSuchProviderException JavaDoc;
59 import java.security.PrivateKey JavaDoc;
60 import java.security.Signature JavaDoc;
61 import java.security.SignatureException JavaDoc;
62 import java.security.cert.CRL JavaDoc;
63 import java.security.cert.CRLException JavaDoc;
64 import java.security.cert.Certificate JavaDoc;
65 import java.security.cert.CertificateException JavaDoc;
66 import java.security.cert.X509CRL JavaDoc;
67 import java.security.cert.X509Certificate JavaDoc;
68 import java.util.ArrayList JavaDoc;
69 import java.util.Arrays JavaDoc;
70 import java.util.Calendar JavaDoc;
71 import java.util.Collection JavaDoc;
72 import java.util.Enumeration JavaDoc;
73 import java.util.GregorianCalendar JavaDoc;
74 import java.util.HashMap JavaDoc;
75 import java.util.HashSet JavaDoc;
76 import java.util.Iterator JavaDoc;
77 import java.util.Set JavaDoc;
78
79 import com.lowagie.text.ExceptionConverter;
80 import java.math.BigInteger JavaDoc;
81 import org.bouncycastle.asn1.ASN1EncodableVector;
82 import org.bouncycastle.asn1.ASN1InputStream;
83 import org.bouncycastle.asn1.ASN1OutputStream;
84 import org.bouncycastle.asn1.ASN1Sequence;
85 import org.bouncycastle.asn1.ASN1Set;
86 import org.bouncycastle.asn1.ASN1TaggedObject;
87 import org.bouncycastle.asn1.DERConstructedSet;
88 import org.bouncycastle.asn1.DERInteger;
89 import org.bouncycastle.asn1.DERNull;
90 import org.bouncycastle.asn1.DERObject;
91 import org.bouncycastle.asn1.DERObjectIdentifier;
92 import org.bouncycastle.asn1.DEROctetString;
93 import org.bouncycastle.asn1.DERSequence;
94 import org.bouncycastle.asn1.DERSet;
95 import org.bouncycastle.asn1.DERString;
96 import org.bouncycastle.asn1.DERTaggedObject;
97 import org.bouncycastle.asn1.DERUTCTime;
98 import org.bouncycastle.jce.provider.X509CRLParser;
99 import org.bouncycastle.jce.provider.X509CertParser;
100 import org.bouncycastle.util.StreamParsingException;
101
102 /**
103  * This class does all the processing related to signing and verifying a PKCS#7
104  * signature.
105  * <p>
106  * It's based in code found at org.bouncycastle.
107  */

108 public class PdfPKCS7 {
109
110     private byte sigAttr[];
111     private byte digestAttr[];
112     private int version, signerversion;
113     private Set JavaDoc digestalgos;
114     private Collection JavaDoc certs, crls;
115     private X509Certificate JavaDoc signCert;
116     private byte[] digest;
117     private MessageDigest JavaDoc messageDigest;
118     private String JavaDoc digestAlgorithm, digestEncryptionAlgorithm;
119     private Signature JavaDoc sig;
120     private transient PrivateKey JavaDoc privKey;
121     private byte RSAdata[];
122     private boolean verified;
123     private boolean verifyResult;
124     private byte externalDigest[];
125     private byte externalRSAdata[];
126     
127     private static final String JavaDoc ID_PKCS7_DATA = "1.2.840.113549.1.7.1";
128     private static final String JavaDoc ID_PKCS7_SIGNED_DATA = "1.2.840.113549.1.7.2";
129     private static final String JavaDoc ID_MD5 = "1.2.840.113549.2.5";
130     private static final String JavaDoc ID_MD2 = "1.2.840.113549.2.2";
131     private static final String JavaDoc ID_SHA1 = "1.3.14.3.2.26";
132     private static final String JavaDoc ID_RSA = "1.2.840.113549.1.1.1";
133     private static final String JavaDoc ID_DSA = "1.2.840.10040.4.1";
134     private static final String JavaDoc ID_CONTENT_TYPE = "1.2.840.113549.1.9.3";
135     private static final String JavaDoc ID_MESSAGE_DIGEST = "1.2.840.113549.1.9.4";
136     private static final String JavaDoc ID_SIGNING_TIME = "1.2.840.113549.1.9.5";
137     private static final String JavaDoc ID_MD2RSA = "1.2.840.113549.1.1.2";
138     private static final String JavaDoc ID_MD5RSA = "1.2.840.113549.1.1.4";
139     private static final String JavaDoc ID_SHA1RSA = "1.2.840.113549.1.1.5";
140     private static final String JavaDoc ID_ADBE_REVOCATION = "1.2.840.113583.1.1.8";
141     /**
142      * Holds value of property reason.
143      */

144     private String JavaDoc reason;
145     
146     /**
147      * Holds value of property location.
148      */

149     private String JavaDoc location;
150     
151     /**
152      * Holds value of property signDate.
153      */

154     private Calendar JavaDoc signDate;
155     
156     /**
157      * Holds value of property signName.
158      */

159     private String JavaDoc signName;
160     
161     /**
162      * Verifies a signature using the sub-filter adbe.x509.rsa_sha1.
163      * @param contentsKey the /Contents key
164      * @param certsKey the /Cert key
165      * @param provider the provider or <code>null</code> for the default provider
166      * @throws SecurityException on error
167      * @throws InvalidKeyException on error
168      * @throws CertificateException on error
169      * @throws NoSuchProviderException on error
170      * @throws NoSuchAlgorithmException on error
171      * @throws IOException on error
172      */

173     public PdfPKCS7(byte[] contentsKey, byte[] certsKey, String JavaDoc provider) throws SecurityException JavaDoc, InvalidKeyException JavaDoc, CertificateException JavaDoc, NoSuchProviderException JavaDoc, NoSuchAlgorithmException JavaDoc, IOException JavaDoc, StreamParsingException {
174         X509CertParser cr = new X509CertParser();
175         cr.engineInit(new ByteArrayInputStream JavaDoc(certsKey));
176         certs = cr.engineReadAll();
177         signCert = (X509Certificate JavaDoc)certs.iterator().next();
178         crls = new ArrayList JavaDoc();
179         ASN1InputStream in = new ASN1InputStream(new ByteArrayInputStream JavaDoc(contentsKey));
180         digest = ((DEROctetString)in.readObject()).getOctets();
181         if (provider == null)
182             sig = Signature.getInstance("SHA1withRSA");
183         else
184             sig = Signature.getInstance("SHA1withRSA", provider);
185         sig.initVerify(signCert.getPublicKey());
186     }
187     
188     /**
189      * Verifies a signature using the sub-filter adbe.pkcs7.detached or
190      * adbe.pkcs7.sha1.
191      * @param contentsKey the /Contents key
192      * @param provider the provider or <code>null</code> for the default provider
193      * @throws SecurityException on error
194      * @throws CRLException on error
195      * @throws InvalidKeyException on error
196      * @throws CertificateException on error
197      * @throws NoSuchProviderException on error
198      * @throws NoSuchAlgorithmException on error
199      */

200     public PdfPKCS7(byte[] contentsKey, String JavaDoc provider) throws SecurityException JavaDoc, CRLException JavaDoc, InvalidKeyException JavaDoc, CertificateException JavaDoc, NoSuchProviderException JavaDoc, NoSuchAlgorithmException JavaDoc, StreamParsingException {
201         ASN1InputStream din = new ASN1InputStream(new ByteArrayInputStream JavaDoc(contentsKey));
202         
203         //
204
// Basic checks to make sure it's a PKCS#7 SignedData Object
205
//
206
DERObject pkcs;
207         
208         try {
209             pkcs = din.readObject();
210         }
211         catch (IOException JavaDoc e) {
212             throw new SecurityException JavaDoc("can't decode PKCS7SignedData object");
213         }
214         if (!(pkcs instanceof ASN1Sequence)) {
215             throw new SecurityException JavaDoc("Not a valid PKCS#7 object - not a sequence");
216         }
217         ASN1Sequence signedData = (ASN1Sequence)pkcs;
218         DERObjectIdentifier objId = (DERObjectIdentifier)signedData.getObjectAt(0);
219         if (!objId.getId().equals(ID_PKCS7_SIGNED_DATA))
220             throw new SecurityException JavaDoc("Not a valid PKCS#7 object - not signed data");
221         ASN1Sequence content = (ASN1Sequence)((DERTaggedObject)signedData.getObjectAt(1)).getObject();
222         // the positions that we care are:
223
// 0 - version
224
// 1 - digestAlgorithms
225
// 2 - possible ID_PKCS7_DATA
226
// (the certificates and crls are taken out by other means)
227
// last - signerInfos
228

229         // the version
230
version = ((DERInteger)content.getObjectAt(0)).getValue().intValue();
231         
232         // the digestAlgorithms
233
digestalgos = new HashSet JavaDoc();
234         Enumeration JavaDoc e = ((ASN1Set)content.getObjectAt(1)).getObjects();
235         while (e.hasMoreElements())
236         {
237             ASN1Sequence s = (ASN1Sequence)e.nextElement();
238             DERObjectIdentifier o = (DERObjectIdentifier)s.getObjectAt(0);
239             digestalgos.add(o.getId());
240         }
241         
242         // the certificates and crls
243
X509CertParser cr = new X509CertParser();
244         cr.engineInit(new ByteArrayInputStream JavaDoc(contentsKey));
245         certs = cr.engineReadAll();
246         X509CRLParser cl = new X509CRLParser();
247         cl.engineInit(new ByteArrayInputStream JavaDoc(contentsKey));
248         crls = cl.engineReadAll();
249         
250         // the possible ID_PKCS7_DATA
251
ASN1Sequence rsaData = (ASN1Sequence)content.getObjectAt(2);
252         if (rsaData.size() > 1) {
253             DEROctetString rsaDataContent = (DEROctetString)((DERTaggedObject)rsaData.getObjectAt(1)).getObject();
254             RSAdata = rsaDataContent.getOctets();
255         }
256         
257         // the signerInfos
258
int next = 3;
259         while (content.getObjectAt(next) instanceof DERTaggedObject)
260             ++next;
261         ASN1Set signerInfos = (ASN1Set)content.getObjectAt(next);
262         if (signerInfos.size() != 1)
263             throw new SecurityException JavaDoc("This PKCS#7 object has multiple SignerInfos - only one is supported at this time");
264         ASN1Sequence signerInfo = (ASN1Sequence)signerInfos.getObjectAt(0);
265         // the positions that we care are
266
// 0 - version
267
// 1 - the signing certificate serial number
268
// 2 - the digest algorithm
269
// 3 or 4 - digestEncryptionAlgorithm
270
// 4 or 5 - encryptedDigest
271
signerversion = ((DERInteger)signerInfo.getObjectAt(0)).getValue().intValue();
272         // Get the signing certificate
273
ASN1Sequence issuerAndSerialNumber = (ASN1Sequence)signerInfo.getObjectAt(1);
274         BigInteger JavaDoc serialNumber = ((DERInteger)issuerAndSerialNumber.getObjectAt(1)).getValue();
275         for (Iterator JavaDoc i = certs.iterator(); i.hasNext();) {
276             X509Certificate JavaDoc cert = (X509Certificate JavaDoc)i.next();
277             if (serialNumber.equals(cert.getSerialNumber())) {
278                 signCert = cert;
279                 break;
280             }
281         }
282         if (signCert == null) {
283             throw new SecurityException JavaDoc("Can't find signing certificate with serial " + serialNumber.toString(16));
284         }
285         digestAlgorithm = ((DERObjectIdentifier)((ASN1Sequence)signerInfo.getObjectAt(2)).getObjectAt(0)).getId();
286         next = 3;
287         if (signerInfo.getObjectAt(next) instanceof ASN1TaggedObject) {
288             ASN1TaggedObject tagsig = (ASN1TaggedObject)signerInfo.getObjectAt(next);
289             ASN1Sequence sseq = (ASN1Sequence)tagsig.getObject();
290             ByteArrayOutputStream JavaDoc bOut = new ByteArrayOutputStream JavaDoc();
291             ASN1OutputStream dout = new ASN1OutputStream(bOut);
292             try {
293                 ASN1EncodableVector attribute = new ASN1EncodableVector();
294                 for (int k = 0; k < sseq.size(); ++k) {
295                     attribute.add(sseq.getObjectAt(k));
296                 }
297                 dout.writeObject(new DERSet(attribute));
298                 dout.close();
299             }
300             catch (IOException JavaDoc ioe){}
301             sigAttr = bOut.toByteArray();
302             
303             for (int k = 0; k < sseq.size(); ++k) {
304                 ASN1Sequence seq2 = (ASN1Sequence)sseq.getObjectAt(k);
305                 if (((DERObjectIdentifier)seq2.getObjectAt(0)).getId().equals(ID_MESSAGE_DIGEST)) {
306                     ASN1Set set = (ASN1Set)seq2.getObjectAt(1);
307                     digestAttr = ((DEROctetString)set.getObjectAt(0)).getOctets();
308                     break;
309                 }
310             }
311             if (digestAttr == null)
312                 throw new SecurityException JavaDoc("Authenticated attribute is missing the digest.");
313             ++next;
314         }
315         digestEncryptionAlgorithm = ((DERObjectIdentifier)((ASN1Sequence)signerInfo.getObjectAt(next++)).getObjectAt(0)).getId();
316         digest = ((DEROctetString)signerInfo.getObjectAt(next)).getOctets();
317         if (RSAdata != null || digestAttr != null) {
318             if (provider == null || provider.startsWith("SunPKCS11"))
319                 messageDigest = MessageDigest.getInstance(getHashAlgorithm());
320             else
321                 messageDigest = MessageDigest.getInstance(getHashAlgorithm(), provider);
322         }
323         if (provider == null)
324             sig = Signature.getInstance(getDigestAlgorithm());
325         else
326             sig = Signature.getInstance(getDigestAlgorithm(), provider);
327         sig.initVerify(signCert.getPublicKey());
328     }
329
330     /**
331      * Generates a signature.
332      * @param privKey the private key
333      * @param certChain the certificate chain
334      * @param crlList the certificate revocation list
335      * @param hashAlgorithm the hash algorithm
336      * @param provider the provider or <code>null</code> for the default provider
337      * @param hasRSAdata <CODE>true</CODE> if the sub-filter is adbe.pkcs7.sha1
338      * @throws SecurityException on error
339      * @throws InvalidKeyException on error
340      * @throws NoSuchProviderException on error
341      * @throws NoSuchAlgorithmException on error
342      */

343     public PdfPKCS7(PrivateKey JavaDoc privKey, Certificate JavaDoc[] certChain, CRL JavaDoc[] crlList,
344                     String JavaDoc hashAlgorithm, String JavaDoc provider, boolean hasRSAdata)
345       throws SecurityException JavaDoc, InvalidKeyException JavaDoc, NoSuchProviderException JavaDoc,
346       NoSuchAlgorithmException JavaDoc
347     {
348         this.privKey = privKey;
349         
350         if (hashAlgorithm.equals("MD5")) {
351             digestAlgorithm = ID_MD5;
352         }
353         else if (hashAlgorithm.equals("MD2")) {
354             digestAlgorithm = ID_MD2;
355         }
356         else if (hashAlgorithm.equals("SHA")) {
357             digestAlgorithm = ID_SHA1;
358         }
359         else if (hashAlgorithm.equals("SHA1")) {
360             digestAlgorithm = ID_SHA1;
361         }
362         else {
363             throw new NoSuchAlgorithmException JavaDoc("Unknown Hash Algorithm "+hashAlgorithm);
364         }
365         
366         version = signerversion = 1;
367         certs = new ArrayList JavaDoc();
368         crls = new ArrayList JavaDoc();
369         digestalgos = new HashSet JavaDoc();
370         digestalgos.add(digestAlgorithm);
371         
372         //
373
// Copy in the certificates and crls used to sign the private key.
374
//
375
signCert = (X509Certificate JavaDoc)certChain[0];
376         for (int i = 0;i < certChain.length;i++) {
377             certs.add(certChain[i]);
378         }
379         
380         if (crlList != null) {
381             for (int i = 0;i < crlList.length;i++) {
382                 crls.add(crlList[i]);
383             }
384         }
385         
386         if (privKey != null) {
387             //
388
// Now we have private key, find out what the digestEncryptionAlgorithm is.
389
//
390
digestEncryptionAlgorithm = privKey.getAlgorithm();
391             if (digestEncryptionAlgorithm.equals("RSA")) {
392                 digestEncryptionAlgorithm = ID_RSA;
393             }
394             else if (digestEncryptionAlgorithm.equals("DSA")) {
395                 digestEncryptionAlgorithm = ID_DSA;
396             }
397             else {
398                 throw new NoSuchAlgorithmException JavaDoc("Unknown Key Algorithm "+digestEncryptionAlgorithm);
399             }
400         }
401         if (hasRSAdata) {
402             RSAdata = new byte[0];
403             if (provider == null || provider.startsWith("SunPKCS11"))
404                 messageDigest = MessageDigest.getInstance(getHashAlgorithm());
405             else
406                 messageDigest = MessageDigest.getInstance(getHashAlgorithm(), provider);
407         }
408
409         if (privKey != null) {
410             if (provider == null)
411                 sig = Signature.getInstance(getDigestAlgorithm());
412             else
413                 sig = Signature.getInstance(getDigestAlgorithm(), provider);
414
415             sig.initSign(privKey);
416         }
417     }
418
419     /**
420      * Update the digest with the specified bytes. This method is used both for signing and verifying
421      * @param buf the data buffer
422      * @param off the offset in the data buffer
423      * @param len the data length
424      * @throws SignatureException on error
425      */

426     public void update(byte[] buf, int off, int len) throws SignatureException JavaDoc {
427         if (RSAdata != null || digestAttr != null)
428             messageDigest.update(buf, off, len);
429         else
430             sig.update(buf, off, len);
431     }
432     
433     /**
434      * Verify the digest.
435      * @throws SignatureException on error
436      * @return <CODE>true</CODE> if the signature checks out, <CODE>false</CODE> otherwise
437      */

438     public boolean verify() throws SignatureException JavaDoc {
439         if (verified)
440             return verifyResult;
441         if (sigAttr != null) {
442             sig.update(sigAttr);
443             if (RSAdata != null) {
444                 byte msd[] = messageDigest.digest();
445                 messageDigest.update(msd);
446             }
447             verifyResult = (Arrays.equals(messageDigest.digest(), digestAttr) && sig.verify(digest));
448         }
449         else {
450             if (RSAdata != null)
451                 sig.update(messageDigest.digest());
452             verifyResult = sig.verify(digest);
453         }
454         verified = true;
455         return verifyResult;
456     }
457     
458     /**
459      * Get the X.509 certificates associated with this PKCS#7 object
460      * @return the X.509 certificates associated with this PKCS#7 object
461      */

462     public Certificate JavaDoc[] getCertificates() {
463         return (X509Certificate JavaDoc[])certs.toArray(new X509Certificate JavaDoc[certs.size()]);
464     }
465     
466     /**
467      * Get the X.509 certificate revocation lists associated with this PKCS#7 object
468      * @return the X.509 certificate revocation lists associated with this PKCS#7 object
469      */

470     public Collection JavaDoc getCRLs() {
471         return crls;
472     }
473     
474     /**
475      * Get the X.509 certificate actually used to sign the digest.
476      * @return the X.509 certificate actually used to sign the digest
477      */

478     public X509Certificate JavaDoc getSigningCertificate() {
479         return signCert;
480     }
481     
482     /**
483      * Get the version of the PKCS#7 object. Always 1
484      * @return the version of the PKCS#7 object. Always 1
485      */

486     public int getVersion() {
487         return version;
488     }
489     
490     /**
491      * Get the version of the PKCS#7 "SignerInfo" object. Always 1
492      * @return the version of the PKCS#7 "SignerInfo" object. Always 1
493      */

494     public int getSigningInfoVersion() {
495         return signerversion;
496     }
497     
498     /**
499      * Get the algorithm used to calculate the message digest
500      * @return the algorithm used to calculate the message digest
501      */

502     public String JavaDoc getDigestAlgorithm() {
503         String JavaDoc dea = digestEncryptionAlgorithm;
504         
505         if (digestEncryptionAlgorithm.equals(ID_RSA) || digestEncryptionAlgorithm.equals(ID_MD5RSA)
506             || digestEncryptionAlgorithm.equals(ID_MD2RSA) || digestEncryptionAlgorithm.equals(ID_SHA1RSA)) {
507             dea = "RSA";
508         }
509         else if (digestEncryptionAlgorithm.equals(ID_DSA)) {
510             dea = "DSA";
511         }
512         
513         return getHashAlgorithm() + "with" + dea;
514     }
515
516     /**
517      * Returns the algorithm.
518      * @return the digest algorithm
519      */

520     public String JavaDoc getHashAlgorithm() {
521         String JavaDoc da = digestAlgorithm;
522         
523         if (digestAlgorithm.equals(ID_MD5) || digestAlgorithm.equals(ID_MD5RSA)) {
524             da = "MD5";
525         }
526         else if (digestAlgorithm.equals(ID_MD2) || digestAlgorithm.equals(ID_MD2RSA)) {
527             da = "MD2";
528         }
529         else if (digestAlgorithm.equals(ID_SHA1) || digestAlgorithm.equals(ID_SHA1RSA)) {
530             da = "SHA1";
531         }
532         return da;
533     }
534
535     /**
536      * Loads the default root certificates at &lt;java.home&gt;/lib/security/cacerts
537      * with the default provider.
538      * @return a <CODE>KeyStore</CODE>
539      */

540     public static KeyStore JavaDoc loadCacertsKeyStore() {
541         return loadCacertsKeyStore(null);
542     }
543
544     /**
545      * Loads the default root certificates at &lt;java.home&gt;/lib/security/cacerts.
546      * @param provider the provider or <code>null</code> for the default provider
547      * @return a <CODE>KeyStore</CODE>
548      */

549     public static KeyStore JavaDoc loadCacertsKeyStore(String JavaDoc provider) {
550         File JavaDoc file = new File JavaDoc(System.getProperty("java.home"), "lib");
551         file = new File JavaDoc(file, "security");
552         file = new File JavaDoc(file, "cacerts");
553         FileInputStream JavaDoc fin = null;
554         try {
555             fin = new FileInputStream JavaDoc(file);
556             KeyStore JavaDoc k;
557             if (provider == null)
558                 k = KeyStore.getInstance("JKS");
559             else
560                 k = KeyStore.getInstance("JKS", provider);
561             k.load(fin, null);
562             return k;
563         }
564         catch (Exception JavaDoc e) {
565             throw new ExceptionConverter(e);
566         }
567         finally {
568             try{if (fin != null) {fin.close();}}catch(Exception JavaDoc ex){}
569         }
570     }
571     
572     /**
573      * Verifies a single certificate.
574      * @param cert the certificate to verify
575      * @param crls the certificate revocation list or <CODE>null</CODE>
576      * @param calendar the date or <CODE>null</CODE> for the current date
577      * @return a <CODE>String</CODE> with the error description or <CODE>null</CODE>
578      * if no error
579      */

580     public static String JavaDoc verifyCertificate(X509Certificate JavaDoc cert, Collection JavaDoc crls, Calendar JavaDoc calendar) {
581         if (calendar == null)
582             calendar = new GregorianCalendar JavaDoc();
583         if (cert.hasUnsupportedCriticalExtension())
584             return "Has unsupported critical extension";
585         try {
586             cert.checkValidity(calendar.getTime());
587         }
588         catch (Exception JavaDoc e) {
589             return e.getMessage();
590         }
591         if (crls != null) {
592             for (Iterator JavaDoc it = crls.iterator(); it.hasNext();) {
593                 if (((CRL JavaDoc)it.next()).isRevoked(cert))
594                     return "Certificate revoked";
595             }
596         }
597         return null;
598     }
599     
600     /**
601      * Verifies a certificate chain against a KeyStore.
602      * @param certs the certificate chain
603      * @param keystore the <CODE>KeyStore</CODE>
604      * @param crls the certificate revocation list or <CODE>null</CODE>
605      * @param calendar the date or <CODE>null</CODE> for the current date
606      * @return <CODE>null</CODE> if the certificate chain could be validade or a
607      * <CODE>Object[]{cert,error}</CODE> where <CODE>cert</CODE> is the
608      * failed certificate and <CODE>error</CODE> is the error message
609      */

610     public static Object JavaDoc[] verifyCertificates(Certificate JavaDoc certs[], KeyStore JavaDoc keystore, Collection JavaDoc crls, Calendar JavaDoc calendar) {
611         if (calendar == null)
612             calendar = new GregorianCalendar JavaDoc();
613         for (int k = 0; k < certs.length; ++k) {
614             X509Certificate JavaDoc cert = (X509Certificate JavaDoc)certs[k];
615             String JavaDoc err = verifyCertificate(cert, crls, calendar);
616             if (err != null)
617                 return new Object JavaDoc[]{cert, err};
618             try {
619                 for (Enumeration JavaDoc aliases = keystore.aliases(); aliases.hasMoreElements();) {
620                     try {
621                         String JavaDoc alias = (String JavaDoc)aliases.nextElement();
622                         if (!keystore.isCertificateEntry(alias))
623                             continue;
624                         X509Certificate JavaDoc certStoreX509 = (X509Certificate JavaDoc)keystore.getCertificate(alias);
625                         if (verifyCertificate(certStoreX509, crls, calendar) != null)
626                             continue;
627                         try {
628                             cert.verify(certStoreX509.getPublicKey());
629                             return null;
630                         }
631                         catch (Exception JavaDoc e) {
632                             continue;
633                         }
634                     }
635                     catch (Exception JavaDoc ex) {
636                     }
637                 }
638             }
639             catch (Exception JavaDoc e) {
640             }
641             int j;
642             for (j = 0; j < certs.length; ++j) {
643                 if (j == k)
644                     continue;
645                 X509Certificate JavaDoc certNext = (X509Certificate JavaDoc)certs[j];
646                 try {
647                     cert.verify(certNext.getPublicKey());
648                     break;
649                 }
650                 catch (Exception JavaDoc e) {
651                 }
652             }
653             if (j == certs.length)
654                 return new Object JavaDoc[]{cert, "Cannot be verified against the KeyStore or the certificate chain"};
655         }
656         return new Object JavaDoc[]{null, "Invalid state. Possible circular certificate chain"};
657     }
658
659     /**
660      * Get the "issuer" from the TBSCertificate bytes that are passed in
661      * @param enc a TBSCertificate in a byte array
662      * @return a DERObject
663      */

664     private static DERObject getIssuer(byte[] enc) {
665         try {
666             ASN1InputStream in = new ASN1InputStream(new ByteArrayInputStream JavaDoc(enc));
667             ASN1Sequence seq = (ASN1Sequence)in.readObject();
668             return (DERObject)seq.getObjectAt(seq.getObjectAt(0) instanceof DERTaggedObject ? 3 : 2);
669         }
670         catch (IOException JavaDoc e) {
671             throw new ExceptionConverter(e);
672         }
673     }
674
675     /**
676      * Get the "subject" from the TBSCertificate bytes that are passed in
677      * @param enc A TBSCertificate in a byte array
678      * @return a DERObject
679      */

680     private static DERObject getSubject(byte[] enc) {
681         try {
682             ASN1InputStream in = new ASN1InputStream(new ByteArrayInputStream JavaDoc(enc));
683             ASN1Sequence seq = (ASN1Sequence)in.readObject();
684             return (DERObject)seq.getObjectAt(seq.getObjectAt(0) instanceof DERTaggedObject ? 5 : 4);
685         }
686         catch (IOException JavaDoc e) {
687             throw new ExceptionConverter(e);
688         }
689     }
690
691     /**
692      * Get the issuer fields from an X509 Certificate
693      * @param cert an X509Certificate
694      * @return an X509Name
695      */

696     public static X509Name getIssuerFields(X509Certificate JavaDoc cert) {
697         try {
698             return new X509Name((ASN1Sequence)getIssuer(cert.getTBSCertificate()));
699         }
700         catch (Exception JavaDoc e) {
701             throw new ExceptionConverter(e);
702         }
703     }
704
705     /**
706      * Get the subject fields from an X509 Certificate
707      * @param cert an X509Certificate
708      * @return an X509Name
709      */

710     public static X509Name getSubjectFields(X509Certificate JavaDoc cert) {
711         try {
712             return new X509Name((ASN1Sequence)getSubject(cert.getTBSCertificate()));
713         }
714         catch (