1 package org.cacert.gigi.util;
3 import java.lang.reflect.Field;
4 import java.lang.reflect.Method;
5 import java.util.Arrays;
6 import java.util.Collection;
7 import java.util.HashMap;
8 import java.util.TreeSet;
10 import sun.security.ssl.SSLContextImpl;
12 public class CipherInfo implements Comparable<CipherInfo> {
13 private static class CipherInfoGenerator {
14 private Class<?> cipherSuite;
15 private Field cipherSuiteNameMap;
16 private Field exchange;
18 private Field keySize;
19 private Field algortihm;
20 private Field transformation;
21 private HashMap<?, ?> names;
23 private Field macName;
24 private Field macSize;
26 public CipherInfoGenerator() throws ReflectiveOperationException {
27 SSLContextImpl sc = new SSLContextImpl.TLS12Context();
28 Method m = SSLContextImpl.class.getDeclaredMethod("getSupportedCipherSuiteList");
29 m.setAccessible(true);
30 Object o = m.invoke(sc);
31 Class<?> cipherSuiteList = o.getClass();
32 Method collection = cipherSuiteList.getDeclaredMethod("collection");
33 collection.setAccessible(true);
34 Collection<?> suites = (Collection<?>) collection.invoke(o);
35 Object oneSuite = suites.iterator().next();
36 cipherSuite = oneSuite.getClass();
37 cipherSuiteNameMap = cipherSuite.getDeclaredField("nameMap");
38 cipherSuiteNameMap.setAccessible(true);
39 names = (HashMap<?, ?>) cipherSuiteNameMap.get(null);
40 exchange = cipherSuite.getDeclaredField("keyExchange");
41 exchange.setAccessible(true);
42 cipher = cipherSuite.getDeclaredField("cipher");
43 cipher.setAccessible(true);
44 Class<?> bulkCipher = cipher.getType();
45 keySize = bulkCipher.getDeclaredField("keySize");
46 keySize.setAccessible(true);
47 algortihm = bulkCipher.getDeclaredField("algorithm");
48 algortihm.setAccessible(true);
49 transformation = bulkCipher.getDeclaredField("transformation");
50 transformation.setAccessible(true);
52 macAlg = cipherSuite.getDeclaredField("macAlg");
53 macAlg.setAccessible(true);
54 Class<?> mac = macAlg.getType();
55 macName = mac.getDeclaredField("name");
56 macName.setAccessible(true);
57 macSize = mac.getDeclaredField("size");
58 macSize.setAccessible(true);
61 public CipherInfo generateInfo(String suiteName) throws IllegalArgumentException, IllegalAccessException {
62 Object suite = names.get(suiteName);
63 String keyExchange = exchange.get(suite).toString();
64 Object bulkCipher = cipher.get(suite);
65 Object mac = macAlg.get(suite);
67 String transform = (String) transformation.get(bulkCipher);
68 String[] transformationParts = transform.split("/");
69 int keysize = keySize.getInt(bulkCipher);
71 String macNam = (String) macName.get(mac);
72 int macSiz = macSize.getInt(mac);
74 String chaining = null;
75 String padding = null;
76 if (transformationParts.length > 1) {
77 chaining = transformationParts[1];
78 padding = transformationParts[2];
81 return new CipherInfo(suiteName, keyExchange, transformationParts[0], keysize * 8, chaining, padding,
90 String cipherChaining;
96 private CipherInfo(String suiteName, String keyExchange, String cipher, int keySize, String cipherChaining,
97 String cipherPadding, String macName, int macSize) {
98 this.suiteName = suiteName;
99 this.keyExchange = keyExchange;
100 this.cipher = cipher;
101 this.keySize = keySize;
102 this.cipherChaining = cipherChaining;
103 this.cipherPadding = cipherPadding;
104 this.macName = macName;
105 this.macSize = macSize;
108 static CipherInfoGenerator cig;
111 cig = new CipherInfoGenerator();
112 } catch (ReflectiveOperationException e) {
117 public static CipherInfo generateInfo(String name) {
122 return cig.generateInfo(name);
123 } catch (IllegalArgumentException e) {
125 } catch (IllegalAccessException e) {
131 public String getSuiteName() {
136 * 5: ECDHE, AES||CAMELLIA, keysize >=256 <br>
137 * 4: DHE, AES||CAMELLIA, keysize >= 256<br>
138 * 3: ECDHE|| DHE, AES||CAMELLIA<br>
143 * @return the strength
145 public int getStrength() {
146 if (cipher.equals("NULL") || cipher.equals("RC4") || cipher.contains("DES")) {
149 boolean ecdhe = keyExchange.startsWith("ECDHE");
150 boolean dhe = keyExchange.startsWith("DHE");
151 boolean pfs = ecdhe || dhe;
152 boolean goodCipher = cipher.equals("AES") || cipher.equals("CAMELLIA");
153 if (ecdhe && goodCipher && keySize >= 256) {
156 if (dhe && goodCipher && keySize >= 256) {
159 if (pfs && goodCipher) {
165 if (keyExchange.equals("RSA") || keyExchange.equals("DSA")) {
171 private static final String[] CIPHER_RANKING = new String[] { "CAMELLIA", "AES", "RC4", "3DES", "DES", "DES40" };
174 public String toString() {
175 return "CipherInfo [keyExchange=" + keyExchange + ", cipher=" + cipher + ", keySize=" + keySize
176 + ", cipherChaining=" + cipherChaining + ", cipherPadding=" + cipherPadding + ", macName=" + macName
177 + ", macSize=" + macSize + "]";
183 * Cipher {@link #CIPHER_RANKING}<br>
184 * Cipher {@link #keySize}<br>
191 public int compareTo(CipherInfo o) {
192 int myStrength = getStrength();
193 int oStrength = o.getStrength();
194 if (myStrength > oStrength) {
197 if (myStrength < oStrength) {
201 boolean myEcdhe = keyExchange.startsWith("ECDHE");
202 boolean oEcdhe = o.keyExchange.startsWith("ECDHE");
203 if (myEcdhe && !oEcdhe) {
206 if (!myEcdhe && oEcdhe) {
209 boolean myGCM = "GCM".equals(cipherChaining);
210 boolean oGCM = "GCM".equals(o.cipherChaining);
211 if (myGCM && !oGCM) {
214 if (!myGCM && oGCM) {
217 if (!cipher.equals(o.cipher)) {
219 for (String testCipher : CIPHER_RANKING) {
220 if (cipher.equals(testCipher)) {
223 if (o.cipher.equals(testCipher)) {
227 if (cipher.equals("NULL")) {
230 if (o.cipher.equals("NULL")) {
234 if (keySize > o.keySize) {
237 if (keySize < o.keySize) {
240 boolean mySHA = macName.startsWith("SHA");
241 boolean oSHA = o.macName.startsWith("SHA");
242 if (mySHA && !oSHA) {
245 if (mySHA && !oSHA) {
248 if (macSize > o.macSize) {
251 if (macSize < o.macSize) {
255 return suiteName.compareTo(o.suiteName);
258 static String[] cipherRanking = null;
260 public static String[] getCompleteRanking() {
261 if (cipherRanking == null) {
262 String[] ciphers = filterCiphers((Iterable<String>) cig.names.keySet());
263 cipherRanking = ciphers;
265 return cipherRanking;
268 private static String[] filterCiphers(Iterable<String> toFilter) {
269 TreeSet<CipherInfo> chosenCiphers = new TreeSet<CipherInfo>();
270 for (String o : toFilter) {
272 CipherInfo info = CipherInfo.generateInfo(s);
274 if (info.getStrength() > 1) {
275 chosenCiphers.add(info);
279 String[] ciphers = new String[chosenCiphers.size()];
281 for (CipherInfo i : chosenCiphers) {
282 ciphers[counter++] = i.getSuiteName();
287 public static String[] filter(String[] supportedCipherSuites) {
288 return filterCiphers(Arrays.asList(supportedCipherSuites));