KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > osgi > internal > verifier > PKCS7Processor


1 /*******************************************************************************
2  * Copyright (c) 2006, 2007 IBM Corporation and others. All rights reserved.
3  * This program and the accompanying materials are made available under the
4  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
5  * and is available at http://www.eclipse.org/legal/epl-v10.html
6  *
7  * Contributors: IBM Corporation - initial API and implementation
8  ******************************************************************************/

9 package org.eclipse.osgi.internal.verifier;
10
11 import java.io.ByteArrayInputStream JavaDoc;
12 import java.io.IOException JavaDoc;
13 import java.math.BigInteger JavaDoc;
14 import java.security.*;
15 import java.security.cert.*;
16 import java.security.cert.Certificate JavaDoc;
17 import java.text.DateFormat JavaDoc;
18 import java.text.ParseException JavaDoc;
19 import java.text.SimpleDateFormat JavaDoc;
20 import java.util.*;
21
22 import javax.security.auth.x500.X500Principal JavaDoc;
23 import org.eclipse.osgi.framework.log.FrameworkLogEntry;
24 import org.eclipse.osgi.internal.provisional.verifier.CertificateChain;
25 import org.eclipse.osgi.internal.provisional.verifier.CertificateTrustAuthority;
26 import org.eclipse.osgi.util.NLS;
27
28 /**
29  * This class processes a PKCS7 file. See RFC 2315 for specifics.
30  */

31 public class PKCS7Processor implements CertificateChain, JarVerifierConstant {
32
33     private static CertificateFactory certFact;
34
35     static {
36         try {
37             certFact = CertificateFactory.getInstance("X.509"); //$NON-NLS-1$
38
} catch (CertificateException e) {
39             SignedBundleHook.log(e.getMessage(), FrameworkLogEntry.ERROR, e);
40         }
41     }
42
43     private String JavaDoc certChain;
44     private Certificate JavaDoc[] certificates;
45     private Certificate JavaDoc[] tsaCertificates;
46     private boolean trusted;
47
48     // key(object id) = value(structure)
49
private Map signedAttrs;
50
51     // key(object id) = value(structure)
52
private Map unsignedAttrs;
53
54     // store the signature of a signerinfo
55
private byte signature[];
56     private String JavaDoc digestAlgorithm;
57     private String JavaDoc signatureAlgorithm;
58
59     private Certificate JavaDoc signerCert;
60     private Date signingTime;
61
62     String JavaDoc oid2String(int oid[]) {
63         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
64         for (int i = 0; i < oid.length; i++) {
65             if (i > 0)
66                 sb.append('.');
67             sb.append(oid[i]);
68         }
69         return sb.toString();
70     }
71
72     String JavaDoc findEncryption(int encOid[]) throws NoSuchAlgorithmException {
73         if (Arrays.equals(DSA_OID, encOid)) {
74             return "DSA"; //$NON-NLS-1$
75
}
76         if (Arrays.equals(RSA_OID, encOid)) {
77             return "RSA"; //$NON-NLS-1$
78
}
79         throw new NoSuchAlgorithmException("No algorithm found for " + oid2String(encOid)); //$NON-NLS-1$
80
}
81
82     String JavaDoc findDigest(int digestOid[]) throws NoSuchAlgorithmException {
83         if (Arrays.equals(SHA1_OID, digestOid)) {
84             return SHA1_STR;
85         }
86         if (Arrays.equals(MD5_OID, digestOid)) {
87             return MD5_STR;
88         }
89         if (Arrays.equals(MD2_OID, digestOid)) {
90             return MD2_STR;
91         }
92         throw new NoSuchAlgorithmException("No algorithm found for " + oid2String(digestOid)); //$NON-NLS-1$
93
}
94
95     /*
96      * static void printBP(BERProcessor bp, int depth) {
97      * System.out.print(depth); for(int i = 0; i < depth; i++)
98      * System.out.print(" "); System.out.println(bp); }
99      *
100      * static void dumpSeq(BERProcessor bp, int depth) {
101      * while(!bp.endOfSequence()) { printBP(bp, depth); if (bp.constructed) {
102      * dumpSeq(bp.stepInto(), depth+1); } bp.stepOver(); } }
103      *
104      * void hexDump(byte buffer[], int off, int len) { for(int i = 0; i < len;
105      * i++) { System.out.print(Integer.toString(buffer[i]&0xff, 16) + " "); if
106      * (i % 16 == 15) System.out.println(); } System.out.println(); }
107      */

108
109     public PKCS7Processor(String JavaDoc certChain, boolean trusted, byte[][] certificates, long signingTime) throws CertificateException {
110         this.certChain = certChain;
111         this.trusted = trusted;
112         this.certificates = new Certificate JavaDoc[certificates.length];
113         for (int i = 0; i < certificates.length; i++)
114             this.certificates[i] = certFact.generateCertificate(new ByteArrayInputStream JavaDoc(certificates[i]));
115         if (signingTime > Long.MIN_VALUE)
116             this.signingTime = new Date(signingTime);
117     }
118
119     public PKCS7Processor(byte pkcs7[], int pkcs7Offset, int pkcs7Length) throws IOException JavaDoc, CertificateException, NoSuchAlgorithmException {
120
121         // First grab the certificates
122
List certs = null;
123
124         BERProcessor bp = new BERProcessor(pkcs7, pkcs7Offset, pkcs7Length);
125
126         // Just do a sanity check and make sure we are actually doing a PKCS7
127
// stream
128
// PKCS7: Step into the ContentType
129
bp = bp.stepInto();
130         if (!Arrays.equals(bp.getObjId(), SIGNEDDATA_OID)) {
131             throw new IOException JavaDoc("Not a valid PKCS#7 file"); //$NON-NLS-1$
132
}
133
134         // PKCS7: Process the SignedData structure
135
bp.stepOver(); // (**wrong comments**) skip over the oid
136
bp = bp.stepInto(); // go into the Signed data
137
bp = bp.stepInto(); // It is a structure;
138
bp.stepOver(); // Yeah, yeah version = 1
139
bp.stepOver(); // We'll see the digest stuff again; digestAlgorithms
140

141         // process the encapContentInfo structure
142
processEncapContentInfo(bp);
143
144         bp.stepOver();
145
146         // PKCS7: check if the class tag is 0
147
if (bp.classOfTag == BERProcessor.CONTEXTSPECIFIC_TAGCLASS && bp.tag == 0) {
148             // process the certificate elements inside the signeddata strcuture
149
certs = processCertificates(bp);
150         }
151
152         if (certs == null || certs.size() < 1)
153             throw new SecurityException JavaDoc("There are no certificates in the .RSA/.DSA file!"); //$NON-NLS-1$
154

155         // Okay, here are our certificates.
156
bp.stepOver();
157         if (bp.classOfTag == BERProcessor.UNIVERSAL_TAGCLASS && bp.tag == 1) {
158             bp.stepOver(); // Don't use the CRLs if present
159
}
160
161         processSignerInfos(bp, certs);
162
163         // construct the cert path
164
certs = constructCertPath(certs, signerCert);
165
166         // set the cert chain variable
167
int numCerts = certs.size();
168         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
169         for (int i = 0; i < numCerts; i++) {
170             X509Certificate x509Cert = ((X509Certificate) certs.get(i));
171             sb.append(x509Cert.getSubjectDN().getName());
172             sb.append("; "); //$NON-NLS-1$
173
}
174         certChain = sb.toString();
175
176         // initialize the certificates
177
certificates = (Certificate JavaDoc[]) certs.toArray(new Certificate JavaDoc[numCerts]);
178
179         // if this pkcs7process is tsa asn.1 block, the signingTime should already be set
180
if (null == signingTime)
181             signingTime = PKCS7DateParser.parseDate(this);
182     }
183
184     private void processEncapContentInfo(BERProcessor bp) throws IOException JavaDoc {
185         // check immediately if TSTInfo is there
186
BERProcessor encapContentBERS = bp.stepInto();
187         if (Arrays.equals(encapContentBERS.getObjId(), TIMESTAMP_TST_OID)) {
188
189             // eContent
190
encapContentBERS.stepOver();
191             BERProcessor encapContentBERS1 = encapContentBERS.stepInto();
192
193             // obtain eContent octet structure
194
byte bytesman[] = encapContentBERS1.getBytes();
195             BERProcessor eContentStructure = new BERProcessor(bytesman, 0, bytesman.length);
196
197             // pointing at 'version Integer' now
198
BERProcessor eContentBER = eContentStructure.stepInto();
199             int tsaVersion = eContentBER.getIntValue().intValue();
200
201             if (tsaVersion != 1) {
202                 throw new IOException JavaDoc("Not a version 1 time-stamp token"); //$NON-NLS-1$
203
}
204
205             // policty : TSAPolicyId
206
eContentBER.stepOver();
207
208             // messageImprint : MessageImprint
209
eContentBER.stepOver();
210
211             // serialNumber : INTEGER
212
eContentBER.stepOver();
213
214             // genTime : GeneralizedTime
215
eContentBER.stepOver();
216
217             // check time ends w/ 'Z'
218
String JavaDoc dateString = new String JavaDoc(eContentBER.getBytes());
219             if (!dateString.endsWith("Z")) { //$NON-NLS-1$
220
throw new IOException JavaDoc("Wrong dateformat used in time-stamp token"); //$NON-NLS-1$
221
}
222
223             // create the appropriate date time string format
224
// date format could be yyyyMMddHHmmss[.s...]Z or yyyyMMddHHmmssZ
225
int dotIndex = dateString.indexOf('.');
226             StringBuffer JavaDoc dateFormatSB = new StringBuffer JavaDoc("yyyyMMddHHmmss"); //$NON-NLS-1$
227
if (dotIndex != -1) {
228                 // yyyyMMddHHmmss[.s...]Z, find out number of s in the bracket
229
int noS = dateString.indexOf('Z') - 1 - dotIndex;
230                 dateFormatSB.append('.');
231
232                 // append s
233
for (int i = 0; i < noS; i++) {
234                     dateFormatSB.append('s');
235                 }
236             }
237             dateFormatSB.append("'Z'"); //$NON-NLS-1$
238

239             try {
240                 DateFormat JavaDoc dateFormt = new SimpleDateFormat JavaDoc(dateFormatSB.toString());
241                 dateFormt.setTimeZone(TimeZone.getTimeZone("GMT")); //$NON-NLS-1$
242
signingTime = dateFormt.parse(dateString);
243             } catch (ParseException JavaDoc e) {
244                 throw new IOException JavaDoc(JarVerifierMessages.PKCS7_Parse_Signing_Time_1);
245             }
246         }
247     }
248
249     private List constructCertPath(List certs, Certificate JavaDoc targetCert) {
250         List certsList = new ArrayList();
251         certsList.add(targetCert);
252
253         X509Certificate currentCert = (X509Certificate) targetCert;
254         int numIteration = certs.size();
255         int i = 0;
256         while (i < numIteration) {
257
258             X500Principal JavaDoc subject = currentCert.getSubjectX500Principal();
259             X500Principal JavaDoc issuer = currentCert.getIssuerX500Principal();
260
261             if (subject.equals(issuer)) {
262                 // the cert path has been constructed
263
break;
264             }
265
266             currentCert = null;
267             Iterator itr = certs.iterator();
268
269             while (itr.hasNext()) {
270                 X509Certificate tempCert = (X509Certificate) itr.next();
271
272                 if (tempCert.getSubjectX500Principal().equals(issuer)) {
273                     certsList.add(tempCert);
274                     currentCert = tempCert;
275                 }
276             }
277
278             i++;
279         }
280
281         return certsList;
282     }
283
284     public void validateCerts() throws CertificateExpiredException, CertificateNotYetValidException, InvalidKeyException, SignatureException {
285         if (certificates == null || certificates.length == 0) {
286             throw new SecurityException JavaDoc("There are no certificates in the signature block file!"); //$NON-NLS-1$
287
}
288
289         int len = certificates.length;
290
291         // check the certs validity and signatures
292
for (int i = 0; i < len; i++) {
293             X509Certificate currentX509Cert = (X509Certificate) certificates[i];
294
295             if (signingTime == null)
296                 currentX509Cert.checkValidity();
297             else
298                 currentX509Cert.checkValidity(signingTime);
299
300             try {
301                 if (i == len - 1) {
302                     if (currentX509Cert.getSubjectDN().equals(currentX509Cert.getIssuerDN()))
303                         currentX509Cert.verify(currentX509Cert.getPublicKey());
304                 } else {
305                     X509Certificate nextX509Cert = (X509Certificate) certificates[i + 1];
306                     currentX509Cert.verify(nextX509Cert.getPublicKey());
307                 }
308             } catch (NoSuchAlgorithmException e) {
309                 SignedBundleHook.log(e.getMessage(), FrameworkLogEntry.ERROR, e);
310                 throw new SecurityException JavaDoc(NLS.bind(JarVerifierMessages.No_Such_Algorithm_Excep, new String JavaDoc[] {e.getMessage()}));
311             } catch (NoSuchProviderException e) {
312                 SignedBundleHook.log(e.getMessage(), FrameworkLogEntry.ERROR, e);
313                 throw new SecurityException JavaDoc(NLS.bind(JarVerifierMessages.No_Such_Provider_Excep, new String JavaDoc[] {e.getMessage()}));
314             } catch (CertificateException e) {
315                 SignedBundleHook.log(e.getMessage(), FrameworkLogEntry.ERROR, e);
316                 throw new SecurityException JavaDoc(NLS.bind(JarVerifierMessages.Validate_Certs_Certificate_Exception, new String JavaDoc[] {e.getMessage()}));
317             }
318         }
319     }
320
321     private Certificate JavaDoc processSignerInfos(BERProcessor bp, List certs) throws CertificateException, NoSuchAlgorithmException {
322         // We assume there is only one SingerInfo element
323

324         // PKCS7: SignerINFOS processing
325
bp = bp.stepInto(); // Step into the set of signerinfos
326
bp = bp.stepInto(); // Step into the signerinfo sequence
327

328         // make sure the version is 1
329
BigInteger JavaDoc signerInfoVersion = bp.getIntValue();
330         if (signerInfoVersion.intValue() != 1) {
331             throw new CertificateException(JarVerifierMessages.PKCS7_SignerInfo_Version_Not_Supported);
332         }
333
334         // PKCS7: version CMSVersion
335
bp.stepOver(); // Skip the version
336

337         // PKCS7: sid [SignerIdentifier : issuerAndSerialNumber or subjectKeyIdentifer]
338
BERProcessor issuerAndSN = bp.stepInto();
339         X500Principal JavaDoc signerIssuer = new X500Principal JavaDoc(new ByteArrayInputStream JavaDoc(issuerAndSN.buffer, issuerAndSN.offset, issuerAndSN.endOffset - issuerAndSN.offset));
340         issuerAndSN.stepOver();
341         BigInteger JavaDoc sn = issuerAndSN.getIntValue();
342
343         // initilize the newSignerCert to the issuer cert of leaf cert
344
Certificate JavaDoc newSignerCert = null;
345
346         Iterator itr = certs.iterator();
347         // PKCS7: compuare the issuers in the issuerAndSN BER equals to the issuers in Certs generated at the beginning of this method
348
// it seems like there is no neeed, cause both ways use the same set of bytes
349
while (itr.hasNext()) {
350             X509Certificate cert = (X509Certificate) itr.next();
351             if (cert.getIssuerX500Principal().equals(signerIssuer) && cert.getSerialNumber().equals(sn)) {
352                 newSignerCert = cert;
353                 break;
354             }
355         }
356
357         if (newSignerCert == null)
358             throw new CertificateException("Signer certificate not in pkcs7block"); //$NON-NLS-1$
359

360         // set the signer cert
361
signerCert = newSignerCert;
362
363         // PKCS7: skip over the sid [SignerIdentifier : issuerAndSerialNumber or subjectKeyIdentifer]
364
bp.stepOver(); // skip the issuer name and serial number
365

366         // PKCS7: digestAlgorithm DigestAlgorithmIdentifier
367
BERProcessor digestAlg = bp.stepInto();
368         digestAlgorithm = findDigest(digestAlg.getObjId());
369
370         // PKCS7: check if the next one if context class for signedAttrs
371
bp.stepOver(); // skip the digest alg
372

373         // process the signed attributes if there is any
374
processSignedAttributes(bp);
375
376         // PKCS7: signatureAlgorithm for this SignerInfo
377
BERProcessor encryptionAlg = bp.stepInto();
378         signatureAlgorithm = findEncryption(encryptionAlg.getObjId());
379         bp.stepOver(); // skip the encryption alg
380

381         // PKCS7: signature
382
signature = bp.getBytes();
383
384         // PKCS7: Step into the unsignedAttrs,
385
bp.stepOver();
386
387         // process the unsigned attributes if there is any
388
processUnsignedAttributes(bp);
389
390         return newSignerCert;
391     }
392
393     private void processUnsignedAttributes(BERProcessor bp) {
394
395         if (bp.classOfTag == BERProcessor.CONTEXTSPECIFIC_TAGCLASS && bp.tag == 1) {
396
397             // there are some unsignedAttrs are found!!
398
unsignedAttrs = new HashMap();
399
400             // step into a set of unsigned attributes, I believe, when steps
401
// into here, the 'poiter' is pointing to the first element
402
BERProcessor unsignedAttrsBERS = bp.stepInto();
403             do {
404                 // process the unsignedAttrsBER by getting the attr type first,
405
// then the strcuture for the type
406
BERProcessor unsignedAttrBER = unsignedAttrsBERS.stepInto();
407
408                 // check if it is timestamp attribute type
409
int objID[] = unsignedAttrBER.getObjId();
410                 // if(Arrays.equals(TIMESTAMP_OID, objID)) {
411
// System.out.println("This is a timestamp type, to continue");
412
// }
413

414                 // get the structure for the attribute type
415
unsignedAttrBER.stepOver();
416                 byte structure[] = unsignedAttrBER.getBytes();
417                 unsignedAttrs.put(objID, structure);
418                 unsignedAttrsBERS.stepOver();
419             } while (!unsignedAttrsBERS.endOfSequence());
420         }
421     }
422
423     private void processSignedAttributes(BERProcessor bp) {
424         if (bp.classOfTag == BERProcessor.CONTEXTSPECIFIC_TAGCLASS) {
425
426             // process the signed attributes
427
signedAttrs = new HashMap();
428
429             BERProcessor signedAttrsBERS = bp.stepInto();
430             do {
431                 BERProcessor signedAttrBER = signedAttrsBERS.stepInto();
432                 int[] signedAttrObjID = signedAttrBER.getObjId();
433
434                 // step over to the attribute value
435
signedAttrBER.stepOver();
436
437                 byte[] signedAttrStructure = signedAttrBER.getBytes();
438
439                 signedAttrs.put(signedAttrObjID, signedAttrStructure);
440
441                 signedAttrsBERS.stepOver();
442             } while (!signedAttrsBERS.endOfSequence());
443             bp.stepOver();
444         }
445     }
446
447     /**
448      * Returns the Certificate of the signer of this PKCS7Block
449      */

450     public Certificate JavaDoc getSigner() {
451         if (certificates == null || certificates.length == 0)
452             return null;
453         return certificates[0];
454     }
455
456     public Certificate JavaDoc getRoot() {
457         if (certificates == null || certificates.length == 0)
458             return null;
459         return certificates[certificates.length - 1];
460     }
461
462     public Certificate JavaDoc[] getCertificates() {
463         return certificates;
464     }
465
466     /**
467      * Returns the list of X500 distinguished names that make up the signature chain. Each
468      * distinguished name is separated by a ';'.
469      */

470     public String JavaDoc getChain() {
471         return certChain;
472     }
473
474     /**
475      * Returns true if the signer certificate is trusted
476      * @return true if the signer certificate is trusted
477      */

478     public boolean isTrusted() {
479         return trusted;
480     }
481
482     public boolean equals(Object JavaDoc obj) {
483         if (!(obj instanceof CertificateChain))
484             return false;
485         if (certificates == null)
486             return false;
487         CertificateChain chain = (CertificateChain) obj;
488         if((signingTime == null ? chain.getSigningTime() != null : !signingTime.equals(chain.getSigningTime())))
489             return false;
490         if (trusted != chain.isTrusted() || (certChain == null ? chain.getChain() != null : !certChain.equals(chain.getChain())))
491             return false;
492         Certificate JavaDoc[] otherCerts = chain.getCertificates();
493         if (otherCerts == null || certificates.length != otherCerts.length)
494             return false;
495         for (int i = 0; i < certificates.length; i++)
496             if (!certificates[i].equals(otherCerts[i]))
497                 return false;
498         return true;
499     }
500
501     public void verifySFSignature(byte data[], int dataOffset, int dataLength) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
502         Signature sig = Signature.getInstance(digestAlgorithm + "with" + signatureAlgorithm); //$NON-NLS-1$
503
sig.initVerify(signerCert.getPublicKey());
504         sig.update(data, dataOffset, dataLength);
505         if (!sig.verify(signature)) {
506             throw new SignatureException(JarVerifierMessages.Signature_Not_Verify);
507         }
508     }
509
510     /**
511      * Return a map of signed attributes, the key(objid) = value(PKCSBlock in bytes for the key)
512      *
513      * @return map if there is any signed attributes, null otherwise
514      */

515     public Map getUnsignedAttrs() {
516         return unsignedAttrs;
517     }
518
519     /**
520      * Return a map of signed attributes, the key(objid) = value(PKCSBlock in bytes for the key)
521      *
522      * @return map if there is any signed attributes, null otherwise
523      */

524     public Map getSignedAttrs() {
525         return signedAttrs;
526     }
527
528     /**
529      *
530      * @param bp
531      * @return a List of certificates from target cert to root cert in order
532      *
533      * @throws CertificateException
534      */

535     private List processCertificates(BERProcessor bp) throws CertificateException {
536         List rtvList = new ArrayList(3);
537
538         // Step into the first certificate-element
539
BERProcessor certsBERS = bp.stepInto();
540
541         do {
542             X509Certificate x509Cert = (X509Certificate) certFact.generateCertificate(new ByteArrayInputStream JavaDoc(certsBERS.buffer, certsBERS.offset, certsBERS.endOffset - certsBERS.offset));
543
544             if (x509Cert != null) {
545                 rtvList.add(x509Cert);
546             }
547
548             // go to the next cert element
549
certsBERS.stepOver();
550         } while (!certsBERS.endOfSequence());
551
552         // Collections.reverse(rtvList);
553
return rtvList;
554     }
555
556     void determineTrust(CertificateTrustAuthority certsTrust) {
557         try {
558             certsTrust.checkTrust(certificates);
559             if (null != tsaCertificates) {
560                 certsTrust.checkTrust(tsaCertificates);
561             }
562             trusted = true;
563         } catch (CertificateException e) {
564             trusted = false;
565         }
566     }
567
568     public Date getSigningTime() {
569         return signingTime;
570     }
571
572     void setTSACertificates(Certificate JavaDoc[] tsaCertificates) {
573         this.tsaCertificates = tsaCertificates;
574     }
575
576     /*
577      public static void main(String[] args) throws InvalidKeyException, CertificateException, NoSuchAlgorithmException, SignatureException, KeyStoreException, IOException {
578      byte buffer[] = new byte[65536];
579      int len = System.in.read(buffer);
580      byte manifestBuff[] = new byte[65536];
581      int rc = new FileInputStream("man").read(manifestBuff);
582      PKCS7Processor p7 = new PKCS7Processor(buffer, 0, len, manifestBuff, 0, rc);
583      System.out.println(p7.getSignerCertificate());
584      System.out.println(p7.getCertificateChain());
585      }
586      */

587 }
Popular Tags