001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.midp.jadtool;
028:
029: /*
030: * Web proxy - The URL retrieval mechanism in AppDescriptor does not
031: * work through a proxy server at this time.
032: */
033:
034: import java.io.*;
035: import java.util.*;
036: import java.net.URL;
037: import java.net.MalformedURLException;
038:
039: import java.security.KeyStore;
040: import java.security.MessageDigest;
041: import java.security.DigestInputStream;
042: import java.security.NoSuchAlgorithmException;
043: import java.security.KeyStoreException;
044: import java.security.Key;
045: import java.security.PrivateKey;
046: import java.security.Signature;
047:
048: import java.security.SignatureException;
049: import java.security.InvalidKeyException;
050: import java.security.NoSuchProviderException;
051: import java.security.UnrecoverableKeyException;
052:
053: import java.security.cert.Certificate;
054: import java.security.cert.X509Certificate;
055: import java.security.cert.CertificateFactory;
056:
057: import java.security.cert.CertificateException;
058: import java.security.cert.CertificateEncodingException;
059: import java.security.cert.CertificateExpiredException;
060: import java.security.cert.CertificateNotYetValidException;
061:
062: import com.sun.midp.jadtool.AppDescriptorException;
063:
064: import com.sun.midp.installer.*;
065:
066: /**
067: * Java API for signing MIDletSuites.
068: * <p>
069: * AppDescriptor is an extension of the Properties class which provides
070: * additional methods for adding message digest and certificate
071: * properties as well as signing the app descriptor file and verifying
072: * a signed app descriptor file.
073: */
074: public class AppDescriptor extends JadProperties {
075:
076: /** Index of the key in arrays returned by getAllCerts. */
077: public static int KEY = 0;
078:
079: /** Index of the cert in arrays returned by getAllCerts. */
080: public static int CERT = 1;
081:
082: /** App Descriptor key for . */
083: public static final String JAR_SIGNATURE = "MIDlet-Jar-RSA-SHA1";
084: /** App Descriptor key for . */
085: public static final String CP_ATTR = "MIDlet-Certificate-";
086: /** App Descriptor key for . */
087: public static final String JAR_URL = "MIDlet-Jar-URL";
088: /** App Descriptor key for . */
089: public static final String JAR_SIZE = "MIDlet-Jar-Size";
090: /** App Descriptor key for . */
091: public static final String SEP_ATTR = ": ";
092:
093: /** SHA1 with RSA constant. */
094: public static final String SIGN_ALG = "SHA1withRSA";
095:
096: /** KeyStore to get certificates and keys from. */
097: private KeyStore keystore = null;
098:
099: /**
100: * Default constructor
101: */
102: public AppDescriptor() {
103: super ();
104: }
105:
106: /**
107: * Used to input a stored app descriptor into an AppDescriptor
108: * instance from a stream. The input stream will be converted
109: * to Unicode using <code>encoding</code> if it is specified.
110: * If <code>encoding</code> is not specified, a default
111: * encoding of type "UTF8" escapes will be used.
112: *
113: * Overrides <code>Properties.load</code>
114: *
115: * @param inputJad App descriptor input stream.
116: * @param encoding Encoding of the inputJad stream.
117: *
118: * @exception IOException If an error occurs while loading the
119: * inputJad stream.
120: * @exception UnsupportedEncodingException If the given encoding
121: * type is not supported.
122: * @exception InvalidJadException If the JAD has a format error
123: */
124: public synchronized void load(InputStream inputJad, String encoding)
125: throws UnsupportedEncodingException, IOException,
126: InvalidJadException {
127: super .load(inputJad, encoding);
128: }
129:
130: /**
131: * Used to store an app descriptor instance into a jad file
132: * through an output stream. The internal Unicode stream
133: * will be converted to an output format using
134: * <code>encoding</code> if it is specified.
135: * If <code>encoding</code> is not specified, a default
136: * encoding of type Ascii with Unicode escapes will be used.
137: *
138: * Overrides <code>Properties.store</code>
139: *
140: * @param outputJad App descriptor output stream.
141: * @param encoding Encoding of the outputJad stream.
142: *
143: * @exception IOException If an error occurs while writing the
144: * inputJad stream.
145: * @exception UnsupportedEncodingException If the given encoding
146: * type is not supported.
147: */
148: public synchronized void store(OutputStream outputJad,
149: String encoding) throws UnsupportedEncodingException,
150: IOException {
151:
152: if (encoding != null) {
153: JadWriter.write(this , outputJad, encoding);
154: } else {
155: JadWriter.write(this , outputJad);
156: }
157: }
158:
159: /**
160: * Provides a KeyStore instance for use by this AppDescriptor
161: * object. (The default KeyStore type from the Java security
162: * properties file is used. This should be type "JKS", and is
163: * the only supported keystore format.)
164: * <p>
165: * This KeyStore is required by the <code>addcert</code>,
166: * <code>sign</code>, <code>signcert</code>, and <code>verify</code>
167: * methods. If any of these methods is called before
168: * <code>loadKeyStore</code> an exception will be thrown.
169: *
170: * @param ksfile The input stream stream to load a KeyStore from.
171: * @param storepass The password to unlock the KeyStore, can be null.
172: * @exception KeyStoreException The default keystore provider type
173: * is not available in any of the provider packages
174: * searched.
175: * @exception IOException Thrown if there is a problem parsing
176: * the input stream or loading its data.
177: * @exception CertificateException Thrown if there is trouble loading
178: * certificates into the KeyStore
179: * @exception NoSuchAlgorithmException Thrown if the algorithm
180: * needed to verify the KeyStore cannot be found.
181: */
182: public synchronized void loadKeyStore(InputStream ksfile,
183: char[] storepass) throws KeyStoreException, IOException,
184: NoSuchAlgorithmException, CertificateException, Exception {
185: try {
186: keystore = KeyStore.getInstance(KeyStore.getDefaultType());
187: keystore.load(ksfile, storepass);
188: } catch (Exception e) {
189: throw new Exception("loadKeyStore failed");
190: }
191: }
192:
193: /**
194: * Store a the keystore in the descriptor in a file.
195: *
196: * @param ksfname file to use
197: * @param storepass password of keystore
198: */
199: public synchronized void storeKeyStore(String ksfname,
200: char[] storepass) throws IOException, KeyStoreException,
201: NoSuchAlgorithmException, CertificateException {
202:
203: FileOutputStream fout = new FileOutputStream(ksfname);
204: keystore.store(fout, storepass);
205: fout.close();
206: }
207:
208: /**
209: * Adds a Base64 encoded signature of the jar file at the URL
210: * specified by the MIDlet-Jar-URL key in the app descriptor.
211: * <code>load</code> and <code>loadKeyStore</code> must be
212: * call before this method.
213: * <p>
214: * The line in the app descriptor corresponding to this
215: * insertion will look like this:
216: * <p>
217: * MIDlet-Jar-RSA-SHA1:j3zKCv6eud2Ubkw80XjpNb7tk5s...
218: *
219: * If a MIDlet-Jar-RSA-SHA1 property already exists it will
220: * be replaced.
221: *
222: * @param alias Alias of the signing key in the keystore.
223: * @param keypass Password to access the signing (private) key.
224: *
225: * @exception AppDescriptorException JAR URL or content provider
226: * certificate was
227: * not found in the app descriptor.
228: * @exception MalformedURLException The URL corresponding to
229: * the MIDlet-Jar-URL key could not be parsed.
230: * @exception IOException error reading the JAR
231: * @exception NoSuchAlgorithmException If SHA1 or RSA need by
232: * getEncodedSig could not be found in an
233: * installed JCA provider.
234: * @exception KeyStoreException
235: * @exception InvalidKeyException
236: * @exception SignatureException
237: * @exception UnrecoverableKeyException
238: */
239: public void addJarSignature(String alias, char[] keypass)
240: throws AppDescriptorException, MalformedURLException,
241: IOException, KeyStoreException, InvalidKeyException,
242: SignatureException, NoSuchAlgorithmException,
243: UnrecoverableKeyException {
244:
245: String urlStr = getProperty(JAR_URL);
246:
247: if (urlStr == null) {
248: throw new AppDescriptorException(JAR_URL
249: + " not in descriptor");
250: }
251:
252: URL url = new URL(urlStr);
253: InputStream jarStream = url.openStream();
254: addJarSignature(alias, keypass, jarStream);
255: }
256:
257: /**
258: * Adds a Base64 encoded signature of the jar file provided in
259: * an input stream to the app descriptor.
260: * <p>
261: * The line in the app descriptor corresponding to this
262: * insertion will look like this:
263: *
264: * MIDlet-Jar-RSA-SHA1:j3zKCv6eud2Ubkw80XjpNb7tk5s...
265: *
266: * If a MIDlet-Jar-RSA-SHA1 property already exists it will
267: * be replaced.
268: *
269: * @param alias Alias of the signing key in the keystore.
270: * @param keypass Password to access the signing (private) key.
271: * @param jarStream stream to read the jar file from
272: *
273: * @exception IOException If there is a problem reading the
274: * input stream.
275: * @exception NoSuchAlgorithmException If SHA1 or RSA need by
276: * getEncodedSig could not be found in an
277: * installed JCA provider.
278: * @exception KeyStoreException
279: * @exception InvalidKeyException
280: * @exception SignatureException
281: * @exception UnrecoverableKeyException
282: */
283: public void addJarSignature(String alias, char[] keypass,
284: InputStream jarStream) throws IOException,
285: NoSuchAlgorithmException, KeyStoreException,
286: InvalidKeyException, SignatureException,
287: NoSuchAlgorithmException, UnrecoverableKeyException {
288:
289: setProperty(JAR_SIGNATURE, getEncodedSig(alias, keypass,
290: jarStream));
291: }
292:
293: /**
294: * Retrieves a certificate out of a KeyStore and adds it
295: * to the app descriptor as:
296: * <p>
297: * content_provider.certificate-1-1:<Base64 encoded newcert>
298: * <p>
299: * Instance variable <code>keystore</code> must not be null,
300: * and should have been set by <code>loadKeyStore</code>
301: * before this method is called.
302: *
303: * @param alias Alias of the chosen certificate in the keystore
304: * @param chainNum number of the chain to add certificate to
305: * @param certNum number of the certificate in the chain to replace it,
306: * or 0 to add the certificate at the end of the chain
307: *
308: * @exception KeyStoreException If there is an error with the keystore.
309: * @exception CertificateException If there is a problem with the
310: * encoding of the certificate.
311: * @exception AppDescriptorException If the KeyStore has not been
312: * initialized (keystore is null)
313: */
314: public void addCert(String alias, int chainNum, int certNum)
315: throws CertificateException, KeyStoreException,
316: AppDescriptorException {
317: Certificate[] chain = getCertificatesFromKeyStore(alias);
318: if (certNum == 0) {
319: // find next number after the highest existing certificate number
320: for (certNum = 1; getProperty(CP_ATTR + chainNum + "-"
321: + certNum) != null; certNum++)
322: ;
323:
324: for (int i = 0; i < chain.length; i++) {
325: addEncodedCertificate(chain[i], chainNum, certNum + i);
326: }
327: } else {
328: addEncodedCertificate(chain[certNum - 1], chainNum, certNum);
329: }
330: }
331:
332: /**
333: * Returns an X509Certificate object from the app descriptor property
334: * chosen by <code>certnum</code>, or
335: * null if that certificate does not exist in the descriptor.
336: * <p>
337: * After finding the chosen property in the app descriptor,
338: * decodes it from Base64 into a byte-encoded certificate and then
339: * creates the X509 format certificate from that opaque data.
340: *
341: * @param chainNum number of the certificate chain
342: * @param certNum number of the certificate in the chain
343: *
344: * @return an X509Certificate object or null if the certificate is
345: * not in the JAD
346: *
347: * @exception CertificateException If there is a format problem with
348: * the certificate
349: */
350: public X509Certificate getCert(int chainNum, int certNum)
351: throws CertificateException {
352:
353: X509Certificate c = null;
354:
355: String base64 = getProperty(CP_ATTR + chainNum + "-" + certNum);
356: if (base64 != null) {
357: c = base64CertToX509Cert(base64);
358: }
359:
360: return c;
361: }
362:
363: /**
364: * Returns an X509Certificate object from the app descriptor property
365: * chosen by <code>certnum</code>, or
366: * null if that certificate does not exist in the descriptor.
367: * <p>
368: * After finding the chosen property in the app descriptor,
369: * decodes it from Base64 into a byte-encoded certificate and then
370: * creates the X509 format certificate from that opaque data.
371: *
372: * @param chainNum number of the certificate chain
373: * @param certNum number of the certificate in the chain
374: *
375: * @return an X509Certificate object or null if the certificate is
376: * not in the JAD
377: *
378: * @exception CertificateException If there is a format problem with
379: * the certificate
380: */
381: public X509Certificate getCertAttribute(int chainNum, int certNum)
382: throws CertificateException {
383:
384: X509Certificate c = null;
385:
386: String base64 = getProperty(CP_ATTR + chainNum + "-" + certNum);
387: if (base64 != null) {
388: c = base64CertToX509Cert(base64);
389: }
390:
391: return c;
392: }
393:
394: /**
395: * Returns all X509Certificate objects from the app descriptor.
396: * <p>
397: * After finding a certificate property in the app descriptor,
398: * decodes it from Base64 into a byte-encoded certificate and then
399: * creates the X509 format certificate from that opaque data.
400: *
401: * @return Vector of object arrays, each containing key, and a
402: * X509Certificate object
403: */
404: public Vector getAllCerts() throws CertificateException {
405: Vector certs = new Vector();
406:
407: for (int idx = 0; idx < size(); idx++) {
408: String key = getKeyAt(idx);
409: String base64 = getValueAt(idx);
410:
411: if (key.startsWith(CP_ATTR)) {
412: X509Certificate c = base64CertToX509Cert(base64);
413: Object[] temp = new Object[2];
414:
415: temp[KEY] = key;
416: temp[CERT] = c;
417: certs.addElement(temp);
418: }
419: }
420:
421: return certs;
422: }
423:
424: /**
425: * Returns a message digest of a certificate in "human readable"
426: * from from the app descriptor property
427: * chosen by <code>certnum</code>, or
428: * null if that certificate does not exist in the descriptor.
429: * <p>
430: * After finding the chosen property in the app descriptor,
431: * decodes it from Base64 into a byte-encoded certificate and then
432: * creates a readable digest String based on that data.
433: *
434: * @param chainNum number of the certificate chain
435: * @param certNum number of the certificate in the chain
436: * @param alg A Digest algorithm to use, e.g. "SHA1" or "MD5".
437: *
438: * @return A message digest of a certificate in hex as a String or
439: * null if the certificate is not in the JAD.
440: *
441: * @exception NoSuchAlgorithmException Thrown if the digest
442: * <code>algorithm</code> could not be found.
443: */
444: public String getCertDigest(int chainNum, int certNum, String alg)
445: throws NoSuchAlgorithmException {
446:
447: String digest = null;
448:
449: String base64 = getProperty(CP_ATTR + chainNum + "-" + certNum);
450: if (base64 != null) {
451: byte[] certificateData = Base64.decode(base64);
452: digest = createFingerprint(certificateData, alg);
453: }
454:
455: return digest;
456: }
457:
458: /* ************ PRIVATE METHODS ************* */
459:
460: /**
461: * Retrieves a certificate chain out of a KeyStore.
462: * Instance variable <code>keystore</code> must not be null,
463: * and should have been set by <code>loadKeyStore</code>
464: * before this method is called.
465: *
466: * @param alias Alias of the chosen certificate in the keystore
467: * @return all certificates in the chain
468: * @exception KeyStoreException If there is an error with the keystore.
469: * @exception CertificateException If there is a problem with the
470: * encoding of the certificate.
471: * @exception AppDescriptorException If the KeyStore has not been
472: * initialized (keystore is null);
473: */
474: private Certificate[] getCertificatesFromKeyStore(String alias)
475: throws AppDescriptorException, CertificateException,
476: KeyStoreException {
477:
478: if (keystore == null) {
479: throw new AppDescriptorException(
480: AppDescriptorException.KEYSTORE_NOT_INITIALIZED);
481: }
482:
483: // Return a certifcate chain if we can get it.
484: Certificate[] chain = keystore.getCertificateChain(alias);
485: if (chain == null) {
486: // Otherwise try for a certificate.
487: Certificate cert = keystore.getCertificate(alias);
488: if (cert == null) {
489: throw new CertificateException("Certificate not found");
490: }
491: chain = new Certificate[] { cert };
492: }
493:
494: return chain;
495: }
496:
497: /**
498: * Add a certificate to the app descriptor as:
499: * <p>
500: * content_provider.certificate-1-1:<Base64 encoded newcert>
501: * <p>
502: * Instance variable <code>keystore</code> must not be null,
503: * and should have been set by <code>loadKeyStore</code>
504: * before this method is called.
505: *
506: * @param cert the certificate to add
507: * @param chainNum number of the chain to add certificate to
508: * @param certNum number of the certificate in the chain to replace it,
509: * or 0 to add the certificate at the end of the chain
510: *
511: * @exception CertificateEncodingException If there is a problem with the
512: * encoding of the certificate.
513: */
514: private void addEncodedCertificate(Certificate cert, int chainNum,
515: int certNum) throws CertificateEncodingException {
516:
517: // encode the (x.509?) cert in base64
518: byte[] certbytes = cert.getEncoded();
519: String certtoadd = Base64.encode(certbytes);
520: // replace any existing certificate
521: setProperty(CP_ATTR + chainNum + "-" + certNum, certtoadd);
522: }
523:
524: /**
525: * <code>getEncodedCertificate</code> - A helper function used
526: * by <code>addCert</code>.
527: *
528: * Retrieves a certificate out of a KeyStore and returns it
529: * as a Base64 encoded String. Instance variable <code>keystore</code>
530: * must not be null, and should have been set by <code>loadKeyStore</code>
531: * before this method is called.
532: *
533: * @param alias Alias of the chosen certificate in the keystore
534: * @return Base64 encoded certificate as a String
535: * @exception KeyStoreException If there is an error with the keystore.
536: * @exception CertificateException If there is a problem with the
537: * encoding of the certificate.
538: * @exception AppDescriptorException If the KeyStore has not been
539: * initialized (keystore is null);
540: */
541: private String getEncodedCertificate(String alias)
542: throws KeyStoreException, CertificateException,
543: AppDescriptorException {
544:
545: Certificate cert;
546:
547: if (keystore == null) {
548: throw new AppDescriptorException(
549: AppDescriptorException.KEYSTORE_NOT_INITIALIZED);
550: }
551:
552: // Load a keystore data structure to get keys from
553: cert = keystore.getCertificate(alias);
554: if (cert == null) {
555: throw new CertificateException("Certificate not found");
556: }
557:
558: byte[] certbytes = cert.getEncoded();
559:
560: // return the (x.509?) encoded cert in base64
561: return Base64.encode(certbytes);
562: }
563:
564: /**
565: * <code>getNextCertIndex</code> - A helper function used by
566: * <code>addcert</code>.
567: *
568: * Iterates through the current properties data returns the
569: * index of the first unused key index for either a content-provider
570: * (cp) or https certificate.
571: *
572: * @param key What cert key should be used...CP_ATTR or HTTPS_ATTR
573: * @return The first unused index for a particular certificate type.
574: */
575: private int getNextCertIndex(String key) {
576: int idx = 1;
577:
578: while (idx > 0) {
579: String value = getProperty(key + idx);
580: if (value == null) {
581: break;
582: }
583:
584: idx++;
585: }
586:
587: return idx;
588: }
589:
590: /**
591: * <code>createFingerprint</code> - A helper function used by
592: * <code>getdigest</code>.
593: *
594: * <code>createFingerprint</code>, given a certificated encoded
595: * as a byte array will compute a "fingerprint", or Message
596: * Digest of the certificate using the selected <code>algorithm</code>
597: * type.
598: *
599: * A fingerprint is meant to be human readable, and is thus
600: * returned as a hex string separated at byte boundaries by
601: * a delimiter ":".
602: *
603: * @param certificateBytes - a certificate encoded as a byte array
604: * @param algorithm - The name of a digest algorithm to use,
605: * e.g. "SHA1" or "MD5"
606: * @return the fingerprint in String form.
607: * @exception NoSuchAlgorithmException Thrown if the digest
608: * <code>algorithm</code> could not be found.
609: */
610: static String createFingerprint(byte[] certificateBytes,
611: String algorithm) throws NoSuchAlgorithmException {
612: MessageDigest md = MessageDigest.getInstance(algorithm);
613: md.update(certificateBytes);
614: byte[] digest = md.digest();
615: StringBuffer sb = new StringBuffer();
616:
617: for (int i = 0; i < digest.length; i++) {
618: int b = digest[i] & 0xff;
619: String hex = Integer.toHexString(b);
620:
621: if (i != 0) {
622: sb.append(":");
623: }
624:
625: if (hex.length() == 1) {
626: sb.append("0");
627: }
628:
629: sb.append(hex);
630: }
631:
632: return sb.toString();
633: }
634:
635: /**
636: * A helper function used by <code>sign</code>.
637: * Produces a base64 encoded signature for the given buffer.
638: *
639: * @param alias Alias of the signing key in the keystore.
640: * @param keypass Password to access the signing (private) key.
641: * @param stream stream to read the bytes from
642: *
643: * @return Base64 encoded signature of bits in the buffer
644: *
645: * @exception IOException If there is a problem reading the
646: * input stream.
647: * @exception KeyStoreException
648: * @exception InvalidKeyException
649: * @exception SignatureException
650: * @exception NoSuchAlgorithmException
651: * @exception UnrecoverableKeyException
652: */
653: private String getEncodedSig(String alias, char[] keypass,
654: InputStream stream) throws KeyStoreException,
655: InvalidKeyException, SignatureException,
656: NoSuchAlgorithmException, UnrecoverableKeyException,
657: IOException {
658: int bytesRead;
659: byte[] buffer = new byte[10240];
660:
661: // get a signature object
662: Signature signature = Signature.getInstance(SIGN_ALG);
663:
664: // init the signature with a private key for signing
665: Key pk = keystore.getKey(alias, keypass);
666: signature.initSign((PrivateKey) pk);
667:
668: for (;;) {
669: bytesRead = stream.read(buffer);
670: if (bytesRead == -1) {
671: break;
672: }
673:
674: signature.update(buffer, 0, bytesRead);
675: }
676:
677: // return the signature
678: byte[] raw = signature.sign();
679: return Base64.encode(raw);
680: }
681:
682: /**
683: * <code>getVerifyCert</code> - A helper function used by
684: * <code>verify</code>.
685: *
686: * Outputs an app descriptor file to the caller provided
687: * ByteArrayOutputStream <code>baos</code> and returns a
688: * base64 encoded signature for those bits. The signature
689: * is valid only for exactly the returned bits. If
690: * <code>encoding</code> is not specified, a default encoding
691: * type of Ascii with Unicode escapes is used.
692: *
693: * @param alias Alias of the verify key in the keystore.
694: * @return Verified X509Certificate containing the public key
695: * with which this app descriptor file's signature should
696: * be verified.
697: * @exception AppDescriptorException
698: * @exception NoSuchAlgorithmException
699: * @exception KeyStoreException
700: * @exception CertificateException
701: * @exception UnrecoverableKeyException
702: * @exception InvalidKeyException
703: * @exception NoSuchProviderException
704: * @exception SignatureException
705: */
706:
707: private X509Certificate getVerifyCert(String alias)
708: throws AppDescriptorException, NoSuchAlgorithmException,
709: KeyStoreException, CertificateException,
710: UnrecoverableKeyException, InvalidKeyException,
711: NoSuchProviderException, SignatureException {
712: X509Certificate returncert = null;
713: X509Certificate current = null;
714: X509Certificate operatorXcert = null;
715: Certificate operatorcert = null;
716: byte[] operatordata;
717: String operatordn = null;
718: String currentdn = null;
719: String rv = null;
720:
721: // get the operator verification cert from the keystore
722: operatorcert = keystore.getCertificate(alias);
723:
724: // convert opaque operator cert into X509 encoding so we
725: // can use it.
726: operatordata = operatorcert.getEncoded();
727: CertificateFactory cf = CertificateFactory.getInstance("X.509");
728: ByteArrayInputStream bais = new ByteArrayInputStream(
729: operatordata);
730: while (bais.available() > 0) {
731: operatorXcert = (X509Certificate) cf
732: .generateCertificate(bais);
733: }
734:
735: try {
736: bais.close();
737: } catch (IOException ioe) {
738: }
739:
740: // Get the operator's distinguished name
741: operatordn = (operatorXcert.getSubjectDN()).getName();
742:
743: /*
744: * Now we search for a CP_ATTR type certificate
745: * who's issuer DN matches "operatordn."
746: * It is the certificate that should verify the
747: * signature on this app descriptor.
748: *
749: * Before it can do that, it must be trusted by being
750: * verified by the "operator" certificate.
751: * The calls to verify() and checkValidity() do this.
752: */
753: int count = 1;
754: while ((current = getCert(1, count)) != null) {
755: currentdn = (current.getIssuerDN()).getName();
756: if (operatordn.equals(currentdn)) {
757: // verify cert sig with operator public key
758: current.verify(operatorcert.getPublicKey());
759: // check cert validity dates
760: current.checkValidity();
761: returncert = current;
762: break;
763: }
764:
765: count++;
766: }
767:
768: return returncert;
769: }
770:
771: /**
772: * <code>base64CertToX509Cert</code> - A helper function used by
773: * <code>verify</code>
774: *
775: * @param b64str A certificate encoded as Base64 String
776: * @return An X509Certificate object.
777: * @exception CertificateException Thrown if there is an error
778: * converting the <code>b64str</code> certificate
779: * into X509 format.
780: */
781: private X509Certificate base64CertToX509Cert(String b64str)
782: throws CertificateException {
783: X509Certificate c = null;
784:
785: byte[] certificateData = Base64.decode(b64str);
786:
787: CertificateFactory cf = CertificateFactory.getInstance("X.509");
788: ByteArrayInputStream bais = new ByteArrayInputStream(
789: certificateData);
790:
791: while (bais.available() > 0) {
792: c = (X509Certificate) cf.generateCertificate(bais);
793: }
794:
795: try {
796: bais.close();
797: } catch (IOException ioe) {
798: }
799:
800: return c;
801: }
802: }
|