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: import java.io.*;
030: import java.util.Date;
031:
032: import java.security.KeyStore;
033: import java.security.NoSuchAlgorithmException;
034: import java.security.KeyStoreException;
035: import java.security.Key;
036: import java.security.PrivateKey;
037:
038: import java.security.SignatureException;
039: import java.security.InvalidKeyException;
040: import java.security.NoSuchProviderException;
041: import java.security.UnrecoverableKeyException;
042:
043: import java.security.cert.Certificate;
044: import java.security.cert.X509Certificate;
045: import java.security.cert.CertificateException;
046:
047: import com.sun.midp.jadtool.AppDescriptorException;
048:
049: // FIX: Use of these classes is not portable. (See below.)
050: // Perhaps they should be re-implemented at some point.
051: import sun.security.x509.AlgorithmId;
052: import sun.security.x509.X509CertImpl;
053: import sun.security.x509.X509CertInfo;
054: import sun.security.x509.X500Name;
055: import sun.security.x509.CertificateSubjectName;
056: import sun.security.x509.CertificateIssuerName;
057: import sun.security.x509.CertificateValidity;
058: import sun.security.x509.CertificateSerialNumber;
059: import sun.security.x509.CertificateAlgorithmId;
060:
061: /**
062: * SignCert is a utility class used by the AppDescriptor Class to
063: * modify a self-signed certificate in a KeyStore. These methods
064: * do not modify an AppDescriptor at all, but only modify the contents
065: * of a Java KeyStore.
066: * <p>
067: *
068: * Given the alias of a self-signed certificate and the alias of
069: * a "signing" certificate...the alias of another certificate that
070: * is paired with a private key...the static method
071: * <code>SignACert</code> will replace the self-signed certificate
072: * with the same certificate signed by the owner of the "signing"
073: * certificate.
074: * <p>
075: *
076: * PORTABILITY WARNING!
077: * --------------------
078: * This class uses Sun implementation specific classes in the
079: * <code>sun.security.x509.*</code> namespace imported here. This
080: * is NOT portable to Java runtime environments provided by other
081: * vendors. It may not even be supported in future releases
082: * of the Sun JDK. This code works under Sun's JDK1.3.
083: * <p>
084: *
085: * We were forced to use Sun's X509 certificate implementation classes
086: * directly because the public X509 certificate methods in
087: * <code>java.security.cert.*</code> do not provide ways to
088: * programmatically modify fields within a X509 certificate object.
089: * <p>
090: *
091: * For more information about the sun.* classes, see:<br>
092: * <a href=http://java.sun.com/products/jdk/faq/faq-sun-packages.html>
093: * http://java.sun.com/products/jdk/faq/faq-sun-packages.html</a>
094: */
095:
096: public class SignCert {
097:
098: /**
099: * Signs a certificate <code>signee_alias</code> using the signing
100: * (private) key associated with <code>signing_alias</code>.
101: * <code>keyPass</code> unlocks the signing key.
102: * <p>
103: * Creates a signed certificate and stores it as a single-element
104: * certificate chain associated with <code>signee_alias</code>.
105: */
106:
107: public static void signACert(String signee_alias,
108: String signing_alias, char[] keyPass, KeyStore keyStore,
109: char[] storePass) throws AppDescriptorException,
110: CertificateException, IOException, KeyStoreException,
111: NoSuchAlgorithmException, InvalidKeyException,
112: UnrecoverableKeyException, NoSuchProviderException,
113: SignatureException, Exception {
114:
115: String sigAlgName;
116:
117: if (signee_alias == null || signing_alias == null
118: || keyPass == null || keyStore == null) {
119: throw new AppDescriptorException(
120: "signACert got a null argument", 4);
121: }
122:
123: Object[] objs = recoverPrivateKey(signing_alias, storePass,
124: keyPass, keyStore);
125: PrivateKey privKey = (PrivateKey) objs[0];
126: if (keyPass == null)
127: keyPass = (char[]) objs[1];
128:
129: // Determine the signature algorithm
130: // If no signature algorithm was specified at the command line,
131: // we choose one that is compatible with the selected private key
132: String keyAlgName = privKey.getAlgorithm();
133: if (keyAlgName.equalsIgnoreCase("DSA")
134: || keyAlgName.equalsIgnoreCase("DSS")) {
135: sigAlgName = "SHA1WithDSA";
136: } else if (keyAlgName.equalsIgnoreCase("RSA")) {
137: sigAlgName = "SHA1WithRSA";
138: } else {
139: throw new AppDescriptorException(
140: "Cannot derive signature algorithm", 5);
141: }
142:
143: // Get the old certificate
144: Certificate oldCert = keyStore.getCertificate(signee_alias);
145: if (oldCert == null) {
146: throw new AppDescriptorException(signee_alias
147: + " has no public key", 4);
148: }
149:
150: if (!(oldCert instanceof X509Certificate)) {
151: throw new AppDescriptorException(signee_alias
152: + " has no X.509 certificate", 6);
153: }
154:
155: // Get the "signing" certificate
156: Certificate signingCert = keyStore
157: .getCertificate(signing_alias);
158: if (signingCert == null) {
159: throw new AppDescriptorException(signee_alias
160: + " has no public key", 7);
161: }
162:
163: if (!(signingCert instanceof X509Certificate)) {
164: throw new AppDescriptorException(signee_alias
165: + " has no X.509 certificate", 8);
166: }
167:
168: // convert to X509CertImpl, so that we can modify selected fields
169: // (no public APIs available yet)
170: byte[] encoded = oldCert.getEncoded();
171: X509CertImpl certImpl = new X509CertImpl(encoded);
172: X509CertInfo certInfo = (X509CertInfo) certImpl
173: .get(X509CertImpl.NAME + "." + X509CertImpl.INFO);
174:
175: // get an X509Certificate from the signing_alias
176: encoded = signingCert.getEncoded();
177: X509CertImpl signingCertImpl = new X509CertImpl(encoded);
178: X509CertInfo signingCertInfo = (X509CertInfo) signingCertImpl
179: .get(X509CertImpl.NAME + "." + X509CertImpl.INFO);
180:
181: // Extend its validity
182: int validity = 180; // 180 days default
183: Date firstDate = new Date();
184: Date lastDate = new Date();
185: lastDate.setTime(firstDate.getTime() + validity * 1000 * 24
186: * 60 * 60L);
187: CertificateValidity interval = new CertificateValidity(
188: firstDate, lastDate);
189: certInfo.set(X509CertInfo.VALIDITY, interval);
190:
191: // Make new serial number
192: certInfo.set(X509CertInfo.SERIAL_NUMBER,
193: new CertificateSerialNumber(
194: (int) (firstDate.getTime() / 1000)));
195:
196: // Set owner and issuer fields
197: X500Name owner;
198: // Get the owner name from the certificate
199: owner = (X500Name) certInfo.get(X509CertInfo.SUBJECT + "."
200: + CertificateSubjectName.DN_NAME);
201:
202: // Get the issuer name - the owner of the signing certificate
203: X500Name issuer;
204: issuer = (X500Name) signingCertInfo.get(X509CertInfo.SUBJECT
205: + "." + CertificateSubjectName.DN_NAME);
206:
207: certInfo.set(X509CertInfo.ISSUER + "."
208: + CertificateIssuerName.DN_NAME, issuer);
209:
210: // The inner and outer signature algorithms have to match.
211: // The way we achieve that is really ugly, but there seems to be no
212: // other solution: We first sign the cert, then retrieve the
213: // outer sigalg and use it to set the inner sigalg
214:
215: X509CertImpl newCert = new X509CertImpl(certInfo);
216: newCert.sign(privKey, sigAlgName);
217: AlgorithmId sigAlgid = (AlgorithmId) newCert
218: .get(X509CertImpl.SIG_ALG);
219: certInfo.set(CertificateAlgorithmId.NAME + "."
220: + CertificateAlgorithmId.ALGORITHM, sigAlgid);
221:
222: // Sign the new certificate
223: newCert = new X509CertImpl(certInfo);
224: newCert.sign(privKey, sigAlgName);
225:
226: // Store the new certificate as a single-element certificate chain
227: keyStore.setKeyEntry(signee_alias, privKey,
228: (keyPass != null) ? keyPass : storePass,
229: new Certificate[] { newCert });
230:
231: System.err
232: .println("New certificate signed & inserted into KeyStore!");
233: System.err.print(newCert.toString());
234: System.err.println();
235: }
236:
237: /**
238: * Recovers (private) key associated with given alias.
239: *
240: * @return an array of objects, where the 1st element in the array is the
241: * recovered private key, and the 2nd element is the password used to
242: * recover it.
243: */
244: private static Object[] recoverPrivateKey(String alias,
245: char[] storePass, char[] keyPass, KeyStore keyStore)
246: throws KeyStoreException, NoSuchAlgorithmException,
247: UnrecoverableKeyException, Exception {
248: Key key = null;
249:
250: if (keyStore.containsAlias(alias) == false) {
251: throw new Exception("Alias <" + alias + "> does not exist");
252: }
253: if (keyStore.isKeyEntry(alias) == false) {
254: throw new Exception("Alias <" + alias
255: + "> has no (private) key");
256: }
257:
258: if (keyPass == null) {
259: // Try to recover the key using the keystore password
260: try {
261: key = keyStore.getKey(alias, storePass);
262: keyPass = storePass;
263: } catch (UnrecoverableKeyException e) {
264: throw new Exception("Invalid Key password entered");
265: // Did not work out, so prompt user for key password
266: // keyPass = getKeyPasswd(alias, null, null);
267: // key = keyStore.getKey(alias, keyPass);
268: }
269: } else {
270: key = keyStore.getKey(alias, keyPass);
271: }
272: if (!(key instanceof PrivateKey)) {
273: throw new Exception("Recovered key is not a private key");
274: }
275: return new Object[] { (PrivateKey) key, keyPass };
276: }
277: }
|