Crypto: Use new internal key generation instead of calling

out to keytool; save CRL for new su3 amd family keys
Allow su3 bulksign for xml files (news)
This commit is contained in:
zzz
2016-02-09 20:48:23 +00:00
parent 651c1b6545
commit 981b708230
4 changed files with 229 additions and 51 deletions

View File

@ -97,9 +97,9 @@ public final class CertUtil {
* Writes a certificate in base64 format. * Writes a certificate in base64 format.
* Does NOT close the stream. Throws on all errors. * Does NOT close the stream. Throws on all errors.
* *
* @since 0.9.24, pulled out of saveCert() * @since 0.9.24, pulled out of saveCert(), public since 0.9.25
*/ */
private static void exportCert(Certificate cert, OutputStream out) public static void exportCert(Certificate cert, OutputStream out)
throws IOException, CertificateEncodingException { throws IOException, CertificateEncodingException {
// Get the encoded form which is suitable for exporting // Get the encoded form which is suitable for exporting
byte[] buf = cert.getEncoded(); byte[] buf = cert.getEncoded();
@ -365,7 +365,7 @@ public final class CertUtil {
* @throws CRLException if the crl does not support encoding * @throws CRLException if the crl does not support encoding
* @since 0.9.25 * @since 0.9.25
*/ */
private static void exportCRL(X509CRL crl, OutputStream out) public static void exportCRL(X509CRL crl, OutputStream out)
throws IOException, CRLException { throws IOException, CRLException {
byte[] buf = crl.getEncoded(); byte[] buf = crl.getEncoded();
writePEM(buf, "X509 CRL", out); writePEM(buf, "X509 CRL", out);

View File

@ -9,12 +9,16 @@ import java.math.BigInteger;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException; import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.security.cert.X509CRL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -430,6 +434,9 @@ public final class KeyStoreUtil {
/** /**
* Create a keypair and store it in the keystore at ks, creating it if necessary. * Create a keypair and store it in the keystore at ks, creating it if necessary.
* *
* For new code, the createKeys() with the SigType argument is recommended over this one,
* as it throws exceptions, and returns the certificate and CRL.
*
* Warning, may take a long time. * Warning, may take a long time.
* *
* @param ks path to the keystore * @param ks path to the keystore
@ -447,6 +454,137 @@ public final class KeyStoreUtil {
*/ */
public static boolean createKeys(File ks, String ksPW, String alias, String cname, String ou, public static boolean createKeys(File ks, String ksPW, String alias, String cname, String ou,
int validDays, String keyAlg, int keySize, String keyPW) { int validDays, String keyAlg, int keySize, String keyPW) {
boolean useKeytool = I2PAppContext.getGlobalContext().getBooleanProperty("crypto.useExternalKeytool");
if (useKeytool) {
return createKeysCLI(ks, ksPW, alias, cname, ou, validDays, keyAlg, keySize, keyPW);
} else {
try {
createKeysAndCRL(ks, ksPW, alias, cname, ou, validDays, keyAlg, keySize, keyPW);
return true;
} catch (GeneralSecurityException gse) {
error("Create keys error", gse);
return false;
} catch (IOException ioe) {
error("Create keys error", ioe);
return false;
}
}
}
/**
* New way - Native Java, does not call out to keytool.
* Create a keypair and store it in the keystore at ks, creating it if necessary.
*
* This returns the public key, private key, certificate, and CRL in an array.
* All of these are Java classes. Keys may be converted to I2P classes with SigUtil.
* The private key and selfsigned cert are stored in the keystore.
* The public key may be derived from the private key with KeyGenerator.getSigningPublicKey().
* The public key certificate may be stored separately with
* CertUtil.saveCert() if desired.
* The CRL is not stored by this method, store it with
* CertUtil.saveCRL() or CertUtil.exportCRL() if desired.
*
* Throws on all errors.
* Warning, may take a long time.
*
* @param ks path to the keystore
* @param ksPW the keystore password
* @param alias the name of the key
* @param cname e.g. randomstuff.console.i2p.net
* @param ou e.g. console
* @param validDays e.g. 3652 (10 years)
* @param keyAlg e.g. DSA , RSA, EC
* @param keySize e.g. 1024
* @param keyPW the key password, must be at least 6 characters
* @return all you need:
* 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.25
*/
public static Object[] createKeysAndCRL(File ks, String ksPW, String alias, String cname, String ou,
int validDays, String keyAlg, int keySize, String keyPW)
throws GeneralSecurityException, IOException {
String algoName = getSigAlg(keySize, keyAlg);
SigType type = null;
for (SigType t : EnumSet.allOf(SigType.class)) {
if (t.getAlgorithmName().equals(algoName)) {
type = t;
break;
}
}
if (type == null)
throw new GeneralSecurityException("Unsupported algorithm/size: " + keyAlg + '/' + keySize);
return createKeysAndCRL(ks, ksPW, alias, cname, ou, validDays, type, keyPW);
}
/**
* New way - Native Java, does not call out to keytool.
* Create a keypair and store it in the keystore at ks, creating it if necessary.
*
* This returns the public key, private key, certificate, and CRL in an array.
* All of these are Java classes. Keys may be converted to I2P classes with SigUtil.
* The private key and selfsigned cert are stored in the keystore.
* The public key may be derived from the private key with KeyGenerator.getSigningPublicKey().
* The public key certificate may be stored separately with
* CertUtil.saveCert() if desired.
* The CRL is not stored by this method, store it with
* CertUtil.saveCRL() or CertUtil.exportCRL() if desired.
*
* Throws on all errors.
* Warning, may take a long time.
*
* @param ks path to the keystore
* @param ksPW the keystore password
* @param alias the name of the key
* @param cname e.g. randomstuff.console.i2p.net
* @param ou e.g. console
* @param validDays e.g. 3652 (10 years)
* @param keyAlg e.g. DSA , RSA, EC
* @param keySize e.g. 1024
* @param keyPW the key password, must be at least 6 characters
* @return all you need:
* 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.25
*/
public static Object[] createKeysAndCRL(File ks, String ksPW, String alias, String cname, String ou,
int validDays, SigType type, String keyPW)
throws GeneralSecurityException, IOException {
Object[] rv = SelfSignedGenerator.generate(cname, ou, "XX", "I2P Anonymous Network", "XX", "XX", validDays, type);
PublicKey jpub = (PublicKey) rv[0];
PrivateKey jpriv = (PrivateKey) rv[1];
X509Certificate cert = (X509Certificate) rv[2];
X509CRL crl = (X509CRL) rv[3];
List<X509Certificate> certs = Collections.singletonList(cert);
storePrivateKey(ks, ksPW, alias, keyPW, jpriv, certs);
return rv;
}
/**
* OLD way - keytool
* Create a keypair and store it in the keystore at ks, creating it if necessary.
*
* Warning, may take a long time.
*
* @param ks path to the keystore
* @param ksPW the keystore password
* @param alias the name of the key
* @param cname e.g. randomstuff.console.i2p.net
* @param ou e.g. console
* @param validDays e.g. 3652 (10 years)
* @param keyAlg e.g. DSA , RSA, EC
* @param keySize e.g. 1024
* @param keyPW the key password, must be at least 6 characters
*
* @return success
* @since 0.8.3, consolidated from RouterConsoleRunner and SSLClientListenerRunner in 0.9.9
*/
private static boolean createKeysCLI(File ks, String ksPW, String alias, String cname, String ou,
int validDays, String keyAlg, int keySize, String keyPW) {
if (ks.exists()) { if (ks.exists()) {
try { try {
if (getCert(ks, ksPW, alias) != null) { if (getCert(ks, ksPW, alias) != null) {

View File

@ -15,6 +15,8 @@ import java.security.GeneralSecurityException;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.security.cert.X509CRL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.EnumSet; import java.util.EnumSet;
@ -33,6 +35,7 @@ import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.Signature; import net.i2p.data.Signature;
import net.i2p.data.SimpleDataStructure; import net.i2p.data.SimpleDataStructure;
import net.i2p.util.SecureFileOutputStream;
/** /**
* Succesor to the ".sud" format used in TrustedUpdate. * Succesor to the ".sud" format used in TrustedUpdate.
@ -538,10 +541,11 @@ public class SU3File {
String ctype = null; String ctype = null;
String ftype = null; String ftype = null;
String kfile = null; String kfile = null;
String crlfile = null;
String kspass = KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD; String kspass = KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD;
boolean error = false; boolean error = false;
boolean shouldVerify = true; boolean shouldVerify = true;
Getopt g = new Getopt("SU3File", args, "t:c:f:k:xp:"); Getopt g = new Getopt("SU3File", args, "t:c:f:k:xp:r:");
int c; int c;
while ((c = g.getopt()) != -1) { while ((c = g.getopt()) != -1) {
switch (c) { switch (c) {
@ -561,6 +565,10 @@ public class SU3File {
kfile = g.getOptarg(); kfile = g.getOptarg();
break; break;
case 'r':
crlfile = g.getOptarg();
break;
case 'x': case 'x':
shouldVerify = false; shouldVerify = false;
break; break;
@ -598,7 +606,10 @@ public class SU3File {
} else if ("verifysig".equals(cmd)) { } else if ("verifysig".equals(cmd)) {
ok = verifySigCLI(a.get(0), kfile); ok = verifySigCLI(a.get(0), kfile);
} else if ("keygen".equals(cmd)) { } else if ("keygen".equals(cmd)) {
ok = genKeysCLI(stype, a.get(0), a.get(1), a.get(2), kspass); Properties props = new Properties();
props.setProperty("prng.bufferSize", "16384");
new I2PAppContext(props);
ok = genKeysCLI(stype, a.get(0), a.get(1), crlfile, a.get(2), kspass);
} else if ("extract".equals(cmd)) { } else if ("extract".equals(cmd)) {
ok = extractCLI(a.get(0), a.get(1), shouldVerify, kfile); ok = extractCLI(a.get(0), a.get(1), shouldVerify, kfile);
} else { } else {
@ -614,9 +625,10 @@ public class SU3File {
} }
private static final void showUsageCLI() { private static final void showUsageCLI() {
System.err.println("Usage: SU3File keygen [-t type|code] [-p keystorepw] publicKeyFile keystore.ks you@mail.i2p\n" + System.err.println("Usage: SU3File keygen [-t type|code] [-p keystorepw] [-r crlFile.crl] publicKeyFile.crt keystore.ks you@mail.i2p\n" +
" SU3File sign [-t type|code] [-c type|code] [-f type|code] [-p keystorepw] inputFile.zip signedFile.su3 keystore.ks version you@mail.i2p\n" + " SU3File sign [-t type|code] [-c type|code] [-f type|code] [-p keystorepw] inputFile.zip signedFile.su3 keystore.ks version you@mail.i2p\n" +
" SU3File bulksign [-t type|code] [-c type|code] [-p keystorepw] directory keystore.ks version you@mail.i2p\n" + " SU3File bulksign [-t type|code] [-c type|code] [-p keystorepw] directory keystore.ks version you@mail.i2p\n" +
" (signs all .zip and .xml files in the directory)\n" +
" SU3File showversion signedFile.su3\n" + " SU3File showversion signedFile.su3\n" +
" SU3File verifysig [-k file.crt] signedFile.su3 ## -k use this pubkey cert for verification\n" + " SU3File verifysig [-k file.crt] signedFile.su3 ## -k use this pubkey cert for verification\n" +
" SU3File extract [-x] [-k file.crt] signedFile.su3 outFile ## -x don't check sig"); " SU3File extract [-x] [-k file.crt] signedFile.su3 outFile ## -x don't check sig");
@ -750,7 +762,7 @@ public class SU3File {
int success = 0; int success = 0;
for (File in : files) { for (File in : files) {
String inputFile = in.getPath(); String inputFile = in.getPath();
if (!inputFile.endsWith(".zip")) if (!inputFile.endsWith(".zip") && !inputFile.endsWith(".xml"))
continue; continue;
String signedFile = inputFile.substring(0, inputFile.length() - 4) + ".su3"; String signedFile = inputFile.substring(0, inputFile.length() - 4) + ".su3";
boolean rv = signCLI(stype, ctype, null, inputFile, signedFile, boolean rv = signCLI(stype, ctype, null, inputFile, signedFile,
@ -897,26 +909,29 @@ public class SU3File {
} }
/** /**
* @param crlFile may be null; non-null to save
* @return success * @return success
* @since 0.9.9 * @since 0.9.9
*/ */
private static final boolean genKeysCLI(String stype, String publicKeyFile, String privateKeyFile, private static final boolean genKeysCLI(String stype, String publicKeyFile, String privateKeyFile,
String alias, String kspass) { String crlFile, String alias, String kspass) {
SigType type = stype == null ? SigType.getByCode(Integer.valueOf(DEFAULT_SIG_CODE)) : SigType.parseSigType(stype); SigType type = stype == null ? SigType.getByCode(Integer.valueOf(DEFAULT_SIG_CODE)) : SigType.parseSigType(stype);
if (type == null) { if (type == null) {
System.out.println("Signature type " + stype + " is not supported"); System.out.println("Signature type " + stype + " is not supported");
return false; return false;
} }
return genKeysCLI(type, publicKeyFile, privateKeyFile, alias, kspass); return genKeysCLI(type, publicKeyFile, privateKeyFile, crlFile, alias, kspass);
} }
/** /**
* Writes Java-encoded keys (X.509 for public and PKCS#8 for private) * Writes Java-encoded keys (X.509 for public and PKCS#8 for private)
*
* @param crlFile may be null; non-null to save
* @return success * @return success
* @since 0.9.9 * @since 0.9.9
*/ */
private static final boolean genKeysCLI(SigType type, String publicKeyFile, String privateKeyFile, private static final boolean genKeysCLI(SigType type, String publicKeyFile, String privateKeyFile,
String alias, String kspass) { String crlFile, String alias, String kspass) {
File pubFile = new File(publicKeyFile); File pubFile = new File(publicKeyFile);
if (pubFile.exists()) { if (pubFile.exists()) {
System.out.println("Error: Not overwriting file " + publicKeyFile); System.out.println("Error: Not overwriting file " + publicKeyFile);
@ -948,24 +963,29 @@ public class SU3File {
} catch (IOException ioe) { } catch (IOException ioe) {
return false; return false;
} }
int keylen = type.getPubkeyLen() * 8; OutputStream out = null;
if (type.getBaseAlgorithm() == SigAlgo.EC) { try {
keylen /= 2; Object[] rv = KeyStoreUtil.createKeysAndCRL(ksFile, kspass, alias,
if (keylen == 528) alias, "I2P", 3652, type, keypw);
keylen = 521; X509Certificate cert = (X509Certificate) rv[2];
} out = new SecureFileOutputStream(publicKeyFile);
boolean success = KeyStoreUtil.createKeys(ksFile, kspass, alias, CertUtil.exportCert(cert, out);
alias, "I2P", 3652, type.getBaseAlgorithm().getName(), if (crlFile != null) {
keylen, keypw); out.close();
if (!success) { X509CRL crl = (X509CRL) rv[3];
out = new SecureFileOutputStream(crlFile);
CertUtil.exportCRL(crl, out);
}
} catch (GeneralSecurityException gse) {
System.err.println("Error creating keys for " + alias); System.err.println("Error creating keys for " + alias);
gse.printStackTrace();
return false; return false;
} } catch (IOException ioe) {
File outfile = new File(publicKeyFile); System.err.println("Error creating keys for " + alias);
success = KeyStoreUtil.exportCert(ksFile, KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD, alias, outfile); ioe.printStackTrace();
if (!success) {
System.err.println("Error writing public key for " + alias + " to " + outfile);
return false; return false;
} finally {
if (out != null) try { out.close(); } catch (IOException ioe) {}
} }
return true; return true;
} }

View File

@ -8,6 +8,8 @@ import java.security.KeyStore;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.security.cert.X509CRL;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -51,6 +53,7 @@ public class FamilyKeyCrypto {
public static final String PROP_FAMILY_NAME = "netdb.family.name"; public static final String PROP_FAMILY_NAME = "netdb.family.name";
public static final String PROP_KEY_PASSWORD = "netdb.family.keyPassword"; public static final String PROP_KEY_PASSWORD = "netdb.family.keyPassword";
public static final String CERT_SUFFIX = ".crt"; public static final String CERT_SUFFIX = ".crt";
public static final String CRL_SUFFIX = ".crl";
public static final String KEYSTORE_PREFIX = "family-"; public static final String KEYSTORE_PREFIX = "family-";
public static final String KEYSTORE_SUFFIX = ".ks"; public static final String KEYSTORE_SUFFIX = ".ks";
public static final String CN_SUFFIX = ".family.i2p.net"; public static final String CN_SUFFIX = ".family.i2p.net";
@ -61,6 +64,7 @@ public class FamilyKeyCrypto {
private static final int DEFAULT_KEY_SIZE = SigType.ECDSA_SHA256_P256.isAvailable() ? 256 : 1024; private static final int DEFAULT_KEY_SIZE = SigType.ECDSA_SHA256_P256.isAvailable() ? 256 : 1024;
private static final String KS_DIR = "keystore"; private static final String KS_DIR = "keystore";
private static final String CERT_DIR = "certificates/family"; private static final String CERT_DIR = "certificates/family";
private static final String CRL_DIR = "crls";
public static final String OPT_NAME = "family"; public static final String OPT_NAME = "family";
public static final String OPT_SIG = "family.sig"; public static final String OPT_SIG = "family.sig";
public static final String OPT_KEY = "family.key"; public static final String OPT_KEY = "family.key";
@ -270,11 +274,12 @@ public class FamilyKeyCrypto {
throw new GeneralSecurityException(s); throw new GeneralSecurityException(s);
} }
} }
createKeyStore(ks);
// Now read it back out of the new keystore and save it in ascii form try {
// where the clients can get to it. createKeyStore(ks);
exportCert(ks); } catch (IOException ioe) {
throw new GeneralSecurityException("Failed to create NetDb family keystore", ioe);
}
} }
@ -286,26 +291,22 @@ public class FamilyKeyCrypto {
* *
* @throws GeneralSecurityException on all errors * @throws GeneralSecurityException on all errors
*/ */
private void createKeyStore(File ks) throws GeneralSecurityException { private void createKeyStore(File ks) throws GeneralSecurityException, IOException {
// make a random 48 character password (30 * 8 / 5) // make a random 48 character password (30 * 8 / 5)
String keyPassword = KeyStoreUtil.randomString(); String keyPassword = KeyStoreUtil.randomString();
// and one for the cname // and one for the cname
String cname = _fname + CN_SUFFIX; String cname = _fname + CN_SUFFIX;
boolean success = KeyStoreUtil.createKeys(ks, KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD, _fname, cname, "family", Object[] rv = KeyStoreUtil.createKeysAndCRL(ks, KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD, _fname, cname, "family",
DEFAULT_KEY_VALID_DAYS, DEFAULT_KEY_ALGORITHM, DEFAULT_KEY_VALID_DAYS, DEFAULT_KEY_ALGORITHM,
DEFAULT_KEY_SIZE, keyPassword); DEFAULT_KEY_SIZE, keyPassword);
if (success) {
success = ks.exists();
if (success) {
Map<String, String> changes = new HashMap<String, String>(); Map<String, String> changes = new HashMap<String, String>();
changes.put(PROP_KEYSTORE_PASSWORD, KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD); changes.put(PROP_KEYSTORE_PASSWORD, KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD);
changes.put(PROP_KEY_PASSWORD, keyPassword); changes.put(PROP_KEY_PASSWORD, keyPassword);
changes.put(PROP_FAMILY_NAME, _fname); changes.put(PROP_FAMILY_NAME, _fname);
_context.router().saveConfig(changes, null); _context.router().saveConfig(changes, null);
}
}
if (success) {
_log.logAlways(Log.INFO, "Created new private key for netdb family \"" + _fname + _log.logAlways(Log.INFO, "Created new private key for netdb family \"" + _fname +
"\" in keystore: " + ks.getAbsolutePath() + "\n" + "\" in keystore: " + ks.getAbsolutePath() + "\n" +
"Copy the keystore to the other routers in the family,\n" + "Copy the keystore to the other routers in the family,\n" +
@ -314,27 +315,22 @@ public class FamilyKeyCrypto {
PROP_KEYSTORE_PASSWORD + '=' + KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD + '\n' + PROP_KEYSTORE_PASSWORD + '=' + KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD + '\n' +
PROP_KEY_PASSWORD + '=' + keyPassword); PROP_KEY_PASSWORD + '=' + keyPassword);
} else { X509Certificate cert = (X509Certificate) rv[2];
String s = "Failed to create NetDb family keystore.\n" + exportCert(cert);
"This is for the Sun/Oracle keytool, others may be incompatible.\n" + X509CRL crl = (X509CRL) rv[3];
"If you create the keystore manually, you must add " + PROP_KEYSTORE_PASSWORD + " and " + PROP_KEY_PASSWORD + exportCRL(ks.getParentFile(), crl);
" to " + (new File(_context.getConfigDir(), "router.config")).getAbsolutePath();
_log.error(s);
throw new GeneralSecurityException(s);
}
} }
/** /**
* Pull the cert back OUT of the keystore and save it as ascii * Save the public key certificate
* so the clients can get to it. * so the clients can get to it.
*/ */
private void exportCert(File ks) { private void exportCert(X509Certificate cert) {
File sdir = new SecureDirectory(_context.getConfigDir(), CERT_DIR); File sdir = new SecureDirectory(_context.getConfigDir(), CERT_DIR);
if (sdir.exists() || sdir.mkdirs()) { if (sdir.exists() || sdir.mkdirs()) {
String ksPass = _context.getProperty(PROP_KEYSTORE_PASSWORD, KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD);
String name = _fname.replace("@", "_at_") + CERT_SUFFIX; String name = _fname.replace("@", "_at_") + CERT_SUFFIX;
File out = new File(sdir, name); File out = new File(sdir, name);
boolean success = KeyStoreUtil.exportCert(ks, ksPass, _fname, out); boolean success = CertUtil.saveCert(cert, out);
if (success) { if (success) {
_log.logAlways(Log.INFO, "Created new public key certificate for netdb family \"" + _fname + _log.logAlways(Log.INFO, "Created new public key certificate for netdb family \"" + _fname +
"\" in file: " + out.getAbsolutePath() + "\n" + "\" in file: " + out.getAbsolutePath() + "\n" +
@ -342,10 +338,34 @@ public class FamilyKeyCrypto {
"Copy the certificate to the directory $I2P/" + CERT_DIR + " for each of the other routers in the family.\n" + "Copy the certificate to the directory $I2P/" + CERT_DIR + " for each of the other routers in the family.\n" +
"Give this certificate to an I2P developer for inclusion in the next I2P release."); "Give this certificate to an I2P developer for inclusion in the next I2P release.");
} else { } else {
_log.error("Error getting SSL cert to save as ASCII"); _log.error("Error saving family key certificate");
} }
} else { } else {
_log.error("Error saving ASCII SSL keys"); _log.error("Error saving family key certificate");
}
}
/**
* Save the CRL just in case.
* @param ksdir parent of directory to save in
* @since 0.9.25
*/
private void exportCRL(File ksdir, X509CRL crl) {
File sdir = new SecureDirectory(ksdir, CRL_DIR);
if (sdir.exists() || sdir.mkdirs()) {
String name = KEYSTORE_PREFIX + _fname.replace("@", "_at_") + '-' + System.currentTimeMillis() + CRL_SUFFIX;
File out = new File(sdir, name);
boolean success = CertUtil.saveCRL(crl, out);
if (success) {
_log.logAlways(Log.INFO, "Created certificate revocation list (CRL) for netdb family \"" + _fname +
"\" in file: " + out.getAbsolutePath() + "\n" +
"Back up the keystore and CRL files and keep them secure.\n" +
"If your private key is ever compromised, give the CRL to an I2P developer for publication.");
} else {
_log.error("Error saving family key CRL");
}
} else {
_log.error("Error saving family key CRL");
} }
} }