/*
* @(#)SignCert.java 1.3 02/07/25 @(#)
*
* Copyright (c) 2000-2002 Sun Microsystems, Inc. All rights reserved.
* PROPRIETARY/CONFIDENTIAL
* Use is subject to license terms.
*/
package com.sun.midp.jadtool;
import java.io.*;
import java.util.Date;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.KeyStoreException;
import java.security.Key;
import java.security.PrivateKey;
import java.security.SignatureException;
import java.security.InvalidKeyException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateException;
import com.sun.midp.jadtool.AppDescriptorException;
// FIX: Use of these classes is not portable. (See below.)
// Perhaps they should be re-implemented at some point.
import sun.security.x509.AlgorithmId;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;
import sun.security.x509.X500Name;
import sun.security.x509.CertificateSubjectName;
import sun.security.x509.CertificateIssuerName;
import sun.security.x509.CertificateValidity;
import sun.security.x509.CertificateSerialNumber;
import sun.security.x509.CertificateAlgorithmId;
/**
* SignCert is a utility class used by the AppDescriptor Class to
* modify a self-signed certificate in a KeyStore. These methods
* do not modify an AppDescriptor at all, but only modify the contents
* of a Java KeyStore.
* <p>
*
* Given the alias of a self-signed certificate and the alias of
* a "signing" certificate...the alias of another certificate that
* is paired with a private key...the static method
* <code>SignACert</code> will replace the self-signed certificate
* with the same certificate signed by the owner of the "signing"
* certificate.
* <p>
*
* PORTABILITY WARNING!
* --------------------
* This class uses Sun implementation specific classes in the
* <code>sun.security.x509.*</code> namespace imported here. This
* is NOT portable to Java runtime environments provided by other
* vendors. It may not even be supported in future releases
* of the Sun JDK. This code works under Sun's JDK1.3.
* <p>
*
* We were forced to use Sun's X509 certificate implementation classes
* directly because the public X509 certificate methods in
* <code>java.security.cert.*</code> do not provide ways to
* programatically modify fields within a X509 certificate object.
* <p>
*
* For more information about the sun.* classes, see:<br>
* <a href=http://java.sun.com/products/jdk/faq/faq-sun-packages.html>
* http://java.sun.com/products/jdk/faq/faq-sun-packages.html</a>
*/
public class SignCert {
/**
* Signs a certificate <code>signee_alias</code> using the signing
* (private) key associated with <code>signing_alias</code>.
* <code>keyPass</code> unlocks the signing key.
* <p>
* Creates a signed certificate and stores it as a single-element
* certificate chain associated with <code>signee_alias</code>.
*/
public static void signACert(String signee_alias, String signing_alias,
char[] keyPass, KeyStore keyStore,
char[] storePass)
throws AppDescriptorException, CertificateException, IOException,
KeyStoreException, NoSuchAlgorithmException,
InvalidKeyException, UnrecoverableKeyException,
NoSuchProviderException, SignatureException, Exception {
String sigAlgName;
if (signee_alias == null || signing_alias == null ||
keyPass == null || keyStore == null) {
throw new AppDescriptorException("signACert got a null argument",
4);
}
Object[] objs = recoverPrivateKey(signing_alias, storePass,
keyPass, keyStore);
PrivateKey privKey = (PrivateKey)objs[0];
if (keyPass == null)
keyPass = (char[])objs[1];
// Determine the signature algorithm
// If no signature algorithm was specified at the command line,
// we choose one that is compatible with the selected private key
String keyAlgName = privKey.getAlgorithm();
if (keyAlgName.equalsIgnoreCase("DSA")
|| keyAlgName.equalsIgnoreCase("DSS")) {
sigAlgName = "SHA1WithDSA";
} else if (keyAlgName.equalsIgnoreCase("RSA")) {
sigAlgName = "SHA1WithRSA";
} else {
throw new
AppDescriptorException("Cannot derive signature algorithm", 5);
}
// Get the old certificate
Certificate oldCert = keyStore.getCertificate(signee_alias);
if (oldCert == null) {
throw new
AppDescriptorException(signee_alias + " has no public key", 4);
}
if (!(oldCert instanceof X509Certificate)) {
throw new AppDescriptorException(signee_alias +
" has no X.509 certificate", 6);
}
// Get the "signing" certificate
Certificate signingCert = keyStore.getCertificate(signing_alias);
if (signingCert == null) {
throw new
AppDescriptorException(signee_alias + " has no public key", 7);
}
if (!(signingCert instanceof X509Certificate)) {
throw new AppDescriptorException(signee_alias +
" has no X.509 certificate", 8);
}
// convert to X509CertImpl, so that we can modify selected fields
// (no public APIs available yet)
byte[] encoded = oldCert.getEncoded();
X509CertImpl certImpl = new X509CertImpl(encoded);
X509CertInfo certInfo = (X509CertInfo)certImpl.get(X509CertImpl.NAME +
"." + X509CertImpl.INFO);
// get an X509Certificate from the signing_alias
encoded = signingCert.getEncoded();
X509CertImpl signingCertImpl = new X509CertImpl(encoded);
X509CertInfo signingCertInfo = (X509CertInfo)
signingCertImpl.get(X509CertImpl.NAME
+ "." + X509CertImpl.INFO);
// Extend its validity
int validity = 180; // 180 days default
Date firstDate = new Date();
Date lastDate = new Date();
lastDate.setTime(firstDate.getTime() + validity*1000*24*60*60L);
CertificateValidity interval = new CertificateValidity(firstDate,
lastDate);
certInfo.set(X509CertInfo.VALIDITY, interval);
// Make new serial number
certInfo.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber
((int)(firstDate.getTime()/1000)));
// Set owner and issuer fields
X500Name owner;
// Get the owner name from the certificate
owner = (X500Name)certInfo.get(X509CertInfo.SUBJECT + "." +
CertificateSubjectName.DN_NAME);
// Get the issuear name - the owner of the signing certificate
X500Name issuer;
issuer = (X500Name)signingCertInfo.get(X509CertInfo.SUBJECT + "." +
CertificateSubjectName.DN_NAME);
certInfo.set(X509CertInfo.ISSUER + "." +
CertificateIssuerName.DN_NAME, issuer);
// The inner and outer signature algorithms have to match.
// The way we achieve that is really ugly, but there seems to be no
// other solution: We first sign the cert, then retrieve the
// outer sigalg and use it to set the inner sigalg
X509CertImpl newCert = new X509CertImpl(certInfo);
newCert.sign(privKey, sigAlgName);
AlgorithmId sigAlgid = (AlgorithmId)newCert.get(X509CertImpl.SIG_ALG);
certInfo.set(CertificateAlgorithmId.NAME + "." +
CertificateAlgorithmId.ALGORITHM, sigAlgid);
// Sign the new certificate
newCert = new X509CertImpl(certInfo);
newCert.sign(privKey, sigAlgName);
// Store the new certificate as a single-element certificate chain
keyStore.setKeyEntry(signee_alias, privKey,
(keyPass != null) ? keyPass : storePass,
new Certificate[] { newCert });
System.err.println("New certificate signed & inserted into KeyStore!");
System.err.print(newCert.toString());
System.err.println();
}
/**
* Recovers (private) key associated with given alias.
*
* @return an array of objects, where the 1st element in the array is the
* recovered private key, and the 2nd element is the password used to
* recover it.
*/
private static Object[] recoverPrivateKey(String alias, char[] storePass,
char[] keyPass, KeyStore keyStore)
throws KeyStoreException, NoSuchAlgorithmException,
UnrecoverableKeyException, Exception
{
Key key = null;
if (keyStore.containsAlias(alias) == false) {
throw new Exception("Alias <" + alias + "> does not exist");
}
if (keyStore.isKeyEntry(alias) == false) {
throw new Exception("Alias <" + alias + "> has no (private) key");
}
if (keyPass == null) {
// Try to recover the key using the keystore password
try {
key = keyStore.getKey(alias, storePass);
keyPass = storePass;
} catch (UnrecoverableKeyException e) {
throw new Exception("Invalid Key password entered");
// Did not work out, so prompt user for key password
// keyPass = getKeyPasswd(alias, null, null);
// key = keyStore.getKey(alias, keyPass);
}
} else {
key = keyStore.getKey(alias, keyPass);
}
if (!(key instanceof PrivateKey)) {
throw new Exception("Recovered key is not a private key");
}
return new Object[] {(PrivateKey)key, keyPass};
}
}
|