| /* |
| * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package build.tools.blacklistedcertsconverter; |
| |
| import java.io.IOException; |
| import java.math.BigInteger; |
| import java.security.MessageDigest; |
| import java.security.PublicKey; |
| import java.security.cert.Certificate; |
| import java.security.cert.CertificateFactory; |
| import java.security.cert.X509Certificate; |
| import java.security.interfaces.ECPublicKey; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.TreeSet; |
| |
| import sun.security.util.DerInputStream; |
| import sun.security.util.DerOutputStream; |
| import sun.security.util.DerValue; |
| |
| /** |
| * Converts blacklisted.certs.pem from System.in to blacklisted.certs in |
| * System.out. The input must start with a #! line including the fingerprint |
| * algorithm. The output is sorted and unique. |
| */ |
| public class BlacklistedCertsConverter { |
| |
| public static void main(String[] args) throws Exception { |
| |
| byte[] pattern = "#! java BlacklistedCertsConverter ".getBytes(); |
| String mdAlg = ""; |
| |
| for (int i=0; ; i++) { |
| int n = System.in.read(); |
| if (n < 0) { |
| throw new Exception("Unexpected EOF"); |
| } |
| if (i < pattern.length) { |
| if (n != pattern[i]) { |
| throw new Exception("The first line must start with \"" |
| + new String(pattern) + "\""); |
| } |
| } else if (i < pattern.length + 100) { |
| if (n < 32) { |
| break; |
| } else { |
| mdAlg = mdAlg + String.format("%c", n); |
| } |
| } |
| } |
| |
| mdAlg = mdAlg.trim(); |
| System.out.println("Algorithm=" + mdAlg); |
| |
| CertificateFactory cf = CertificateFactory.getInstance("X.509"); |
| Collection<? extends Certificate> certs |
| = cf.generateCertificates(System.in); |
| |
| // Output sorted so that it's easy to locate an entry. |
| Set<String> fingerprints = new TreeSet<>(); |
| for (Certificate cert: certs) { |
| fingerprints.addAll( |
| getCertificateFingerPrints(mdAlg, (X509Certificate)cert)); |
| } |
| |
| for (String s: fingerprints) { |
| System.out.println(s); |
| } |
| } |
| |
| /** |
| * Converts a byte to hex digit and writes to the supplied buffer |
| */ |
| private static void byte2hex(byte b, StringBuffer buf) { |
| char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', |
| '9', 'A', 'B', 'C', 'D', 'E', 'F' }; |
| int high = ((b & 0xf0) >> 4); |
| int low = (b & 0x0f); |
| buf.append(hexChars[high]); |
| buf.append(hexChars[low]); |
| } |
| |
| /** |
| * Computes the possible fingerprints of the certificate. |
| */ |
| private static List<String> getCertificateFingerPrints( |
| String mdAlg, X509Certificate cert) throws Exception { |
| List<String> fingerprints = new ArrayList<>(); |
| for (byte[] encoding : altEncodings(cert)) { |
| MessageDigest md = MessageDigest.getInstance(mdAlg); |
| byte[] digest = md.digest(encoding); |
| StringBuffer buf = new StringBuffer(); |
| for (int i = 0; i < digest.length; i++) { |
| byte2hex(digest[i], buf); |
| } |
| fingerprints.add(buf.toString()); |
| } |
| return fingerprints; |
| } |
| |
| private static List<byte[]> altEncodings(X509Certificate c) |
| throws Exception { |
| List<byte[]> result = new ArrayList<>(); |
| |
| DerValue d = new DerValue(c.getEncoded()); |
| DerValue[] seq = new DerValue[3]; |
| // tbsCertificate |
| seq[0] = d.data.getDerValue(); |
| // signatureAlgorithm |
| seq[1] = d.data.getDerValue(); |
| // signature |
| seq[2] = d.data.getDerValue(); |
| |
| List<DerValue> algIds = Arrays.asList(seq[1], altAlgId(seq[1])); |
| |
| List<DerValue> sigs; |
| PublicKey p = c.getPublicKey(); |
| if (p instanceof ECPublicKey) { |
| ECPublicKey ep = (ECPublicKey) p; |
| BigInteger mod = ep.getParams().getOrder(); |
| sigs = Arrays.asList(seq[2], altSig(mod, seq[2])); |
| } else { |
| sigs = Arrays.asList(seq[2]); |
| } |
| |
| for (DerValue algId : algIds) { |
| for (DerValue sig : sigs) { |
| DerOutputStream tmp = new DerOutputStream(); |
| tmp.putDerValue(seq[0]); |
| tmp.putDerValue(algId); |
| tmp.putDerValue(sig); |
| DerOutputStream tmp2 = new DerOutputStream(); |
| tmp2.write(DerValue.tag_Sequence, tmp); |
| result.add(tmp2.toByteArray()); |
| } |
| } |
| return result; |
| } |
| |
| private static DerValue altSig(BigInteger mod, DerValue sig) |
| throws IOException { |
| byte[] sigBits = sig.getBitString(); |
| DerInputStream in = |
| new DerInputStream(sigBits, 0, sigBits.length, false); |
| DerValue[] values = in.getSequence(2); |
| BigInteger r = values[0].getBigInteger(); |
| BigInteger s = values[1].getBigInteger(); |
| BigInteger s2 = s.negate().mod(mod); |
| DerOutputStream out = new DerOutputStream(); |
| out.putInteger(r); |
| out.putInteger(s2); |
| DerOutputStream tmp = new DerOutputStream(); |
| tmp.putBitString(new DerValue(DerValue.tag_Sequence, |
| out.toByteArray()).toByteArray()); |
| return new DerValue(tmp.toByteArray()); |
| } |
| |
| private static DerValue altAlgId(DerValue algId) throws IOException { |
| DerInputStream in = algId.toDerInputStream(); |
| DerOutputStream bytes = new DerOutputStream(); |
| bytes.putOID(in.getOID()); |
| // encode parameters as NULL if not present or omit if NULL |
| if (in.available() == 0) { |
| bytes.putNull(); |
| } |
| DerOutputStream tmp = new DerOutputStream(); |
| tmp.write(DerValue.tag_Sequence, bytes); |
| return new DerValue(tmp.toByteArray()); |
| } |
| } |