1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.bouncycastle.jce.provider; 18 19 import java.io.Closeable; 20 import java.io.ByteArrayOutputStream; 21 import java.io.FileNotFoundException; 22 import java.io.IOException; 23 import java.io.RandomAccessFile; 24 import java.math.BigInteger; 25 import java.security.PublicKey; 26 import java.util.Arrays; 27 import java.util.Collections; 28 import java.util.HashSet; 29 import java.util.Set; 30 import java.util.logging.Level; 31 import java.util.logging.Logger; 32 import org.bouncycastle.crypto.Digest; 33 import org.bouncycastle.crypto.digests.AndroidDigestFactory; 34 import org.bouncycastle.util.encoders.Hex; 35 36 public class CertBlacklist { 37 private static final Logger logger = Logger.getLogger(CertBlacklist.class.getName()); 38 39 // public for testing 40 public final Set<BigInteger> serialBlacklist; 41 public final Set<byte[]> pubkeyBlacklist; 42 CertBlacklist()43 public CertBlacklist() { 44 String androidData = System.getenv("ANDROID_DATA"); 45 String blacklistRoot = androidData + "/misc/keychain/"; 46 String defaultPubkeyBlacklistPath = blacklistRoot + "pubkey_blacklist.txt"; 47 String defaultSerialBlacklistPath = blacklistRoot + "serial_blacklist.txt"; 48 49 pubkeyBlacklist = readPublicKeyBlackList(defaultPubkeyBlacklistPath); 50 serialBlacklist = readSerialBlackList(defaultSerialBlacklistPath); 51 } 52 53 /** Test only interface, not for public use */ CertBlacklist(String pubkeyBlacklistPath, String serialBlacklistPath)54 public CertBlacklist(String pubkeyBlacklistPath, String serialBlacklistPath) { 55 pubkeyBlacklist = readPublicKeyBlackList(pubkeyBlacklistPath); 56 serialBlacklist = readSerialBlackList(serialBlacklistPath); 57 } 58 isHex(String value)59 private static boolean isHex(String value) { 60 try { 61 new BigInteger(value, 16); 62 return true; 63 } catch (NumberFormatException e) { 64 logger.log(Level.WARNING, "Could not parse hex value " + value, e); 65 return false; 66 } 67 } 68 isPubkeyHash(String value)69 private static boolean isPubkeyHash(String value) { 70 if (value.length() != 40) { 71 logger.log(Level.WARNING, "Invalid pubkey hash length: " + value.length()); 72 return false; 73 } 74 return isHex(value); 75 } 76 readBlacklist(String path)77 private static String readBlacklist(String path) { 78 try { 79 return readFileAsString(path); 80 } catch (FileNotFoundException ignored) { 81 } catch (IOException e) { 82 logger.log(Level.WARNING, "Could not read blacklist", e); 83 } 84 return ""; 85 } 86 87 // From IoUtils.readFileAsString readFileAsString(String path)88 private static String readFileAsString(String path) throws IOException { 89 return readFileAsBytes(path).toString("UTF-8"); 90 } 91 92 // Based on IoUtils.readFileAsBytes readFileAsBytes(String path)93 private static ByteArrayOutputStream readFileAsBytes(String path) throws IOException { 94 RandomAccessFile f = null; 95 try { 96 f = new RandomAccessFile(path, "r"); 97 ByteArrayOutputStream bytes = new ByteArrayOutputStream((int) f.length()); 98 byte[] buffer = new byte[8192]; 99 while (true) { 100 int byteCount = f.read(buffer); 101 if (byteCount == -1) { 102 return bytes; 103 } 104 bytes.write(buffer, 0, byteCount); 105 } 106 } finally { 107 closeQuietly(f); 108 } 109 } 110 111 // Base on IoUtils.closeQuietly closeQuietly(Closeable closeable)112 private static void closeQuietly(Closeable closeable) { 113 if (closeable != null) { 114 try { 115 closeable.close(); 116 } catch (RuntimeException rethrown) { 117 throw rethrown; 118 } catch (Exception ignored) { 119 } 120 } 121 } 122 readSerialBlackList(String path)123 private static final Set<BigInteger> readSerialBlackList(String path) { 124 125 /* Start out with a base set of known bad values. 126 * 127 * WARNING: Do not add short serials to this list! 128 * 129 * Since this currently doesn't compare the serial + issuer, you 130 * should only add serials that have enough entropy here. Short 131 * serials may inadvertently match a certificate that was issued 132 * not in compliance with the Baseline Requirements. 133 */ 134 Set<BigInteger> bl = new HashSet<BigInteger>(Arrays.asList( 135 // From http://src.chromium.org/viewvc/chrome/trunk/src/net/base/x509_certificate.cc?revision=78748&view=markup 136 // Not a real certificate. For testing only. 137 new BigInteger("077a59bcd53459601ca6907267a6dd1c", 16), 138 new BigInteger("047ecbe9fca55f7bd09eae36e10cae1e", 16), 139 new BigInteger("d8f35f4eb7872b2dab0692e315382fb0", 16), 140 new BigInteger("b0b7133ed096f9b56fae91c874bd3ac0", 16), 141 new BigInteger("9239d5348f40d1695a745470e1f23f43", 16), 142 new BigInteger("e9028b9578e415dc1a710a2b88154447", 16), 143 new BigInteger("d7558fdaf5f1105bb213282b707729a3", 16), 144 new BigInteger("f5c86af36162f13a64f54f6dc9587c06", 16), 145 new BigInteger("392a434f0e07df1f8aa305de34e0c229", 16), 146 new BigInteger("3e75ced46b693021218830ae86a82a71", 16) 147 )); 148 149 // attempt to augment it with values taken from gservices 150 String serialBlacklist = readBlacklist(path); 151 if (!serialBlacklist.equals("")) { 152 for(String value : serialBlacklist.split(",")) { 153 try { 154 bl.add(new BigInteger(value, 16)); 155 } catch (NumberFormatException e) { 156 logger.log(Level.WARNING, "Tried to blacklist invalid serial number " + value, e); 157 } 158 } 159 } 160 161 // whether that succeeds or fails, send it on its merry way 162 return Collections.unmodifiableSet(bl); 163 } 164 readPublicKeyBlackList(String path)165 private static final Set<byte[]> readPublicKeyBlackList(String path) { 166 167 // start out with a base set of known bad values 168 Set<byte[]> bl = new HashSet<byte[]>(Arrays.asList( 169 // From http://src.chromium.org/viewvc/chrome/branches/782/src/net/base/x509_certificate.cc?r1=98750&r2=98749&pathrev=98750 170 // C=NL, O=DigiNotar, CN=DigiNotar Root CA/emailAddress=info@diginotar.nl 171 "410f36363258f30b347d12ce4863e433437806a8".getBytes(), 172 // Subject: CN=DigiNotar Cyber CA 173 // Issuer: CN=GTE CyberTrust Global Root 174 "ba3e7bd38cd7e1e6b9cd4c219962e59d7a2f4e37".getBytes(), 175 // Subject: CN=DigiNotar Services 1024 CA 176 // Issuer: CN=Entrust.net 177 "e23b8d105f87710a68d9248050ebefc627be4ca6".getBytes(), 178 // Subject: CN=DigiNotar PKIoverheid CA Organisatie - G2 179 // Issuer: CN=Staat der Nederlanden Organisatie CA - G2 180 "7b2e16bc39bcd72b456e9f055d1de615b74945db".getBytes(), 181 // Subject: CN=DigiNotar PKIoverheid CA Overheid en Bedrijven 182 // Issuer: CN=Staat der Nederlanden Overheid CA 183 "e8f91200c65cee16e039b9f883841661635f81c5".getBytes(), 184 // From http://src.chromium.org/viewvc/chrome?view=rev&revision=108479 185 // Subject: O=Digicert Sdn. Bhd. 186 // Issuer: CN=GTE CyberTrust Global Root 187 "0129bcd5b448ae8d2496d1c3e19723919088e152".getBytes(), 188 // Subject: CN=e-islem.kktcmerkezbankasi.org/emailAddress=ileti@kktcmerkezbankasi.org 189 // Issuer: CN=T\xC3\x9CRKTRUST Elektronik Sunucu Sertifikas\xC4\xB1 Hizmetleri 190 "5f3ab33d55007054bc5e3e5553cd8d8465d77c61".getBytes(), 191 // Subject: CN=*.EGO.GOV.TR 93 192 // Issuer: CN=T\xC3\x9CRKTRUST Elektronik Sunucu Sertifikas\xC4\xB1 Hizmetleri 193 "783333c9687df63377efceddd82efa9101913e8e".getBytes(), 194 // Subject: Subject: C=FR, O=DG Tr\xC3\xA9sor, CN=AC DG Tr\xC3\xA9sor SSL 195 // Issuer: C=FR, O=DGTPE, CN=AC DGTPE Signature Authentification 196 "3ecf4bbbe46096d514bb539bb913d77aa4ef31bf".getBytes() 197 )); 198 199 // attempt to augment it with values taken from gservices 200 String pubkeyBlacklist = readBlacklist(path); 201 if (!pubkeyBlacklist.equals("")) { 202 for (String value : pubkeyBlacklist.split(",")) { 203 value = value.trim(); 204 if (isPubkeyHash(value)) { 205 bl.add(value.getBytes()); 206 } else { 207 logger.log(Level.WARNING, "Tried to blacklist invalid pubkey " + value); 208 } 209 } 210 } 211 212 return bl; 213 } 214 isPublicKeyBlackListed(PublicKey publicKey)215 public boolean isPublicKeyBlackListed(PublicKey publicKey) { 216 byte[] encoded = publicKey.getEncoded(); 217 Digest digest = AndroidDigestFactory.getSHA1(); 218 digest.update(encoded, 0, encoded.length); 219 byte[] out = new byte[digest.getDigestSize()]; 220 digest.doFinal(out, 0); 221 for (byte[] blacklisted : pubkeyBlacklist) { 222 if (Arrays.equals(blacklisted, Hex.encode(out))) { 223 return true; 224 } 225 } 226 return false; 227 } 228 isSerialNumberBlackListed(BigInteger serial)229 public boolean isSerialNumberBlackListed(BigInteger serial) { 230 return serialBlacklist.contains(serial); 231 } 232 233 } 234