forked from I2P_Developers/i2p.i2p
KeyStoreUtil: Implement system cert blacklist
Fix creation of empty keystore test enhancements
This commit is contained in:
@ -77,12 +77,40 @@ public class CertUtil {
|
||||
* @return value or null if not found
|
||||
*/
|
||||
public static String getSubjectValue(X509Certificate cert, String type) {
|
||||
X500Principal p = cert.getSubjectX500Principal();
|
||||
return getValue(p, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value out of the issuer distinguished name.
|
||||
*
|
||||
* Warning - unsupported in Android (no javax.naming), returns null.
|
||||
*
|
||||
* @param type e.g. "CN"
|
||||
* @return value or null if not found
|
||||
* @since 0.9.24
|
||||
*/
|
||||
public static String getIssuerValue(X509Certificate cert, String type) {
|
||||
X500Principal p = cert.getIssuerX500Principal();
|
||||
return getValue(p, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value out of a X500Principal.
|
||||
*
|
||||
* Warning - unsupported in Android (no javax.naming), returns null.
|
||||
*
|
||||
* @param type e.g. "CN"
|
||||
* @return value or null if not found
|
||||
*/
|
||||
private static String getValue(X500Principal p, String type) {
|
||||
if (SystemVersion.isAndroid()) {
|
||||
error("Don't call this in Android", new UnsupportedOperationException("I did it"));
|
||||
return null;
|
||||
}
|
||||
if (p == null)
|
||||
return null;
|
||||
type = type.toUpperCase(Locale.US);
|
||||
X500Principal p = cert.getSubjectX500Principal();
|
||||
String subj = p.getName();
|
||||
try {
|
||||
LdapName name = new LdapName(subj);
|
||||
|
@ -5,6 +5,7 @@ import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
@ -36,6 +37,27 @@ public class KeyStoreUtil {
|
||||
private static final int DEFAULT_KEY_SIZE = 2048;
|
||||
private static final int DEFAULT_KEY_VALID_DAYS = 3652; // 10 years
|
||||
|
||||
/**
|
||||
* No reports of these in a Java keystore but just to be safe...
|
||||
*/
|
||||
private static final BigInteger[] BLACKLIST_SERIAL = new BigInteger[] {
|
||||
// Superfish http://blog.erratasec.com/2015/02/extracting-superfish-certificate.html
|
||||
new BigInteger("d2:fc:13:87:a9:44:dc:e7".replace(":", ""), 16),
|
||||
// eDellRoot https://www.reddit.com/r/technology/comments/3twmfv/dell_ships_laptops_with_rogue_root_ca_exactly/
|
||||
new BigInteger("6b:c5:7b:95:18:93:aa:97:4b:62:4a:c0:88:fc:3b:b6".replace(":", ""), 16)
|
||||
};
|
||||
|
||||
/**
|
||||
* Corresponding issuer CN for the serial number.
|
||||
* Must be same number of entries as BLACKLIST_SERIAL.
|
||||
* See removeBlacklistedCerts() below for alternatives if we want
|
||||
* to blacklist a cert without an issuer CN.
|
||||
*/
|
||||
private static final String[] BLACKLIST_ISSUER_CN = new String[] {
|
||||
"Superfish, Inc.",
|
||||
"eDellRoot"
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new KeyStore object, and load it from ksFile if it is
|
||||
* non-null and it exists.
|
||||
@ -63,6 +85,8 @@ public class KeyStoreUtil {
|
||||
if (ksFile != null && !exists) {
|
||||
OutputStream fos = null;
|
||||
try {
|
||||
// must be initted
|
||||
ks.load(null, DEFAULT_KEYSTORE_PASSWORD.toCharArray());
|
||||
fos = new SecureFileOutputStream(ksFile);
|
||||
ks.store(fos, pwchars);
|
||||
} finally {
|
||||
@ -110,7 +134,9 @@ public class KeyStoreUtil {
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
if (success) {
|
||||
removeBlacklistedCerts(ks);
|
||||
} else {
|
||||
try {
|
||||
// must be initted
|
||||
ks.load(null, DEFAULT_KEYSTORE_PASSWORD.toCharArray());
|
||||
@ -171,7 +197,7 @@ public class KeyStoreUtil {
|
||||
for(Enumeration<String> e = ks.aliases(); e.hasMoreElements();) {
|
||||
String alias = e.nextElement();
|
||||
if (ks.isCertificateEntry(alias)) {
|
||||
info("Found cert " + alias);
|
||||
//info("Found cert " + alias);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
@ -179,6 +205,56 @@ public class KeyStoreUtil {
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all blacklisted X509 Certs in a key store.
|
||||
* Match by serial number and issuer CN, which should uniquely identify a cert,
|
||||
* if the CN is present. Should be faster than fingerprints.
|
||||
*
|
||||
* @return number successfully removed
|
||||
* @since 0.9.24
|
||||
*/
|
||||
private static int removeBlacklistedCerts(KeyStore ks) {
|
||||
// This matches on the CN in the issuer,
|
||||
// and we can't do that on Android.
|
||||
// We could just match the whole string, and we will have to
|
||||
// if we want do it on Android or match a cert that has an issuer without a CN.
|
||||
// Or, most certs that don't have a CN have an OU, that could be a fallback.
|
||||
// Or do sha1hash(cert.getEncoded()) but that would be slower.
|
||||
if (SystemVersion.isAndroid())
|
||||
return 0;
|
||||
int count = 0;
|
||||
try {
|
||||
for(Enumeration<String> e = ks.aliases(); e.hasMoreElements();) {
|
||||
String alias = e.nextElement();
|
||||
if (ks.isCertificateEntry(alias)) {
|
||||
Certificate c = ks.getCertificate(alias);
|
||||
if (c != null && (c instanceof X509Certificate)) {
|
||||
X509Certificate xc = (X509Certificate) c;
|
||||
BigInteger serial = xc.getSerialNumber();
|
||||
for (int i = 0; i < BLACKLIST_SERIAL.length; i++) {
|
||||
// debug:
|
||||
//String xname = CertUtil.getIssuerValue(xc, "CN");
|
||||
//info("Found \"" + xname + "\" s/n: " + serial.toString(16));
|
||||
//if (xname == null)
|
||||
// info("name is null, full issuer: " + xc.getIssuerX500Principal().getName());
|
||||
if (BLACKLIST_SERIAL[i].equals(serial)) {
|
||||
String name = CertUtil.getIssuerValue(xc, "CN");
|
||||
if (BLACKLIST_ISSUER_CN[i].equals(name)) {
|
||||
ks.deleteEntry(alias);
|
||||
warn("Ignoring blacklisted certificate \"" + alias +
|
||||
"\" issued by: \"" + name +
|
||||
"\" s/n: " + serial.toString(16), null);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (GeneralSecurityException e) {}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all X509 Certs from a directory and add them to the
|
||||
* trusted set of certificates in the key store
|
||||
@ -513,9 +589,9 @@ public class KeyStoreUtil {
|
||||
|
||||
/****
|
||||
public static void main(String[] args) {
|
||||
File ksf = (args.length > 0) ? new File(args[0]) : null;
|
||||
try {
|
||||
if (args.length > 0) {
|
||||
File ksf = new File(args[0]);
|
||||
if (ksf != null && !ksf.exists()) {
|
||||
createKeyStore(ksf, DEFAULT_KEYSTORE_PASSWORD);
|
||||
System.out.println("Created empty keystore " + ksf);
|
||||
} else {
|
||||
@ -524,6 +600,16 @@ public class KeyStoreUtil {
|
||||
System.out.println("Loaded system keystore");
|
||||
int count = countCerts(ks);
|
||||
System.out.println("Found " + count + " certs");
|
||||
if (ksf != null && ksf.isDirectory()) {
|
||||
count = addCerts(ksf, ks);
|
||||
System.out.println("Found " + count + " certs in " + ksf);
|
||||
if (count > 0) {
|
||||
// rerun blacklist as a test
|
||||
count = removeBlacklistedCerts(ks);
|
||||
if (count > 0)
|
||||
System.out.println("Found " + count + " blacklisted certs in " + ksf);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
System.out.println("FAIL");
|
||||
}
|
||||
|
Reference in New Issue
Block a user