1 /*
2  * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 import java.io.IOException;
27 import java.math.BigInteger;
28 import java.security.MessageDigest;
29 import java.security.PublicKey;
30 import java.security.NoSuchAlgorithmException;
31 import java.security.cert.Certificate;
32 import java.security.cert.CertificateEncodingException;
33 import java.security.cert.CertificateFactory;
34 import java.security.cert.X509Certificate;
35 import java.security.interfaces.ECPublicKey;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Collection;
39 import java.util.List;
40 import java.util.Set;
41 import java.util.TreeSet;
42 
43 import sun.security.util.DerInputStream;
44 import sun.security.util.DerOutputStream;
45 import sun.security.util.DerValue;
46 
47 /**
48  * This is the tool to convert blacklisted.certs.pem to blacklisted.certs.
49  * Every time a new blacklisted certs is added, please append the PEM format
50  * to the end of blacklisted.certs.pem (with proper comments) and then use
51  * this tool to generate an updated blacklisted.certs. Make sure to include
52  * changes to both in a changeset.
53  */
54 public class BlacklistedCertsConverter {
main(String[] args)55     public static void main(String[] args) throws Exception {
56         if (args.length == 0) {
57             System.out.println("Usage: java BlacklistedCertsConverter SHA-256" +
58                     " < blacklisted.certs.pem > blacklisted.certs");
59             System.exit(1);
60         }
61         String mdAlg = args[0];
62         CertificateFactory cf = CertificateFactory.getInstance("X.509");
63         Collection<? extends Certificate> certs
64                 = cf.generateCertificates(System.in);
65         System.out.println("Algorithm=" + mdAlg);
66         Set<String> fingerprints = new TreeSet<>();
67         for (Certificate cert: certs) {
68             fingerprints.addAll(
69                     getCertificateFingerPrints(mdAlg, (X509Certificate)cert));
70         }
71 
72         for (String s: fingerprints) {
73             System.out.println(s);
74         }
75     }
76 
77     /**
78      * Converts a byte to hex digit and writes to the supplied buffer
79      */
byte2hex(byte b, StringBuffer buf)80     private static void byte2hex(byte b, StringBuffer buf) {
81         char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
82                 '9', 'A', 'B', 'C', 'D', 'E', 'F' };
83         int high = ((b & 0xf0) >> 4);
84         int low = (b & 0x0f);
85         buf.append(hexChars[high]);
86         buf.append(hexChars[low]);
87     }
88 
89     /**
90      * Gets the requested fingerprints of the certificate.
91      */
getCertificateFingerPrints( String mdAlg, X509Certificate cert)92     private static List<String> getCertificateFingerPrints(
93             String mdAlg, X509Certificate cert) throws Exception {
94         List<String> fingerprints = new ArrayList<>();
95         for (byte[] encoding : altEncodings(cert)) {
96             MessageDigest md = MessageDigest.getInstance(mdAlg);
97             byte[] digest = md.digest(encoding);
98             StringBuffer buf = new StringBuffer();
99             for (int i = 0; i < digest.length; i++) {
100                 byte2hex(digest[i], buf);
101             }
102             fingerprints.add(buf.toString());
103         }
104         return fingerprints;
105     }
106 
altEncodings(X509Certificate c)107     private static List<byte[]> altEncodings(X509Certificate c)
108             throws Exception {
109         List<byte[]> result = new ArrayList<>();
110 
111         DerValue d = new DerValue(c.getEncoded());
112         DerValue[] seq = new DerValue[3];
113         // tbsCertificate
114         seq[0] = d.data.getDerValue();
115         // signatureAlgorithm
116         seq[1] = d.data.getDerValue();
117         // signature
118         seq[2] = d.data.getDerValue();
119 
120         List<DerValue> algIds = Arrays.asList(seq[1], altAlgId(seq[1]));
121 
122         List<DerValue> sigs;
123         PublicKey p = c.getPublicKey();
124         if (p instanceof ECPublicKey) {
125             ECPublicKey ep = (ECPublicKey) p;
126             BigInteger mod = ep.getParams().getOrder();
127             sigs = Arrays.asList(seq[2], altSig(mod, seq[2]));
128         } else {
129             sigs = Arrays.asList(seq[2]);
130         }
131 
132         for (DerValue algId : algIds) {
133             for (DerValue sig : sigs) {
134                 DerOutputStream tmp = new DerOutputStream();
135                 tmp.putDerValue(seq[0]);
136                 tmp.putDerValue(algId);
137                 tmp.putDerValue(sig);
138                 DerOutputStream tmp2 = new DerOutputStream();
139                 tmp2.write(DerValue.tag_Sequence, tmp);
140                 result.add(tmp2.toByteArray());
141             }
142         }
143         return result;
144     }
145 
altSig(BigInteger mod, DerValue sig)146     private static DerValue altSig(BigInteger mod, DerValue sig)
147             throws IOException {
148         byte[] sigBits = sig.getBitString();
149         DerInputStream in =
150             new DerInputStream(sigBits, 0, sigBits.length, false);
151         DerValue[] values = in.getSequence(2);
152         BigInteger r = values[0].getBigInteger();
153         BigInteger s = values[1].getBigInteger();
154         BigInteger s2 = s.negate().mod(mod);
155         DerOutputStream out = new DerOutputStream();
156         out.putInteger(r);
157         out.putInteger(s2);
158         DerOutputStream tmp = new DerOutputStream();
159         tmp.putBitString(new DerValue(DerValue.tag_Sequence,
160                 out.toByteArray()).toByteArray());
161         return new DerValue(tmp.toByteArray());
162     }
163 
altAlgId(DerValue algId)164     private static DerValue altAlgId(DerValue algId) throws IOException {
165         DerInputStream in = algId.toDerInputStream();
166         DerOutputStream bytes = new DerOutputStream();
167         bytes.putOID(in.getOID());
168         // encode parameters as NULL if not present or omit if NULL
169         if (in.available() == 0) {
170             bytes.putNull();
171         }
172         DerOutputStream tmp = new DerOutputStream();
173         tmp.write(DerValue.tag_Sequence, bytes);
174         return new DerValue(tmp.toByteArray());
175     }
176 }
177