FYI: the complete sectool
9999999: SecTool
Reviewed-by: nobody
diff --git a/src/share/classes/sun/security/tools/KeyTool.java b/src/share/classes/sun/security/tools/KeyTool.java
--- a/src/share/classes/sun/security/tools/KeyTool.java
+++ b/src/share/classes/sun/security/tools/KeyTool.java
@@ -47,9 +47,14 @@
import java.text.MessageFormat;
import java.util.*;
import java.lang.reflect.Constructor;
+import java.math.BigInteger;
import java.net.URL;
import java.net.URLClassLoader;
+import java.security.Provider.Service;
+import java.security.SecureRandom;
+import java.security.cert.CRL;
+import java.security.cert.X509CRLEntry;
import sun.misc.BASE64Encoder;
import sun.security.util.ObjectIdentifier;
import sun.security.pkcs.PKCS10;
@@ -69,6 +74,10 @@
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
+import javax.security.auth.x500.X500Principal;
+import sun.misc.BASE64Decoder;
+import sun.security.provider.DSAPublicKeyImpl;
+import sun.security.rsa.RSAPublicKeyImpl;
import sun.security.x509.*;
import static java.security.KeyStore.*;
@@ -85,7 +94,6 @@
*
* @since 1.2
*/
-
public final class KeyTool {
private boolean debug = false;
@@ -140,6 +148,14 @@
private Set<char[]> passwords = new HashSet<char[]> ();
private String startDate = null;
+ private String passphrase = null;
+ private BigInteger serial = BigInteger.ONE.negate(); // used in genKeypair/genCert/selfCert
+ private List <String> v3ext = null;
+ private List <String> ids = null; // used in genCrl
+ private String extWarning = "";
+
+ private boolean rethrow = false;
+
private static final int CERTREQ = 1;
private static final int CHANGEALIAS = 2;
private static final int DELETE = 3;
@@ -157,6 +173,18 @@
private static final int SELFCERT = 14;
private static final int STOREPASSWD = 15;
+ private static final int EXPORTPRIVATE = 101;
+ private static final int LISTALG = 102;
+ private static final int PUTPASSPHRASE = 103;
+ private static final int GETPASSPHRASE = 104;
+ private static final int GENCERT = 105;
+ private static final int PRINTCERTREQ = 106;
+ private static final int BASE64DEC = 107;
+ private static final int BASE64ENC = 108;
+ private static final int RAND = 109;
+ private static final int GENCRL = 110;
+ private static final int PRINTCRL = 111;
+
private static final Class[] PARAM_STRING = { String.class };
private static final String JKS = "jks";
@@ -181,6 +209,43 @@
kt.run(args, System.out);
}
+ private void printChainInfo(PrintStream out, Certificate[] certificateChain)
+ throws Exception {
+ X509Certificate x = null;
+ X500Principal signer = null;
+
+ boolean self = false;
+ for (Certificate cert: certificateChain) {
+ x = (X509Certificate)cert;
+ X500Principal me = x.getSubjectX500Principal();
+
+ if (signer != null) {
+ if(signer.equals(me)) {
+ out.print(" - ");
+ } else {
+ out.print(" x ");
+ }
+ }
+
+ String a = keyStore.getCertificateAlias(x);
+ if (a == null) {
+ a = "\"" + x.getSubjectX500Principal().toString() + "\"";
+ }
+ out.print(a);
+ signer = x.getIssuerX500Principal();
+ if (me.equals(signer)) {
+ out.print("(self)");
+ self = true;
+ } else {
+ self = false;
+ }
+ }
+ if (!self && signer != null) {
+ out.print(" - \"" + signer.toString() + "\"");
+ }
+ out.println();
+ }
+
private void run(String[] args, PrintStream out) throws Exception {
try {
parseArgs(args);
@@ -190,7 +255,7 @@
if (verbose) {
e.printStackTrace(System.out);
}
- if (!debug) {
+ if (!debug && !rethrow) {
System.exit(1);
} else {
throw e;
@@ -260,6 +325,30 @@
command = IMPORTKEYSTORE;
} else if (collator.compare(flags, "-genseckey") == 0) {
command = GENSECKEY;
+ } else if (collator.compare(flags, "-exportprivate") == 0) {
+ command = EXPORTPRIVATE;
+ } else if (collator.compare(flags, "-listalg") == 0) {
+ command = LISTALG;
+ } else if (collator.compare(flags, "-putpassphrase") == 0) {
+ command = PUTPASSPHRASE;
+ if (++i == args.length) errorNeedArgument(flags);
+ passphrase = args[i];
+ } else if (collator.compare(flags, "-getpassphrase") == 0) {
+ command = GETPASSPHRASE;
+ } else if (collator.compare(flags, "-gencert") == 0) {
+ command = GENCERT;
+ } else if (collator.compare(flags, "-base64dec") == 0) {
+ command = BASE64DEC;
+ } else if (collator.compare(flags, "-base64enc") == 0) {
+ command = BASE64ENC;
+ } else if (collator.compare(flags, "-printcertreq") == 0) {
+ command = PRINTCERTREQ;
+ } else if (collator.compare(flags, "-rand") == 0) {
+ command = RAND;
+ } else if (collator.compare(flags, "-gencrl") == 0) {
+ command = GENCRL;
+ } else if (collator.compare(flags, "-printcrl") == 0) {
+ command = PRINTCRL;
}
/*
@@ -337,6 +426,17 @@
} else if (collator.compare(flags, "-validity") == 0) {
if (++i == args.length) errorNeedArgument(flags);
validity = Long.parseLong(args[i]);
+ } else if (collator.compare(flags, "-serial") == 0) {
+ if (++i == args.length) errorNeedArgument(flags);
+ serial = new BigInteger(args[i]);
+ } else if (collator.compare(flags, "-v3ext") == 0) {
+ if (++i == args.length) errorNeedArgument(flags);
+ if (v3ext == null) v3ext = new ArrayList <String> ();
+ v3ext.add(args[i]);
+ } else if (collator.compare(flags, "-id") == 0) {
+ if (++i == args.length) errorNeedArgument(flags);
+ if (ids == null) ids = new ArrayList <String> ();
+ ids.add(args[i]);
} else if (collator.compare(flags, "-file") == 0) {
if (++i == args.length) errorNeedArgument(flags);
filename = args[i];
@@ -378,6 +478,8 @@
rfc = true;
} else if (collator.compare(flags, "-noprompt") == 0) {
noprompt = true;
+ } else if (collator.compare(flags, "-rethrow") == 0) {
+ rethrow = true;
} else if (collator.compare(flags, "-trustcacerts") == 0) {
trustcacerts = true;
} else if (collator.compare(flags, "-protected") == 0 ||
@@ -404,6 +506,12 @@
}
}
+ boolean isKeyStoreRelated(int cmd) {
+ return cmd != PRINTCERT && cmd != RAND && cmd != LISTALG
+ && cmd != BASE64DEC && cmd != BASE64ENC && cmd != PRINTCERTREQ
+ && cmd != PRINTCRL;
+ }
+
/**
* Execute the commands.
*/
@@ -568,7 +676,7 @@
// the default, which is located in $HOME/.keystore.
// If the command is "genkey", "identitydb", "import", or "printcert",
// it is OK not to have a keystore.
- if (command != PRINTCERT) {
+ if (isKeyStoreRelated(command)) {
if (ksfname == null) {
ksfname = System.getProperty("user.home") + File.separator
+ ".keystore";
@@ -587,6 +695,7 @@
if (command != GENKEYPAIR &&
command != GENSECKEY &&
command != IDENTITYDB &&
+ command != PUTPASSPHRASE &&
command != IMPORTCERT &&
command != IMPORTKEYSTORE) {
throw new Exception(rb.getString
@@ -721,7 +830,7 @@
}
} else if (!protectedPath
&& !KeyStoreUtil.isWindowsKeyStore(storetype)
- && !(command == PRINTCERT)) {
+ && isKeyStoreRelated(command)) {
// here we have EXPORTCERT and LIST (info valid until STOREPASSWD)
System.err.print(rb.getString("Enter keystore password: "));
System.err.flush();
@@ -763,7 +872,7 @@
// Create a certificate factory
if (command == PRINTCERT || command == IMPORTCERT
- || command == IDENTITYDB) {
+ || command == IDENTITYDB || command == PRINTCRL) {
cf = CertificateFactory.getInstance("X509");
}
@@ -930,6 +1039,71 @@
storePassNew = getNewPasswd("keystore password", storePass);
}
kssave = true;
+ } else if (command == LISTALG) {
+ doListAlg();
+ } else if (command == EXPORTPRIVATE) {
+ if (filename != null) {
+ PrintStream ps = new PrintStream(new FileOutputStream
+ (filename));
+ out = ps;
+ }
+ doExportPrivate(alias, out);
+ } else if (command == PUTPASSPHRASE) {
+ doPutPassphrase(alias, passphrase);
+ kssave = true;
+ } else if (command == GETPASSPHRASE) {
+ doGetPassphrase(alias, out);
+ } else if (command == GENCERT) {
+ InputStream inStream = System.in;
+ if(alias == null) {
+ alias = keyAlias;
+ }
+ if (filename != null) {
+ inStream = new FileInputStream(filename);
+ }
+ doGenCert(alias, sigAlgName, inStream, out);
+ } else if (command == PRINTCERTREQ) {
+ InputStream inStream = System.in;
+ if (filename != null) {
+ inStream = new FileInputStream(filename);
+ }
+ doPrintCertReq(inStream, out);
+ } else if (command == BASE64DEC) {
+ InputStream inStream = System.in;
+ if (filename != null) {
+ inStream = new FileInputStream(filename);
+ }
+ doBase64(false, inStream, out);
+ } else if (command == BASE64ENC) {
+ InputStream inStream = System.in;
+ if (filename != null) {
+ inStream = new FileInputStream(filename);
+ }
+ doBase64(true, inStream, out);
+ } else if (command == RAND) {
+ if (filename != null) {
+ PrintStream ps = new PrintStream(new FileOutputStream
+ (filename));
+ out = ps;
+ }
+ if(keyAlgName == null) keyAlgName = "SHA1PRNG";
+ doRand(out, keyAlgName, keysize);
+ } else if (command == GENCRL) {
+ if(alias == null) {
+ alias = keyAlias;
+ }
+ if (filename != null) {
+ PrintStream ps = new PrintStream(new FileOutputStream
+ (filename));
+ out = ps;
+ }
+ doGenCRL(out);
+ } else if (command == PRINTCRL) {
+ InputStream inStream = System.in;
+ if (filename != null) {
+ inStream = new FileInputStream(filename);
+ }
+ doPrintCRL(inStream, out);
}
// If we need to save the keystore, do so.
@@ -958,6 +1132,297 @@
}
}
}
+ }
+ }
+
+ void doListAlg() {
+ Provider[] providers = Security.getProviders();
+ for (Provider provider: providers) {
+ String providerName = provider.getName();
+ for (Service s : provider.getServices()) {
+ System.out.println(s.toString());
+ }
+ }
+ }
+
+ private void doGenCRL(PrintStream out)
+ throws Exception {
+ if (ids == null) {
+ throw new Exception("Must provide -id when -gencrl");
+ }
+ Certificate signerCert = keyStore.getCertificate(alias);
+ byte[] encoded = signerCert.getEncoded();
+ X509CertImpl signerCertImpl = new X509CertImpl(encoded);
+ X509CertInfo signerCertInfo = (X509CertInfo)signerCertImpl.get(
+ X509CertImpl.NAME + "." + X509CertImpl.INFO);
+ X500Name owner = (X500Name)signerCertInfo.get(X509CertInfo.SUBJECT + "." +
+ CertificateSubjectName.DN_NAME);
+
+ Date firstDate = getStartDate(startDate);
+ Date lastDate = (Date) firstDate.clone();
+ lastDate.setTime(lastDate.getTime() + (long)validity*1000*24*60*60);
+ CertificateValidity interval = new CertificateValidity(firstDate,
+ lastDate);
+
+
+ PrivateKey privateKey = (PrivateKey)recoverKey(alias, storePass, keyPass)[0];
+ // Construct an X500Signer object, so that we can sign the request
+ if (sigAlgName == null) {
+ // If no signature algorithm was specified at the command line,
+ // we choose one that is compatible with the selected private key
+ String keyAlgName = privateKey.getAlgorithm();
+ if (keyAlgName.equalsIgnoreCase("DSA")
+ || keyAlgName.equalsIgnoreCase("DSS")) {
+ sigAlgName = "SHA1WithDSA";
+ } else if (keyAlgName.equalsIgnoreCase("RSA")) {
+ sigAlgName = "MD5WithRSA";
+ } else {
+ throw new Exception(rb.getString
+ ("Cannot derive signature algorithm"));
+ }
+ }
+
+ X509CRLEntry[] badCerts = new X509CRLEntry[ids.size()];
+ for (int i=0; i<ids.size(); i++) {
+ String id = ids.get(i);
+ int d = id.indexOf(':');
+ if (d >= 0) {
+ CRLExtensions ext = new CRLExtensions();
+ ext.set("Reason", new CRLReasonCodeExtension(Integer.parseInt(id.substring(d+1))));
+ badCerts[i] = new X509CRLEntryImpl(new BigInteger(id.substring(0, d)),
+ firstDate, ext);
+ } else {
+ badCerts[i] = new X509CRLEntryImpl(new BigInteger(ids.get(i)), firstDate);
+ }
+ }
+ X509CRLImpl crl = new X509CRLImpl(owner, firstDate, lastDate, badCerts);
+ crl.sign(privateKey, sigAlgName);
+ if (rfc) {
+ out.println("-----BEGIN X509 CRL-----");
+ new BASE64Encoder().encodeBuffer(crl.getEncodedInternal(), out);
+ out.println("-----END X509 CRL-----");
+ } else {
+ out.write(crl.getEncodedInternal());
+ }
+ }
+
+ private void doRand(PrintStream out, String alg, int sz)
+ throws Exception {
+ if (sz == -1) sz = 8;
+ SecureRandom rand = providerName==null?
+ SecureRandom.getInstance(alg) :
+ SecureRandom.getInstance(alg, providerName);
+
+ byte[] b = new byte[sz/8];
+ rand.nextBytes(b);
+
+ if (verbose) {
+ new BASE64Encoder().encodeBuffer(b, out);
+ } else {
+ out.write(b);
+ }
+ }
+
+ private void doBase64(boolean enc, InputStream in, PrintStream out)
+ throws Exception {
+ if (enc) {
+ new BASE64Encoder().encodeBuffer(in, out);
+ } else {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+ StringBuffer sb = new StringBuffer();
+ anotherLine: while (true) {
+ String s = reader.readLine();
+ if (s == null) break;
+ s = s.trim();
+ for (char c: s.toCharArray()) {
+ if (c>='0' && c<='9' || c>='a' && c<='z' || c>='A' && c<='Z' || c=='+' || c=='/' || c=='=') {
+ // this is correct
+ } else {
+ // not a BASE64 encoded line, may be something like
+ // -----BEGIN SILLY BLOCK
+ continue anotherLine;
+ }
+ }
+ sb.append(s);
+ }
+ out.write(new BASE64Decoder().decodeBuffer(new String(sb)));
+ }
+ }
+ /**
+ * Generate a certificate: Read PKCS10 request from in, and print
+ * certificate to out. Use alias as CA, sigAlgName as the signature
+ * type.
+ */
+ private void doGenCert(String alias, String sigAlgName, InputStream in, PrintStream out)
+ throws Exception {
+
+
+ Certificate signerCert = keyStore.getCertificate(alias);
+ byte[] encoded = signerCert.getEncoded();
+ X509CertImpl signerCertImpl = new X509CertImpl(encoded);
+ X509CertInfo signerCertInfo = (X509CertInfo)signerCertImpl.get(
+ X509CertImpl.NAME + "." + X509CertImpl.INFO);
+ X500Name owner = (X500Name)signerCertInfo.get(X509CertInfo.SUBJECT + "." +
+ CertificateSubjectName.DN_NAME);
+
+ Date firstDate = getStartDate(startDate);
+ Date lastDate = (Date) firstDate.clone();
+ lastDate.setTime(lastDate.getTime() + (long)validity*1000*24*60*60);
+ CertificateValidity interval = new CertificateValidity(firstDate,
+ lastDate);
+
+
+ PrivateKey privateKey = (PrivateKey)recoverKey(alias, storePass, keyPass)[0];
+ // Construct an X500Signer object, so that we can sign the request
+ if (sigAlgName == null) {
+ // If no signature algorithm was specified at the command line,
+ // we choose one that is compatible with the selected private key
+ String keyAlgName = privateKey.getAlgorithm();
+ if (keyAlgName.equalsIgnoreCase("DSA")
+ || keyAlgName.equalsIgnoreCase("DSS")) {
+ sigAlgName = "SHA1WithDSA";
+ } else if (keyAlgName.equalsIgnoreCase("RSA")) {
+ sigAlgName = "MD5WithRSA";
+ } else {
+ throw new Exception(rb.getString
+ ("Cannot derive signature algorithm"));
+ }
+ }
+ Signature signature = Signature.getInstance(sigAlgName);
+ signature.initSign(privateKey);
+
+ X500Signer signer = new X500Signer(signature, owner);
+
+ X509CertInfo info = new X509CertInfo();
+ info.set(X509CertInfo.VALIDITY, interval);
+ if (serial.negate().equals(BigInteger.ONE)) {
+ info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber
+ ((int)(firstDate.getTime()/1000)));
+ } else {
+ info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(serial));
+ }
+ info.set(X509CertInfo.VERSION,
+ new CertificateVersion(CertificateVersion.V3));
+ info.set(X509CertInfo.ALGORITHM_ID,
+ new CertificateAlgorithmId(signer.getAlgorithmId()));
+ info.set(X509CertInfo.ISSUER,
+ new CertificateIssuerName(signer.getSigner()));
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+ boolean canRead = false;
+ StringBuffer sb = new StringBuffer();
+ while (true) {
+ String s = reader.readLine();
+ if (s == null) break;
+ // OpenSSL does not use NEW
+ //if (s.startsWith("-----BEGIN NEW CERTIFICATE REQUEST-----")) {
+ if (s.startsWith("-----BEGIN")) {
+ canRead = true;
+ //} else if (s.startsWith("-----END NEW CERTIFICATE REQUEST-----")) {
+ } else if (s.startsWith("-----END")) {
+ break;
+ } else if (canRead) {
+ sb.append(s);
+ }
+ }
+ byte[] rawReq = new BASE64Decoder().decodeBuffer(new String(sb));
+ PKCS10 req = new PKCS10(rawReq);
+
+ info.set(X509CertInfo.KEY, new CertificateX509Key(req.getSubjectPublicKeyInfo()));
+ info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(req.getSubjectName()));
+ if (v3ext != null) {
+ CertificateExtensions ext = createV3Extensions(v3ext, req.getSubjectPublicKeyInfo());
+ info.set(X509CertInfo.EXTENSIONS, ext);
+ if(!extWarning.equals("") && !noprompt) {
+ String reply = getYesNoReply
+ (extWarning + "Do you still want to apply the extensions? [no]: ");
+ if(!reply.equals("YES")) {
+ throw new Exception("Generation aborted by user");
+ }
+ }
+ }
+
+
+ X509CertImpl cert = new X509CertImpl(info);
+ cert.sign(privateKey, sigAlgName);
+ dumpCert(cert, out);
+ }
+
+ /**
+ * Put a passphrase into the keystore.
+ */
+ private void doPutPassphrase(String alias, String passphrase)
+ throws Exception
+ {
+ if (storePass == null) {
+ printWarning();
+ }
+ if (alias == null) {
+ alias = keyAlias;
+ }
+ if (keyStore.containsAlias(alias) == true) {
+ MessageFormat form = new MessageFormat(rb.getString
+ ("Secret key not generated, alias <alias> already exists"));
+ Object[] source = {alias};
+ throw new Exception(form.format(source));
+ }
+
+ if (!token && keyPass == null) {
+ // Prompt for key password
+ int count;
+ for (count = 0; (count < 3) && (keyPass == null); count++) {
+ MessageFormat form = new MessageFormat(rb.getString
+ ("Enter key password for <alias>"));
+ Object[] source = {alias};
+ System.err.println(form.format(source));
+ System.err.print(rb.getString
+ ("\t(RETURN if same as keystore password): "));
+ System.err.flush();
+ keyPass = Password.readPassword(System.in);
+ passwords.add(keyPass);
+ if (keyPass == null) {
+ keyPass = storePass;
+ } else if (keyPass.length < 6) {
+ System.err.println(rb.getString
+ ("Key password is too short - must be at least 6 characters"));
+ keyPass = null;
+ }
+ }
+ if (count == 3) {
+ throw new Exception(rb.getString
+ ("Too many failures - key not added to keystore"));
+ }
+ }
+ keyStore.setKeyEntry(alias, new PassphraseSecretKey(passphrase), keyPass, null);
+ }
+
+ private void doGetPassphrase(String alias, PrintStream out) throws Exception {
+ if (alias == null) {
+ alias = keyAlias;
+ }
+ Object[] objs = recoverKey(alias, storePass, keyPass);
+ PassphraseSecretKey psk = (PassphraseSecretKey)objs[0];
+ out.println(psk.getEncodedAsString());
+ }
+
+ /**
+ * Exports the private key in a raw mode.
+ */
+ private void doExportPrivate(String alias, PrintStream out) throws Exception {
+ if (alias == null) {
+ alias = keyAlias;
+ }
+
+ Object[] objs = recoverKey(alias, storePass, keyPass);
+ PrivateKey privKey = (PrivateKey)objs[0];
+ if(rfc) {
+ out.println("-----BEGIN PRIVATE KEY-----");
+ new BASE64Encoder().encode(privKey.getEncoded(), out);
+ out.println();
+ out.println("-----END PRIVATE KEY-----");
+ }
+ else {
+ out.write(privKey.getEncoded());
}
}
@@ -1206,8 +1671,9 @@
PrivateKey privKey = keypair.getPrivateKey();
X509Certificate[] chain = new X509Certificate[1];
- chain[0] = keypair.getSelfCertificate(
- x500Name, getStartDate(startDate), validity*24L*60L*60L);
+
+ chain[0] = keypair.getSelfCertificate
+ (x500Name, (long)validity*24*60*60);
if (verbose) {
MessageFormat form = new MessageFormat(rb.getString
@@ -1225,6 +1691,9 @@
keyPass = promptForKeyPass(alias, null, storePass);
}
keyStore.setKeyEntry(alias, privKey, keyPass, chain);
+
+ // So that -startdate and -v3ext are applied.
+ doSelfCert(alias, null, sigAlgName);
}
/**
@@ -1429,7 +1898,8 @@
out.println(new MessageFormat(
rb.getString("Entry type: <type>")).format(source));
} else {
- out.println("PrivateKeyEntry, ");
+ out.print("PrivateKeyEntry, ");
+ printChainInfo(out, keyStore.getCertificateChain(alias));
}
// Get the chain
@@ -1471,7 +1941,11 @@
} else if (debug) {
out.println(cert.toString());
} else {
- out.println(rb.getString("trustedCertEntry,"));
+ out.print(rb.getString("trustedCertEntry,"));
+ out.print(" ");
+ printChainInfo(out, new Certificate[] {keyStore.getCertificate(alias)});
+ // indent a little
+ out.print(" ");
out.println(rb.getString("Certificate fingerprint (SHA1): ")
+ getCertFingerPrint("SHA1", cert));
}
@@ -1724,6 +2198,37 @@
("*******************************************\n\n"));
}
}
+ }
+
+ private void doPrintCRL(InputStream in, PrintStream out)
+ throws Exception {
+ CRL crl = cf.generateCRL(in);
+ out.println(crl.toString());
+ try {
+ ((X509CRLImpl)crl).verify(keyStore.getCertificate(alias).getPublicKey());
+ } catch (Exception e) {
+ out.println(rb.getString
+ ("*******************************************"));
+ out.println("WARNING: not verified. Make sure -keystore and -alias are correct.");
+ out.println(rb.getString
+ ("*******************************************\n\n"));
+ }
+ }
+
+ private void doPrintCertReq(InputStream in, PrintStream out)
+ throws Exception {
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+ StringBuffer sb = new StringBuffer();
+ while (true) {
+ String s = reader.readLine();
+ if (s == null) break;
+ if (!s.startsWith("-")) {
+ sb.append(s);
+ }
+ }
+ PKCS10 req = new PKCS10(new BASE64Decoder().decodeBuffer(new String(sb)));
+ out.println(req.toString());
}
/**
@@ -1914,8 +2419,12 @@
certInfo.set(X509CertInfo.VALIDITY, interval);
// Make new serial number
- certInfo.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber
+ if (serial.negate().equals(BigInteger.ONE)) {
+ certInfo.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber
((int)(firstDate.getTime()/1000)));
+ } else {
+ certInfo.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(serial));
+ }
// Set owner and issuer fields
X500Name owner;
@@ -1947,7 +2456,17 @@
certInfo.set(X509CertInfo.VERSION,
new CertificateVersion(CertificateVersion.V3));
-
+ if (v3ext != null) {
+ CertificateExtensions ext = createV3Extensions(v3ext, oldCert.getPublicKey());
+ certInfo.set(X509CertInfo.EXTENSIONS, ext);
+ if(!extWarning.equals("") && !noprompt) {
+ String reply = getYesNoReply
+ (extWarning + "Do you still want to apply the extensions? [no]: ");
+ if(!reply.equals("YES")) {
+ throw new Exception("Generation aborted by user");
+ }
+ }
+ }
// Sign the new certificate
newCert = new X509CertImpl(certInfo);
newCert.sign(privKey, sigAlgName);
@@ -2289,6 +2808,14 @@
cert.getVersion()
};
out.println(form.format(source));
+ PublicKey key = cert.getPublicKey();
+ if (key instanceof DSAPublicKeyImpl) {
+ DSAPublicKeyImpl impl2 = (DSAPublicKeyImpl)key;
+ System.err.println(impl2.getY().bitLength());
+ } else if (key instanceof RSAPublicKeyImpl) {
+ RSAPublicKeyImpl impl2 = (RSAPublicKeyImpl)key;
+ System.err.println(impl2.getModulus().bitLength());
+ }
int extnum = 0;
if (cert instanceof X509CertImpl) {
@@ -2951,7 +3478,7 @@
Calendar c = new GregorianCalendar();
if (s != null) {
IOException ioe = new IOException(
- rb.getString("Illegal startdate value"));
+ ("Illegal startdate value"));
int len = s.length();
if (len == 0) {
throw ioe;
@@ -3027,6 +3554,199 @@
}
/**
+ * Create X509v3 extensions from a string representation. Note that the
+ * SubjectKeyIdentifierExtension will always be created non-critical besides
+ * the extension requested in the <code>extstr</code> argument.
+ *
+ * Side Effects: If the requested extensions are not compliant with
+ * the RFC 3280, the class field <code>extWarning</code> will be a
+ * non empty string, holding the warning.
+ *
+ * @param extstr
+ * the string form for a set of X509v3 extensions, can be null. Each
+ * item looks like <code><em>key</em>(=<em>value</em>)?</code>
+ * <br>
+ * Here, <em>key</em> can be
+ * <ul>
+ * <li> "bC" or "basicConstraints", add a basicConstraints extension
+ * with CA=true; if <em>value</em> is omited, no limit for pathlen;
+ * otherwise, <em>value</em> means pathlen. error if <em>value</em> < 0.
+ * <li> "kU" or "keyUsage", <em>value</em> looks like <code>[TF]+</code>,
+ * each letter means TRUE or FALSE for the 9 keyUsages.
+ * <li> "xKU" or "extendedKeyUsage", <em>value</em> looks like
+ * <code><em>oid</em>(:<em>oid</em>)*</code>, <em>oid</em> can be
+ * any valid OID with string form <code>\d+(\.\d+)*</code>;
+ * Especially, if there is no period inside <em>oid</em>,
+ * it will be auto-prefixed with "1.3.6.1.5.5.7.3."
+ * <li> "sAN" or "subjectAlternativeName", <em>value</em> looks like
+ * <code><em>type</em>-<em>name</em>(,<em>type</em>-<em>name</em>)*</code>.
+ * <em>type</em> can be <code>EMAIL</code>, <code>DNS</code>,
+ * <code>URI</code>, <code>IP</code>, or <code>OID</code> (always
+ * captitalized), and
+ * <em>name</em> is the name with of specified type. <em>type</em>
+ * is case-insensitive. <b>Attention</b>: DO NOT use "=", or ","
+ * inside <em>value</em>.
+ * </ul>
+ * <em>key</em> is case-insensitive, but the extension is set to be
+ * critical if the first letter of <em>key</em> is a captital letter,
+ * otherwise not critical. <em>value</em> can be omited only
+ * when <em>key</em> is "bC" or "basicConstraints".
+ * @param pkey the public key for the certificate
+ * @return the created CertificateExtensions
+ * @throws IllegalArgumentException if pathlen for basicConstraints < 0
+ * @throws NumberFormatException if pathlen for basicConstraints is not a number
+ * @throws NullPointerException if <code>pkey</code> is null
+ */
+ private CertificateExtensions createV3Extensions(
+ List <String> extstr,
+ PublicKey pkey) {
+
+ extWarning = "";
+
+ boolean isCA = false;
+ boolean isKeyCertSign = false;
+
+ CertificateExtensions ext = new CertificateExtensions();
+ try {
+ // it looks like we can choose a 3-level ';'->','->':' delimeters
+ // to seperate the complex extension string. but since unix doesn't
+ // like ';' inside a command, i won't use them. i want to make sure
+ // user doesn't need to use "" or \\ when creating the string
+ for(String s: extstr) {
+ String[] pv = s.split("=");
+ if(pv.length < 1) { // don't know what this mean
+ continue;
+ }
+
+ boolean isCritical =
+ Character.isUpperCase(pv[0].charAt(0));
+
+ if(pv[0].equalsIgnoreCase("bC") ||
+ pv[0].equalsIgnoreCase("basicConstraints")) {
+ isCA = true;
+ if(pv.length == 1) {
+ ext.set(BasicConstraintsExtension.NAME,
+ new BasicConstraintsExtension(isCritical, true,
+ Integer.MAX_VALUE));
+ } else {
+ try {
+ int pathlen = Integer.parseInt(pv[1]);
+ if(pathlen >= 0) {
+ ext.set(BasicConstraintsExtension.NAME,
+ new BasicConstraintsExtension(
+ isCritical, true, pathlen));
+ } else {
+ ext.set(BasicConstraintsExtension.NAME,
+ new BasicConstraintsExtension(
+ isCritical, false, 0));
+ }
+ } catch(NumberFormatException e) {
+ throw new NumberFormatException(
+ "illegal pathlen for basicConstraints: "
+ + pv[1]);
+ }
+ }
+ } else if(pv[0].equalsIgnoreCase("kU") ||
+ pv[0].equalsIgnoreCase("keyUsage")) {
+ if(pv.length > 1 && pv[1].length() > 0) {
+ int len = pv[1].length();
+ boolean[] ok = new boolean[len];
+ for(int i=0; i<len; i++) {
+ if(pv[1].charAt(i) == 'T') {
+ ok[i] = true;
+ if(i == 5) {
+ isKeyCertSign = true;
+ }
+ }
+ else {
+ ok[i] = false;
+ }
+ }
+ ext.set(KeyUsageExtension.NAME,
+ new KeyUsageExtension(ok));
+ } else {
+ ext.delete(KeyUsageExtension.NAME);
+ }
+ } else if(pv[0].equalsIgnoreCase("xKU") ||
+ pv[0].equalsIgnoreCase("extendedKeyUsage")) {
+ if(pv.length > 1) {
+ String[] ps = pv[1].split(":");
+ Vector <ObjectIdentifier> v =
+ new Vector <ObjectIdentifier>();
+ for(String ones: ps) {
+ if(ones.indexOf('.') < 0)
+ ones = "1.3.6.1.5.5.7.3." + ones;
+ v.add(new ObjectIdentifier(ones));
+ }
+ ext.set(ExtendedKeyUsageExtension.NAME,
+ new ExtendedKeyUsageExtension(isCritical, v));
+ }
+ } else if(pv[0].equalsIgnoreCase("sAN") ||
+ pv[0].equalsIgnoreCase("subjectAlternativeName")) {
+ if(pv.length > 1) {
+ String[] ps = pv[1].split(",");
+ GeneralNames gnames = new GeneralNames();
+ for(String ones: ps) {
+ GeneralNameInterface name;
+ String iones = ones.toUpperCase();
+ if(iones.startsWith("EMAIL-")) {
+ name = new RFC822Name(ones.substring(6));
+ } else if(iones.startsWith("DNS-")) {
+ name = new DNSName(ones.substring(4));
+ } else if(iones.startsWith("URI-")) {
+ name = new URIName(ones.substring(4));
+ } else if(iones.startsWith("IP-")) {
+ name = new IPAddressName(ones.substring(3));
+ } else if(iones.startsWith("OID-")) {
+ name = new OIDName(ones.substring(4));
+ } else {
+ continue;
+ }
+ gnames.add(new GeneralName(name));
+ }
+ ext.set(SubjectAlternativeNameExtension.NAME,
+ new SubjectAlternativeNameExtension(
+ isCritical, gnames));
+ }
+ } else if(pv[0].equalsIgnoreCase("nCT") ||
+ pv[0].equalsIgnoreCase("netscapeCertType")) {
+ if(pv.length > 1 && pv[1].length() > 0) {
+ int len = pv[1].length();
+ boolean[] ok = new boolean[len];
+ for(int i=0; i<len; i++) {
+ if(pv[1].charAt(i) == 'T') {
+ ok[i] = true;
+ if(i == 5) {
+ isKeyCertSign = true;
+ }
+ }
+ else {
+ ok[i] = false;
+ }
+ }
+ ext.set(NetscapeCertTypeExtension.NAME,
+ new NetscapeCertTypeExtension(ok));
+ } else {
+ ext.delete(NetscapeCertTypeExtension.NAME);
+ }
+ }
+ ext.set(SubjectKeyIdentifierExtension.NAME,
+ new SubjectKeyIdentifierExtension(
+ new KeyIdentifier(pkey).getIdentifier()));
+ }
+ } catch(IOException e) {
+ throw new RuntimeException(e);
+ }
+ if(!isCA && isKeyCertSign) {
+ extWarning += "WARNING: illegal X509V3 extensions combination: "+
+ "If the cA boolean is not asserted, then the keyCertSign "+
+ "bit in the key usage extension MUST NOT be asserted. "+
+ "(RFC #3280, 4.2.1.10)\n";
+ }
+ return ext;
+ }
+
+ /**
* Prints the usage of this tool.
*/
private void usage() {
@@ -3098,6 +3818,8 @@
("\t [-sigalg <sigalg>] [-dname <dname>]"));
System.err.println(rb.getString
("\t [-startdate <startdate>]"));
+ System.err.println("\t [-v3ext <v3ext>]");
+ System.err.println("\t [-serial <serial>]");
System.err.println(rb.getString
("\t [-validity <valDays>] [-keypass <keypass>]"));
System.err.println(rb.getString
@@ -3211,6 +3933,8 @@
("\t [-providerclass <provider_class_name> [-providerarg <arg>]] ..."));
System.err.println(rb.getString
("\t [-providerpath <pathlist>]"));
+
+ moreHelp();
if (debug) {
throw new RuntimeException("NO ERROR, SORRY");
@@ -3228,6 +3952,37 @@
} else {
System.exit(1);
}
+ }
+
+ private void moreHelp() {
+ System.err.println();
+ System.err.println("\nHelp for new functions and options:\n");
+ System.err.println("-listalg [-v] List all algorithms of available security providers");
+ System.err.println("-exportprivate [-rfc] export the private key");
+ System.err.println("-putpassphrase <value> Save a passphrase as a SecretKey");
+ System.err.println("-getpassphrase Retrieve the passphrase stored");
+ System.err.println("-base64enc [-file <f>] BASE64 encode the stdin or file to stdout");
+ System.err.println("-base64dec [-file <f>] BASE64 decode the stdin or file to stdout");
+ System.err.println("-rand [-keyalg <ka>] [-keysize <ks>] [-v]\n" +
+ " Generate random bytes, -v output in BASE64");
+ System.err.println("-gencert [-rfc] Generate certificate, signed by alias, req from stdin, cert to stdout");
+ System.err.println("-printcertreq Print the content of a certificate request");
+ System.err.println("-gencrl [-rfc] Generate CRL, id can appear multiple times");
+ System.err.println("-printcrl Print the content of a CRL file");
+ System.err.println("");
+ System.err.println("-v3ext <ext> Used in -gencert, -selfcert, -genkeypair to add X.509 v3 extensions:");
+ System.err.println(" can appear multiple times, format is name[=value], critical when name capitalized");
+ System.err.println(" Name Value");
+ System.err.println(" BC or BasicConstraints int pathlen");
+ System.err.println(" KU or KeyUsage [TF]+, true or false for usages");
+ System.err.println(" XKU or ExtendedkeyUsage oid(:oid)*, auto-prefix 1.3.6.1.5.5.7.3. if single component");
+ System.err.println(" SAN or SubjectAlternativeName type-name(,type-name)*, type can be EMAIL,URL,DNS,IP,OID");
+ System.err.println(" NCT or NetscapeCertType [TF]+, true or false for type");
+ System.err.println("-id <id[:reason]> Used in -gencrl to specify what certificates are revoked");
+ System.err.println("-rethrow If specified, exception is re-thrown");
+ System.err.println("-serial <serial> Used in -gencert, -selfcert, -genkeypair to specify serial number");
+ System.err.println("-startdate <value> Used in -gencert, -selfcert, -genkeypair to change the issue time");
+ System.err.println(" Calculation using Calendar.add() on each field, from left to right.");
}
private void errorNeedArgument(String flag) {
@@ -3271,3 +4026,28 @@
else return fst.hashCode() * 17 + snd.hashCode();
}
}
+
+// The read/write of a SecretKey is performed thru Serialization
+class PassphraseSecretKey implements SecretKey {
+ private static final long serialVersionUID = 1L;
+ private String passphrase = null;
+
+ PassphraseSecretKey(String passphrase) {
+ this.passphrase = passphrase;
+ }
+ public String getAlgorithm() {
+ return "PASSPHRASE";
+ }
+
+ public String getFormat() {
+ return "RAW";
+ }
+
+ public byte[] getEncoded() {
+ return null;
+ }
+
+ public String getEncodedAsString() {
+ return passphrase;
+ }
+}