1f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar/* 2f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar * Copyright (C) 2015 The Android Open Source Project 3f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar * 4f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar * Licensed under the Apache License, Version 2.0 (the "License"); 5f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar * you may not use this file except in compliance with the License. 6f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar * You may obtain a copy of the License at 7f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar * 8f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar * http://www.apache.org/licenses/LICENSE-2.0 9f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar * 10f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar * Unless required by applicable law or agreed to in writing, software 11f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar * distributed under the License is distributed on an "AS IS" BASIS, 12f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar * See the License for the specific language governing permissions and 14f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar * limitations under the License. 15f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar */ 16f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar 17f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietarpackage org.conscrypt.ct; 18f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar 192558aec59ff6708b7e39cd9890db217cf9c043efNathan Mittlerimport java.io.ByteArrayInputStream; 202693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietarimport java.io.File; 212693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietarimport java.io.FileInputStream; 222693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietarimport java.io.FileNotFoundException; 23d0687b8c0d5aa6e88853fff774afd24ec331964cKenny Rootimport java.io.InputStream; 242693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietarimport java.nio.ByteBuffer; 252558aec59ff6708b7e39cd9890db217cf9c043efNathan Mittlerimport java.nio.charset.Charset; 262693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietarimport java.security.InvalidKeyException; 27f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietarimport java.security.NoSuchAlgorithmException; 28d0687b8c0d5aa6e88853fff774afd24ec331964cKenny Rootimport java.security.PublicKey; 29f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietarimport java.util.Arrays; 302693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietarimport java.util.Collections; 31bea3563621a1b743812e387b3783b070fb9f9fbbKenny Rootimport java.util.HashMap; 322693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietarimport java.util.HashSet; 332693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietarimport java.util.Scanner; 34d0687b8c0d5aa6e88853fff774afd24ec331964cKenny Rootimport java.util.Set; 3529916ef38dc9cb4e4c6e3fdb87d4e921546d3ef4Nathan Mittlerimport org.conscrypt.Internal; 3629916ef38dc9cb4e4c6e3fdb87d4e921546d3ef4Nathan Mittlerimport org.conscrypt.InternalUtil; 37f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar 3829916ef38dc9cb4e4c6e3fdb87d4e921546d3ef4Nathan Mittler/** 3929916ef38dc9cb4e4c6e3fdb87d4e921546d3ef4Nathan Mittler * @hide 4029916ef38dc9cb4e4c6e3fdb87d4e921546d3ef4Nathan Mittler */ 4129916ef38dc9cb4e4c6e3fdb87d4e921546d3ef4Nathan Mittler@Internal 42f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietarpublic class CTLogStoreImpl implements CTLogStore { 432558aec59ff6708b7e39cd9890db217cf9c043efNathan Mittler private static final Charset US_ASCII = Charset.forName("US-ASCII"); 442558aec59ff6708b7e39cd9890db217cf9c043efNathan Mittler 452693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar /** 462693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar * Thrown when parsing of a log file fails. 472693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar */ 482693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar public static class InvalidLogFileException extends Exception { 492693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar public InvalidLogFileException() { 502693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 512693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 522693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar public InvalidLogFileException(String message) { 532693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar super(message); 542693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 552693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 562693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar public InvalidLogFileException(String message, Throwable cause) { 572693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar super(message, cause); 582693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 592693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 602693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar public InvalidLogFileException(Throwable cause) { 612693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar super(cause); 622693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 632693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 642693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 652693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar private static final File defaultUserLogDir; 662693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar private static final File defaultSystemLogDir; 672693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar // Lazy loaded by CTLogStoreImpl() 682693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar private static volatile CTLogInfo[] defaultFallbackLogs = null; 692693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar static { 702693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar String ANDROID_DATA = System.getenv("ANDROID_DATA"); 712693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar String ANDROID_ROOT = System.getenv("ANDROID_ROOT"); 72518ca8c6c8d88daa55d4ea0d0ca83a4654bd1071Chad Brubaker defaultUserLogDir = new File(ANDROID_DATA + "/misc/keychain/trusted_ct_logs/current/"); 732693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar defaultSystemLogDir = new File(ANDROID_ROOT + "/etc/security/ct_known_logs/"); 742693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 752693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 76ca253dc11efbb040c39eaa49ef9e06c344d7e9f2Kenny Root private File userLogDir; 77ca253dc11efbb040c39eaa49ef9e06c344d7e9f2Kenny Root private File systemLogDir; 78ca253dc11efbb040c39eaa49ef9e06c344d7e9f2Kenny Root private CTLogInfo[] fallbackLogs; 792693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 80bea3563621a1b743812e387b3783b070fb9f9fbbKenny Root private HashMap<ByteBuffer, CTLogInfo> logCache = new HashMap<>(); 81bea3563621a1b743812e387b3783b070fb9f9fbbKenny Root private Set<ByteBuffer> missingLogCache = Collections.synchronizedSet(new HashSet<ByteBuffer>()); 822693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 832693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar public CTLogStoreImpl() { 842693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar this(defaultUserLogDir, 852693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar defaultSystemLogDir, 862693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar getDefaultFallbackLogs()); 872693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 882693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 892693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar public CTLogStoreImpl(File userLogDir, File systemLogDir, CTLogInfo[] fallbackLogs) { 902693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar this.userLogDir = userLogDir; 912693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar this.systemLogDir = systemLogDir; 922693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar this.fallbackLogs = fallbackLogs; 932693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 94f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar 95f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar @Override 96f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar public CTLogInfo getKnownLog(byte[] logId) { 972693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar ByteBuffer buf = ByteBuffer.wrap(logId); 982693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar CTLogInfo log = logCache.get(buf); 992693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar if (log != null) { 1002693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar return log; 1012693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 1022693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar if (missingLogCache.contains(buf)) { 1032693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar return null; 1042693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 1052693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 1062693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar log = findKnownLog(logId); 1072693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar if (log != null) { 1082693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar logCache.put(buf, log); 1092693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } else { 1102693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar missingLogCache.add(buf); 111f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar } 1122693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 1132693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar return log; 1142693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 1152693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 1162693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar private CTLogInfo findKnownLog(byte[] logId) { 1172693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar String filename = hexEncode(logId); 1182693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar try { 1192693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar return loadLog(new File(userLogDir, filename)); 1202693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } catch (InvalidLogFileException e) { 1212693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar return null; 1222693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } catch (FileNotFoundException e) {} 1232693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 1242693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar try { 1252693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar return loadLog(new File(systemLogDir, filename)); 1262693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } catch (InvalidLogFileException e) { 1272693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar return null; 1282693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } catch (FileNotFoundException e) {} 1292693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 130518ca8c6c8d88daa55d4ea0d0ca83a4654bd1071Chad Brubaker // If the updateable logs dont exist then use the fallback logs. 131518ca8c6c8d88daa55d4ea0d0ca83a4654bd1071Chad Brubaker if (!userLogDir.exists()) { 132518ca8c6c8d88daa55d4ea0d0ca83a4654bd1071Chad Brubaker for (CTLogInfo log: fallbackLogs) { 133518ca8c6c8d88daa55d4ea0d0ca83a4654bd1071Chad Brubaker if (Arrays.equals(logId, log.getID())) { 134518ca8c6c8d88daa55d4ea0d0ca83a4654bd1071Chad Brubaker return log; 135518ca8c6c8d88daa55d4ea0d0ca83a4654bd1071Chad Brubaker } 136f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar } 137f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar } 138f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar return null; 139f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar } 140f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar 1412693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar public static CTLogInfo[] getDefaultFallbackLogs() { 1422693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar CTLogInfo[] result = defaultFallbackLogs; 1432693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar if (result == null) { 1442693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar // single-check idiom 1452693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar defaultFallbackLogs = result = createDefaultFallbackLogs(); 1462693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 1472693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar return result; 1482693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 1492693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 1502693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar private static CTLogInfo[] createDefaultFallbackLogs() { 1514dc3e45f010e805b5b7fb9ac2e40cc05244209b0Kenny Root CTLogInfo[] logs = new CTLogInfo[KnownLogs.LOG_COUNT]; 152f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar for (int i = 0; i < KnownLogs.LOG_COUNT; i++) { 153f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar try { 15429916ef38dc9cb4e4c6e3fdb87d4e921546d3ef4Nathan Mittler PublicKey key = InternalUtil.logKeyToPublicKey(KnownLogs.LOG_KEYS[i]); 155f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar 156f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar logs[i] = new CTLogInfo(key, 157f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar KnownLogs.LOG_DESCRIPTIONS[i], 158f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar KnownLogs.LOG_URLS[i]); 159f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar } catch (NoSuchAlgorithmException e) { 160f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar throw new RuntimeException(e); 161f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar } 162f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar } 1632693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 1642693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar defaultFallbackLogs = logs; 165f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar return logs; 166f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar } 1672693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 1682693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar /** 1692693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar * Load a CTLogInfo from a file. 1702693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar * @throws FileNotFoundException if the file does not exist 1712693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar * @throws InvalidLogFileException if the file could not be parsed properly 1722693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar * @return a CTLogInfo or null if the file is empty 1732693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar */ 1742693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar public static CTLogInfo loadLog(File file) throws FileNotFoundException, 1752693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar InvalidLogFileException { 1762693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar return loadLog(new FileInputStream(file)); 1772693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 1782693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 1792693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar /** 180aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root * Load a CTLogInfo from a textual representation. Closes {@code input} upon completion 181aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root * of loading. 182aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root * 1832693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar * @throws InvalidLogFileException if the input could not be parsed properly 1842693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar * @return a CTLogInfo or null if the input is empty 1852693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar */ 1862693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar public static CTLogInfo loadLog(InputStream input) throws InvalidLogFileException { 187aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root final Scanner scan = new Scanner(input, "UTF-8"); 188aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root scan.useDelimiter("\n"); 1892693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 190aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root String description = null; 191aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root String url = null; 192aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root String key = null; 193aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root try { 194aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root // If the scanner can't even read one token then the file must be empty/blank 195aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root if (!scan.hasNext()) { 196aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root return null; 1972693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 1982693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 199aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root while (scan.hasNext()) { 200aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root String[] parts = scan.next().split(":", 2); 201aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root if (parts.length < 2) { 202aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root continue; 203aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root } 204aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root 205aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root String name = parts[0]; 206aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root String value = parts[1]; 207aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root switch (name) { 208aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root case "description": 209aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root description = value; 210aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root break; 211aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root case "url": 212aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root url = value; 213aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root break; 214aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root case "key": 215aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root key = value; 216aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root break; 217aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root } 2182693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 219aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root } finally { 220aab5adf03c8d67b19e43cdf2d7a76672032411f7Kenny Root scan.close(); 2212693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 2222693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 2232693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar if (description == null || url == null || key == null) { 2242693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar throw new InvalidLogFileException("Missing one of 'description', 'url' or 'key'"); 2252693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 2262693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 2272693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar PublicKey pubkey; 2282693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar try { 2292558aec59ff6708b7e39cd9890db217cf9c043efNathan Mittler pubkey = InternalUtil.readPublicKeyPem(new ByteArrayInputStream( 2302558aec59ff6708b7e39cd9890db217cf9c043efNathan Mittler ("-----BEGIN PUBLIC KEY-----\n" + 2312693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar key + "\n" + 2322558aec59ff6708b7e39cd9890db217cf9c043efNathan Mittler "-----END PUBLIC KEY-----").getBytes(US_ASCII))); 2332693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } catch (InvalidKeyException e) { 2342693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar throw new InvalidLogFileException(e); 2352693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } catch (NoSuchAlgorithmException e) { 2362693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar throw new InvalidLogFileException(e); 2372693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 2382693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 2392693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar return new CTLogInfo(pubkey, description, url); 2402693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 2412693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 2422693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar private final static char[] HEX_DIGITS = new char[] { 2432693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar '0', '1', '2', '3', '4', '5', '6', '7', 2442693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 2452693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar }; 2462693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar 2472693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar private static String hexEncode(byte[] data) { 2482558aec59ff6708b7e39cd9890db217cf9c043efNathan Mittler StringBuilder sb = new StringBuilder(data.length * 2); 2492693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar for (byte b: data) { 2502693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar sb.append(HEX_DIGITS[(b >> 4) & 0x0f]); 2512693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar sb.append(HEX_DIGITS[b & 0x0f]); 2522693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 2532693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar return sb.toString(); 2542693d203bf976ff2c5167df13e382d4e10024ad7Paul Lietar } 255f714bf65e491155e7837ddb3242e3ee6be173943Paul Lietar} 256