forked from I2P_Developers/i2p.i2p
Crypto: Blacklist certificates by SHA1 hash,
not by serial/CN/OU
This commit is contained in:
@ -8,6 +8,7 @@ import java.io.OutputStream;
|
|||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
|
import java.security.MessageDigest;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.cert.Certificate;
|
import java.security.cert.Certificate;
|
||||||
@ -20,8 +21,10 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.crypto.provider.I2PProvider;
|
import net.i2p.crypto.provider.I2PProvider;
|
||||||
@ -51,66 +54,85 @@ public final class KeyStoreUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* SHA1 hashes
|
||||||
|
*
|
||||||
* No reports of some of these in a Java keystore but just to be safe...
|
* No reports of some of these in a Java keystore but just to be safe...
|
||||||
* CNNIC ones are in Ubuntu keystore.
|
* CNNIC ones are in Ubuntu keystore.
|
||||||
|
*
|
||||||
|
* In comments below are the serial numer, CN, and OU
|
||||||
*/
|
*/
|
||||||
private static final BigInteger[] BLACKLIST_SERIAL = new BigInteger[] {
|
private static final String[] BLACKLIST_SHA1 = new String[] {
|
||||||
// CNNIC https://googleonlinesecurity.blogspot.com/2015/03/maintaining-digital-certificate-security.html
|
// CNNIC https://googleonlinesecurity.blogspot.com/2015/03/maintaining-digital-certificate-security.html
|
||||||
new BigInteger("49:33:00:01".replace(":", ""), 16),
|
//new BigInteger("49:33:00:01".replace(":", ""), 16),
|
||||||
|
//"CNNIC ROOT",
|
||||||
|
//null,
|
||||||
|
"8b:af:4c:9b:1d:f0:2a:92:f7:da:12:8e:b9:1b:ac:f4:98:60:4b:6f",
|
||||||
|
|
||||||
// CNNIC EV root https://bugzilla.mozilla.org/show_bug.cgi?id=607208
|
// CNNIC EV root https://bugzilla.mozilla.org/show_bug.cgi?id=607208
|
||||||
new BigInteger("48:9f:00:01".replace(":", ""), 16),
|
//new BigInteger("48:9f:00:01".replace(":", ""), 16),
|
||||||
|
//"China Internet Network Information Center EV Certificates Root",
|
||||||
|
//null,
|
||||||
|
"4f:99:aa:93:fb:2b:d1:37:26:a1:99:4a:ce:7f:f0:05:f2:93:5d:1e",
|
||||||
|
|
||||||
// Superfish http://blog.erratasec.com/2015/02/extracting-superfish-certificate.html
|
// Superfish http://blog.erratasec.com/2015/02/extracting-superfish-certificate.html
|
||||||
new BigInteger("d2:fc:13:87:a9:44:dc:e7".replace(":", ""), 16),
|
//new BigInteger("d2:fc:13:87:a9:44:dc:e7".replace(":", ""), 16),
|
||||||
|
//"Superfish, Inc.",
|
||||||
|
//null,
|
||||||
|
"c8:64:48:48:69:d4:1d:2b:0d:32:31:9c:5a:62:f9:31:5a:af:2c:bd",
|
||||||
|
|
||||||
// eDellRoot https://www.reddit.com/r/technology/comments/3twmfv/dell_ships_laptops_with_rogue_root_ca_exactly/
|
// 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),
|
//new BigInteger("6b:c5:7b:95:18:93:aa:97:4b:62:4a:c0:88:fc:3b:b6".replace(":", ""), 16),
|
||||||
|
//"eDellRoot",
|
||||||
|
//null,
|
||||||
|
"98:a0:4e:41:63:35:77:90:c4:a7:9e:6d:71:3f:f0:af:51:fe:69:27",
|
||||||
|
|
||||||
// DSDTestProvider https://blog.hboeck.de/archives/876-Superfish-2.0-Dangerous-Certificate-on-Dell-Laptops-breaks-encrypted-HTTPS-Connections.html
|
// DSDTestProvider https://blog.hboeck.de/archives/876-Superfish-2.0-Dangerous-Certificate-on-Dell-Laptops-breaks-encrypted-HTTPS-Connections.html
|
||||||
// serial number is actually negative; hex string as reported by certtool below
|
// serial number is actually negative; hex string as reported by certtool below
|
||||||
//new BigInteger("a4:4c:38:47:f8:ee:71:80:43:4d:b1:80:b9:a7:e9:62".replace(":", ""), 16)
|
//new BigInteger("a4:4c:38:47:f8:ee:71:80:43:4d:b1:80:b9:a7:e9:62".replace(":", ""), 16)
|
||||||
new BigInteger("-5b:b3:c7:b8:07:11:8e:7f:bc:b2:4e:7f:46:58:16:9e".replace(":", ""), 16),
|
//new BigInteger("-5b:b3:c7:b8:07:11:8e:7f:bc:b2:4e:7f:46:58:16:9e".replace(":", ""), 16),
|
||||||
|
//"DSDTestProvider",
|
||||||
|
//null,
|
||||||
|
"02:c2:d9:31:06:2d:7b:1d:c2:a5:c7:f5:f0:68:50:64:08:1f:b2:21",
|
||||||
|
|
||||||
// Verisign G1 Roots
|
// Verisign G1 Roots
|
||||||
// https://googleonlinesecurity.blogspot.com/2015/12/proactive-measures-in-digital.html
|
// https://googleonlinesecurity.blogspot.com/2015/12/proactive-measures-in-digital.html
|
||||||
// https://knowledge.symantec.com/support/ssl-certificates-support/index?page=content&id=ALERT1941
|
// https://knowledge.symantec.com/support/ssl-certificates-support/index?page=content&id=ALERT1941
|
||||||
// SHA-1
|
// SHA-1
|
||||||
new BigInteger("3c:91:31:cb:1f:f6:d0:1b:0e:9a:b8:d0:44:bf:12:be".replace(":", ""), 16),
|
//new BigInteger("3c:91:31:cb:1f:f6:d0:1b:0e:9a:b8:d0:44:bf:12:be".replace(":", ""), 16),
|
||||||
|
//null,
|
||||||
|
//"Class 3 Public Primary Certification Authority",
|
||||||
|
"a1:db:63:93:91:6f:17:e4:18:55:09:40:04:15:c7:02:40:b0:ae:6b",
|
||||||
|
|
||||||
// MD2
|
// MD2
|
||||||
new BigInteger("70:ba:e4:1d:10:d9:29:34:b6:38:ca:7b:03:cc:ba:bf".replace(":", ""), 16),
|
//new BigInteger("70:ba:e4:1d:10:d9:29:34:b6:38:ca:7b:03:cc:ba:bf".replace(":", ""), 16),
|
||||||
|
//null,
|
||||||
|
//"Class 3 Public Primary Certification Authority",
|
||||||
|
"74:2c:31:92:e6:07:e4:24:eb:45:49:54:2b:e1:bb:c5:3e:61:74:e2",
|
||||||
|
|
||||||
// Comodo SHA1 https://cabforum.org/pipermail/public/2015-December/006500.html
|
// Comodo SHA1 https://cabforum.org/pipermail/public/2015-December/006500.html
|
||||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1208461
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1208461
|
||||||
new BigInteger("44:be:0c:8b:50:00:21:b4:11:d3:2a:68:06:a9:ad:69".replace(":", ""), 16)
|
//new BigInteger("44:be:0c:8b:50:00:21:b4:11:d3:2a:68:06:a9:ad:69".replace(":", ""), 16)
|
||||||
|
//"UTN - DATACorp SGC"
|
||||||
|
//null
|
||||||
|
"58:11:9f:0e:12:82:87:ea:50:fd:d9:87:45:6f:4f:78:dc:fa:d6:d4"
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
private static final Set<SHA1Hash> _blacklist = new HashSet<SHA1Hash>(16);
|
||||||
* Corresponding issuer CN for the serial number.
|
|
||||||
* Must be same number of entries as BLACKLIST_SERIAL.
|
|
||||||
* Either CN or OU must be non-null
|
|
||||||
*/
|
|
||||||
private static final String[] BLACKLIST_ISSUER_CN = new String[] {
|
|
||||||
"CNNIC ROOT",
|
|
||||||
"China Internet Network Information Center EV Certificates Root",
|
|
||||||
"Superfish, Inc.",
|
|
||||||
"eDellRoot",
|
|
||||||
"DSDTestProvider",
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"UTN - DATACorp SGC"
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Corresponding issuer OU for the serial number.
|
|
||||||
* Must be same number of entries as BLACKLIST_SERIAL.
|
|
||||||
* Either CN or OU must be non-null
|
|
||||||
*/
|
|
||||||
private static final String[] BLACKLIST_ISSUER_OU = new String[] {
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"Class 3 Public Primary Certification Authority",
|
|
||||||
"Class 3 Public Primary Certification Authority",
|
|
||||||
null
|
|
||||||
};
|
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (int i = 0; i < BLACKLIST_SHA1.length; i++) {
|
||||||
|
String s = BLACKLIST_SHA1[i].replace(":", "");
|
||||||
|
BigInteger bi = new BigInteger(s, 16);
|
||||||
|
byte[] b = bi.toByteArray();
|
||||||
|
if (b.length == 21) {
|
||||||
|
byte[] b2 = new byte[20];
|
||||||
|
System.arraycopy(b, 1, b2, 0, 20);
|
||||||
|
b = b2;
|
||||||
|
}
|
||||||
|
SHA1Hash h = new SHA1Hash(b);
|
||||||
|
_blacklist.add(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new KeyStore object, and load it from ksFile if it is
|
* Create a new KeyStore object, and load it from ksFile if it is
|
||||||
@ -261,64 +283,57 @@ public final class KeyStoreUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all blacklisted X509 Certs in a key store.
|
* 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
|
* @return number successfully removed
|
||||||
* @since 0.9.24
|
* @since 0.9.24
|
||||||
*/
|
*/
|
||||||
private static int removeBlacklistedCerts(KeyStore ks) {
|
private static int removeBlacklistedCerts(KeyStore ks) {
|
||||||
// This matches on the CN or OU in the issuer,
|
|
||||||
// and we can't do that on Android.
|
|
||||||
// Alternative is sha1hash(cert.getEncoded()) but that would be slower,
|
|
||||||
// unless the blacklist gets a little longer.
|
|
||||||
if (SystemVersion.isAndroid())
|
if (SystemVersion.isAndroid())
|
||||||
return 0;
|
return 0;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
try {
|
try {
|
||||||
|
MessageDigest md = SHA1.getInstance();
|
||||||
for(Enumeration<String> e = ks.aliases(); e.hasMoreElements();) {
|
for(Enumeration<String> e = ks.aliases(); e.hasMoreElements();) {
|
||||||
String alias = e.nextElement();
|
String alias = e.nextElement();
|
||||||
if (ks.isCertificateEntry(alias)) {
|
if (ks.isCertificateEntry(alias)) {
|
||||||
Certificate c = ks.getCertificate(alias);
|
Certificate c = ks.getCertificate(alias);
|
||||||
if (c != null && (c instanceof X509Certificate)) {
|
if (c != null && (c instanceof X509Certificate)) {
|
||||||
X509Certificate xc = (X509Certificate) c;
|
//X509Certificate xc = (X509Certificate) c;
|
||||||
BigInteger serial = xc.getSerialNumber();
|
//BigInteger serial = xc.getSerialNumber();
|
||||||
// debug:
|
// debug:
|
||||||
//String xname = CertUtil.getIssuerValue(xc, "CN");
|
//String xname = CertUtil.getIssuerValue(xc, "CN");
|
||||||
//info("Found \"" + xname + "\" s/n: " + serial.toString(16));
|
//info("Found \"" + xname + "\" s/n: " + serial.toString(16));
|
||||||
//if (xname == null)
|
//if (xname == null)
|
||||||
// info("name is null, full issuer: " + xc.getIssuerX500Principal().getName());
|
// info("name is null, full issuer: " + xc.getIssuerX500Principal().getName());
|
||||||
for (int i = 0; i < BLACKLIST_SERIAL.length; i++) {
|
byte[] enc = c.getEncoded();
|
||||||
if (BLACKLIST_SERIAL[i].equals(serial)) {
|
if (enc != null) {
|
||||||
if (BLACKLIST_ISSUER_CN[i] != null) {
|
byte[] h = md.digest(enc);
|
||||||
String name = CertUtil.getIssuerValue(xc, "CN");
|
//StringBuilder buf = new StringBuilder(60);
|
||||||
if (BLACKLIST_ISSUER_CN[i].equals(name)) {
|
//String hex = DataHelper.toString(h);
|
||||||
ks.deleteEntry(alias);
|
//for (int i = 0; i < hex.length(); i += 2) {
|
||||||
count++;
|
// buf.append(hex.charAt(i));
|
||||||
if (!_blacklistLogged) {
|
// buf.append(hex.charAt(i+1));
|
||||||
// should this be a logAlways?
|
// if (i < hex.length() - 2)
|
||||||
warn("Ignoring blacklisted certificate \"" + alias +
|
// buf.append(':');
|
||||||
"\" CN: \"" + name +
|
//}
|
||||||
"\" s/n: " + serial.toString(16), null);
|
//info("hex is: " + buf);
|
||||||
}
|
if (_blacklist.contains(new SHA1Hash(h))) {
|
||||||
break;
|
ks.deleteEntry(alias);
|
||||||
}
|
count++;
|
||||||
}
|
if (!_blacklistLogged) {
|
||||||
if (BLACKLIST_ISSUER_OU[i] != null) {
|
// should this be a logAlways?
|
||||||
String name = CertUtil.getIssuerValue(xc, "OU");
|
X509Certificate xc = (X509Certificate) c;
|
||||||
if (BLACKLIST_ISSUER_OU[i].equals(name)) {
|
BigInteger serial = xc.getSerialNumber();
|
||||||
ks.deleteEntry(alias);
|
String cn = CertUtil.getIssuerValue(xc, "CN");
|
||||||
count++;
|
String ou = CertUtil.getIssuerValue(xc, "OU");
|
||||||
if (!_blacklistLogged) {
|
warn("Ignoring blacklisted certificate \"" + alias +
|
||||||
// should this be a logAlways?
|
"\" CN: \"" + cn +
|
||||||
warn("Ignoring blacklisted certificate \"" + alias +
|
"\" OU: \"" + ou +
|
||||||
"\" OU: \"" + name +
|
"\" s/n: " + serial.toString(16), null);
|
||||||
"\" s/n: " + serial.toString(16), null);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
info("null encoding!!!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1000,8 +1015,8 @@ public final class KeyStoreUtil {
|
|||||||
|
|
||||||
private static void testKeygen2(String[] args) throws Exception {
|
private static void testKeygen2(String[] args) throws Exception {
|
||||||
// keygen test using the I2PProvider
|
// keygen test using the I2PProvider
|
||||||
//SigType type = SigType.EdDSA_SHA512_Ed25519;
|
SigType type = SigType.EdDSA_SHA512_Ed25519;
|
||||||
SigType type = SigType.ElGamal_SHA256_MODP2048;
|
//SigType type = SigType.ElGamal_SHA256_MODP2048;
|
||||||
java.security.KeyPairGenerator kpg = java.security.KeyPairGenerator.getInstance(type.getBaseAlgorithm().getName());
|
java.security.KeyPairGenerator kpg = java.security.KeyPairGenerator.getInstance(type.getBaseAlgorithm().getName());
|
||||||
kpg.initialize(type.getParams());
|
kpg.initialize(type.getParams());
|
||||||
java.security.KeyPair kp = kpg.generateKeyPair();
|
java.security.KeyPair kp = kpg.generateKeyPair();
|
||||||
|
Reference in New Issue
Block a user