forked from I2P_Developers/i2p.i2p
Crypto: Add utils for renewing a cert in a keystore
This commit is contained in:
@@ -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.
|
||||
*
|
||||
|
@@ -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.
|
||||
|
@@ -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];
|
||||
|
@@ -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",
|
||||
|
Reference in New Issue
Block a user