• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
38     private static final String ANDROID_DATA = System.getenv("ANDROID_DATA");
39     private static final String BLACKLIST_ROOT = ANDROID_DATA + "/misc/keychain/";
40     public static final String DEFAULT_PUBKEY_BLACKLIST_PATH = BLACKLIST_ROOT + "pubkey_blacklist.txt";
41     public static final String DEFAULT_SERIAL_BLACKLIST_PATH = BLACKLIST_ROOT + "serial_blacklist.txt";
42 
43     private static final Logger logger = Logger.getLogger(CertBlacklist.class.getName());
44 
45     // public for testing
46     public final Set<BigInteger> serialBlacklist;
47     public final Set<byte[]> pubkeyBlacklist;
48 
CertBlacklist()49     public CertBlacklist() {
50         this(DEFAULT_PUBKEY_BLACKLIST_PATH, DEFAULT_SERIAL_BLACKLIST_PATH);
51     }
52 
53     /** Test only interface, not for public use */
CertBlacklist(String pubkeyBlacklistPath, String serialBlacklistPath)54     public CertBlacklist(String pubkeyBlacklistPath, String serialBlacklistPath) {
55         serialBlacklist = readSerialBlackList(serialBlacklistPath);
56         pubkeyBlacklist = readPublicKeyBlackList(pubkeyBlacklistPath);
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         Set<BigInteger> bl = new HashSet<BigInteger>(Arrays.asList(
127             // From http://src.chromium.org/viewvc/chrome/trunk/src/net/base/x509_certificate.cc?revision=78748&view=markup
128             // Not a real certificate. For testing only.
129             new BigInteger("077a59bcd53459601ca6907267a6dd1c", 16),
130             new BigInteger("047ecbe9fca55f7bd09eae36e10cae1e", 16),
131             new BigInteger("d8f35f4eb7872b2dab0692e315382fb0", 16),
132             new BigInteger("b0b7133ed096f9b56fae91c874bd3ac0", 16),
133             new BigInteger("9239d5348f40d1695a745470e1f23f43", 16),
134             new BigInteger("e9028b9578e415dc1a710a2b88154447", 16),
135             new BigInteger("d7558fdaf5f1105bb213282b707729a3", 16),
136             new BigInteger("f5c86af36162f13a64f54f6dc9587c06", 16),
137             new BigInteger("392a434f0e07df1f8aa305de34e0c229", 16),
138             new BigInteger("3e75ced46b693021218830ae86a82a71", 16),
139             new BigInteger("864", 16),
140             new BigInteger("827", 16),
141             new BigInteger("31da7", 16)
142         ));
143 
144         // attempt to augment it with values taken from gservices
145         String serialBlacklist = readBlacklist(path);
146         if (!serialBlacklist.equals("")) {
147             for(String value : serialBlacklist.split(",")) {
148                 try {
149                     bl.add(new BigInteger(value, 16));
150                 } catch (NumberFormatException e) {
151                     logger.log(Level.WARNING, "Tried to blacklist invalid serial number " + value, e);
152                 }
153             }
154         }
155 
156         // whether that succeeds or fails, send it on its merry way
157         return Collections.unmodifiableSet(bl);
158     }
159 
readPublicKeyBlackList(String path)160     private static final Set<byte[]> readPublicKeyBlackList(String path) {
161 
162         // start out with a base set of known bad values
163         Set<byte[]> bl = new HashSet<byte[]>(Arrays.asList(
164             // From http://src.chromium.org/viewvc/chrome/branches/782/src/net/base/x509_certificate.cc?r1=98750&r2=98749&pathrev=98750
165             // C=NL, O=DigiNotar, CN=DigiNotar Root CA/emailAddress=info@diginotar.nl
166             "410f36363258f30b347d12ce4863e433437806a8".getBytes(),
167             // Subject: CN=DigiNotar Cyber CA
168             // Issuer: CN=GTE CyberTrust Global Root
169             "ba3e7bd38cd7e1e6b9cd4c219962e59d7a2f4e37".getBytes(),
170             // Subject: CN=DigiNotar Services 1024 CA
171             // Issuer: CN=Entrust.net
172             "e23b8d105f87710a68d9248050ebefc627be4ca6".getBytes(),
173             // Subject: CN=DigiNotar PKIoverheid CA Organisatie - G2
174             // Issuer: CN=Staat der Nederlanden Organisatie CA - G2
175             "7b2e16bc39bcd72b456e9f055d1de615b74945db".getBytes(),
176             // Subject: CN=DigiNotar PKIoverheid CA Overheid en Bedrijven
177             // Issuer: CN=Staat der Nederlanden Overheid CA
178             "e8f91200c65cee16e039b9f883841661635f81c5".getBytes(),
179             // From http://src.chromium.org/viewvc/chrome?view=rev&revision=108479
180             // Subject: O=Digicert Sdn. Bhd.
181             // Issuer: CN=GTE CyberTrust Global Root
182             "0129bcd5b448ae8d2496d1c3e19723919088e152".getBytes(),
183             // Subject: CN=e-islem.kktcmerkezbankasi.org/emailAddress=ileti@kktcmerkezbankasi.org
184             // Issuer: CN=T\xC3\x9CRKTRUST Elektronik Sunucu Sertifikas\xC4\xB1 Hizmetleri
185             "5f3ab33d55007054bc5e3e5553cd8d8465d77c61".getBytes(),
186             // Subject: CN=*.EGO.GOV.TR 93
187             // Issuer: CN=T\xC3\x9CRKTRUST Elektronik Sunucu Sertifikas\xC4\xB1 Hizmetleri
188             "783333c9687df63377efceddd82efa9101913e8e".getBytes(),
189             // Subject: Subject: C=FR, O=DG Tr\xC3\xA9sor, CN=AC DG Tr\xC3\xA9sor SSL
190             // Issuer: C=FR, O=DGTPE, CN=AC DGTPE Signature Authentification
191             "3ecf4bbbe46096d514bb539bb913d77aa4ef31bf".getBytes()
192         ));
193 
194         // attempt to augment it with values taken from gservices
195         String pubkeyBlacklist = readBlacklist(path);
196         if (!pubkeyBlacklist.equals("")) {
197             for (String value : pubkeyBlacklist.split(",")) {
198                 value = value.trim();
199                 if (isPubkeyHash(value)) {
200                     bl.add(value.getBytes());
201                 } else {
202                     logger.log(Level.WARNING, "Tried to blacklist invalid pubkey " + value);
203                 }
204             }
205         }
206 
207         return bl;
208     }
209 
isPublicKeyBlackListed(PublicKey publicKey)210     public boolean isPublicKeyBlackListed(PublicKey publicKey) {
211         byte[] encoded = publicKey.getEncoded();
212         Digest digest = AndroidDigestFactory.getSHA1();
213         digest.update(encoded, 0, encoded.length);
214         byte[] out = new byte[digest.getDigestSize()];
215         digest.doFinal(out, 0);
216         for (byte[] blacklisted : pubkeyBlacklist) {
217             if (Arrays.equals(blacklisted, Hex.encode(out))) {
218                 return true;
219             }
220         }
221         return false;
222     }
223 
isSerialNumberBlackListed(BigInteger serial)224     public boolean isSerialNumberBlackListed(BigInteger serial) {
225         return serialBlacklist.contains(serial);
226     }
227 
228 }
229