Crypto: Add utils for renewing a cert in a keystore

This commit is contained in:
zzz
2018-03-11 22:17:06 +00:00
parent 63a8b4668c
commit 81808d4a60
4 changed files with 186 additions and 2 deletions

View File

@@ -156,6 +156,30 @@ public final class CertUtil {
throw new IOException("Failed write to " + out);
}
/**
* Get the set of Subject Alternative Names, including
* DNSNames, RFC822Names, IPv4 and v6 addresses as strings.
*
* see X509Certificate.getSubjectAlternativeNames()
*
* @return non-null, empty on error or none found
* @since 0.9.34
*/
public static Set<String> getSubjectAlternativeNames(X509Certificate cert) {
Set<String> rv = new HashSet<String>(8);
try {
Collection<List<?>> c = cert.getSubjectAlternativeNames();
if (c != null) {
for (List<?> list : c) {
try {
rv.add((String) list.get(1));
} catch (ClassCastException cce) {}
}
}
} catch(GeneralSecurityException gse) {}
return rv;
}
/**
* Get a value out of the subject distinguished name.
*

View File

@@ -1005,7 +1005,7 @@ public final class KeyStoreUtil {
/**
* Export the private key and certificate chain (if any) out of a keystore.
* Does NOT close the stream. Throws on all errors.
* Does NOT close the output stream. Throws on all errors.
*
* @param ks path to the keystore
* @param ksPW the keystore password, may be null
@@ -1033,6 +1033,59 @@ public final class KeyStoreUtil {
}
}
/**
* Renew the the private key certificate in a keystore.
* Closes the input and output streams. Throws on all errors.
*
* @param ks path to the keystore
* @param ksPW the keystore password, may be null
* @param alias the name of the key, or null to get the first one in keystore
* @param keyPW the key password, must be at least 6 characters
* @param validDays new cert to expire this many days from now
* @return the new certificate
* @since 0.9.34
*/
public static X509Certificate renewPrivateKeyCertificate(File ks, String ksPW, String alias,
String keyPW, int validDays)
throws GeneralSecurityException, IOException {
InputStream fis = null;
OutputStream fos = null;
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
fis = new FileInputStream(ks);
char[] pwchars = ksPW != null ? ksPW.toCharArray() : null;
keyStore.load(fis, pwchars);
try { fis.close(); } catch (IOException ioe) {}
fis = null;
char[] keypwchars = keyPW.toCharArray();
if (alias == null) {
for (Enumeration<String> e = keyStore.aliases(); e.hasMoreElements();) {
alias = e.nextElement();
break;
}
if (alias == null)
throw new GeneralSecurityException("no private keys found");
}
PrivateKey pk = (PrivateKey) keyStore.getKey(alias, keypwchars);
if (pk == null)
throw new GeneralSecurityException("private key not found: " + alias);
Certificate[] certs = keyStore.getCertificateChain(alias);
if (certs.length != 1)
throw new GeneralSecurityException("Bad cert chain length");
X509Certificate cert = (X509Certificate) certs[0];
Object[] rv = SelfSignedGenerator.renew(cert, pk, validDays);
cert = (X509Certificate) rv[2];
certs[0] = cert;
keyStore.setKeyEntry(alias, pk, keypwchars, certs);
fos = new SecureFileOutputStream(ks);
keyStore.store(fos, pwchars);
return cert;
} finally {
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
}
}
/**
* Import the private key and certificate chain to a keystore.
* Keystore will be created if it does not exist.

View File

@@ -141,7 +141,30 @@ public final class SelfSignedGenerator {
SigningPrivateKey priv = (SigningPrivateKey) keys[1];
PublicKey jpub = SigUtil.toJavaKey(pub);
PrivateKey jpriv = SigUtil.toJavaKey(priv);
return generate(jpub, jpriv, priv, type, cname, altNames, ou, o, l, st, c, validDays);
}
/**
* @param cname the common name, non-null. Must be a hostname or email address. IP addresses will not be correctly encoded.
* @param altNames the Subject Alternative Names. May be null. May contain hostnames and/or IP addresses.
* cname, localhost, 127.0.0.1, and ::1 will be automatically added.
* @param ou The OU (organizational unit) in the distinguished name, non-null before 0.9.28, may be null as of 0.9.28
* @param o The O (organization)in the distinguished name, non-null before 0.9.28, may be null as of 0.9.28
* @param l The L (city or locality) in the distinguished name, non-null before 0.9.28, may be null as of 0.9.28
* @param st The ST (state or province) in the distinguished name, non-null before 0.9.28, may be null as of 0.9.28
* @param c The C (country) in the distinguished name, non-null before 0.9.28, may be null as of 0.9.28
*
* @return length 4 array:
* rv[0] is a Java PublicKey
* rv[1] is a Java PrivateKey
* rv[2] is a Java X509Certificate
* rv[3] is a Java X509CRL
*
* @since 0.9.34 added altNames param
*/
private static Object[] generate(PublicKey jpub, PrivateKey jpriv, SigningPrivateKey priv, SigType type,
String cname, Set<String> altNames, String ou, String o, String l, String st, String c,
int validDays) throws GeneralSecurityException {
String oid;
switch (type) {
case DSA_SHA1:
@@ -222,6 +245,37 @@ public final class SelfSignedGenerator {
return rv;
}
/**
* @param cert the old cert to be replaced
* @param jpriv the private key
*
* @return length 4 array:
* rv[0] is a Java PublicKey, from cert as passed in
* rv[1] is a Java PrivateKey, jpriv as passed in
* rv[2] is a Java X509Certificate, new one
* rv[3] is a Java X509CRL, new one
*
* @since 0.9.34 added altNames param
*/
public static Object[] renew(X509Certificate cert, PrivateKey jpriv, int validDays) throws GeneralSecurityException {
String cname = CertUtil.getSubjectValue(cert, "CN");
if (cname == null)
cname = "localhost";
String ou = CertUtil.getSubjectValue(cert, "OU");
String o = CertUtil.getSubjectValue(cert, "O");
String l = CertUtil.getSubjectValue(cert, "L");
String st = CertUtil.getSubjectValue(cert, "ST");
String c = CertUtil.getSubjectValue(cert, "C");
Set<String> altNames = CertUtil.getSubjectAlternativeNames(cert);
SigningPrivateKey priv = SigUtil.fromJavaKey(jpriv);
SigType type = priv.getType();
SigningPublicKey pub = KeyGenerator.getSigningPublicKey(priv);
PublicKey jpub = SigUtil.toJavaKey(pub);
if (type == null)
throw new GeneralSecurityException("Unsupported: " + jpriv);
return generate(jpub, jpriv, priv, type, cname, altNames, ou, o, l, st, c, validDays);
}
/**
* Generate a CRL for the given cert, signed with the given private key
*/
@@ -833,8 +887,54 @@ public final class SelfSignedGenerator {
/**
* Note: For CLI testing, use java -jar i2p.jar su3file keygen pubkey.crt keystore.ks commonName
*/
public static void main(String[] args) throws Exception {
if (args.length == 0) {
usage();
} else if (args[0].equals("keygen")) {
if (args.length >= 4)
SU3File.main(args);
else
usage();
} else if (args[0].equals("renew")) {
if (args.length >= 3) {
String ksPW, cert, ks;
if (args[1].equals("-p")) {
ksPW = args[2];
cert = args[3];
ks = args[4];
} else {
ksPW = KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD;
cert = args[1];
ks = args[2];
}
String keypw = "";
try {
while (keypw.length() < 6) {
System.out.print("Enter password for key: ");
keypw = DataHelper.readLine(System.in);
if (keypw == null) {
System.out.println("\nEOF reading password");
System.exit(1);
}
keypw = keypw.trim();
if (keypw.length() > 0 && keypw.length() < 6)
System.out.println("Key password must be at least 6 characters");
}
} catch (IOException ioe) {
System.out.println("Error asking for password");
throw ioe;
}
File ksf = new File(ks);
X509Certificate newCert = KeyStoreUtil.renewPrivateKeyCertificate(ksf, ksPW, null, keypw, 3652);
CertUtil.saveCert(newCert, new File(cert));
System.out.println("Certificate renewed for 10 years, and stored in " + cert + " and " + ks);
} else {
usage();
}
} else {
usage();
}
/****
public static void main(String[] args) {
try {
int i = 0;
for (SigType t : java.util.EnumSet.allOf(SigType.class)) {
@@ -845,8 +945,14 @@ public final class SelfSignedGenerator {
} catch (Exception e) {
e.printStackTrace();
}
****/
}
private static void usage() {
System.err.println("Usage: selfsignedgenerator keygen [-t type|code] [-p keystorepw] [-r crlFile.crl] publicKeyFile.crt keystore.ks localhost\n" +
" selfsignedgenerator renew [-p keystorepw] publicKeyFile.crt keystore.ks");
}
/****
private static final void test(String name, SigType type) throws Exception {
Object[] rv = generate("cname@example.com", "ou", "o", null, "st", "c", 3652, type);
//PublicKey jpub = (PublicKey) rv[0];

View File

@@ -23,6 +23,7 @@ public class CommandLine {
"net.i2p.CoreVersion",
"net.i2p.crypto.CertUtil",
"net.i2p.crypto.CryptoCheck",
"net.i2p.crypto.SelfSignedGenerator",
"net.i2p.crypto.SU3File",
"net.i2p.crypto.TrustedUpdate",
"net.i2p.data.Base32",