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> {
14 private static class CipherInfoGenerator {
16 private Class<?> cipherSuite;
18 private Field cipherSuiteNameMap;
20 private Field exchange;
24 private Field keySize;
26 private Field algortihm;
28 private Field transformation;
30 private HashMap<?, ?> names;
34 private Field macName;
36 private Field macSize;
38 public CipherInfoGenerator() throws ReflectiveOperationException {
39 SSLContextImpl sc = new SSLContextImpl.TLS12Context();
40 Method m = SSLContextImpl.class.getDeclaredMethod("getSupportedCipherSuiteList");
41 m.setAccessible(true);
42 Object o = m.invoke(sc);
43 Class<?> cipherSuiteList = o.getClass();
44 Method collection = cipherSuiteList.getDeclaredMethod("collection");
45 collection.setAccessible(true);
46 Collection<?> suites = (Collection<?>) collection.invoke(o);
47 Object oneSuite = suites.iterator().next();
48 cipherSuite = oneSuite.getClass();
49 cipherSuiteNameMap = cipherSuite.getDeclaredField("nameMap");
50 cipherSuiteNameMap.setAccessible(true);
51 names = (HashMap<?, ?>) cipherSuiteNameMap.get(null);
52 exchange = cipherSuite.getDeclaredField("keyExchange");
53 exchange.setAccessible(true);
54 cipher = cipherSuite.getDeclaredField("cipher");
55 cipher.setAccessible(true);
56 Class<?> bulkCipher = cipher.getType();
57 keySize = bulkCipher.getDeclaredField("keySize");
58 keySize.setAccessible(true);
59 algortihm = bulkCipher.getDeclaredField("algorithm");
60 algortihm.setAccessible(true);
61 transformation = bulkCipher.getDeclaredField("transformation");
62 transformation.setAccessible(true);
64 macAlg = cipherSuite.getDeclaredField("macAlg");
65 macAlg.setAccessible(true);
66 Class<?> mac = macAlg.getType();
67 macName = mac.getDeclaredField("name");
68 macName.setAccessible(true);
69 macSize = mac.getDeclaredField("size");
70 macSize.setAccessible(true);
73 public CipherInfo generateInfo(String suiteName) throws IllegalArgumentException, IllegalAccessException {
74 Object suite = names.get(suiteName);
75 String keyExchange = exchange.get(suite).toString();
76 Object bulkCipher = cipher.get(suite);
77 Object mac = macAlg.get(suite);
79 String transform = (String) transformation.get(bulkCipher);
80 String[] transformationParts = transform.split("/");
81 int keysize = keySize.getInt(bulkCipher);
83 String macNam = (String) macName.get(mac);
84 int macSiz = macSize.getInt(mac);
86 String chaining = null;
87 String padding = null;
88 if (transformationParts.length > 1) {
89 chaining = transformationParts[1];
90 padding = transformationParts[2];
93 return new CipherInfo(suiteName, keyExchange, transformationParts[0], keysize * 8, chaining, padding, macNam, macSiz * 8);
104 String cipherChaining;
106 String cipherPadding;
114 private CipherInfo(String suiteName, String keyExchange, String cipher, int keySize, String cipherChaining, String cipherPadding, String macName, int macSize) {
115 this.suiteName = suiteName;
116 this.keyExchange = keyExchange;
117 this.cipher = cipher;
118 this.keySize = keySize;
119 this.cipherChaining = cipherChaining;
120 this.cipherPadding = cipherPadding;
121 this.macName = macName;
122 this.macSize = macSize;
125 static CipherInfoGenerator cig;
128 cig = new CipherInfoGenerator();
129 } catch (ReflectiveOperationException e) {
134 public static CipherInfo generateInfo(String name) {
139 return cig.generateInfo(name);
140 } catch (IllegalArgumentException e) {
142 } catch (IllegalAccessException e) {
148 public String getSuiteName() {
153 * 5: ECDHE, AES||CAMELLIA, keysize >=256 <br>
154 * 4: DHE, AES||CAMELLIA, keysize >= 256<br>
155 * 3: ECDHE|| DHE, AES||CAMELLIA<br>
160 * @return the strength
162 public int getStrength() {
163 if (cipher.equals("NULL") || cipher.equals("RC4") || cipher.contains("DES")) {
166 boolean ecdhe = keyExchange.startsWith("ECDHE");
167 boolean dhe = keyExchange.startsWith("DHE");
168 boolean pfs = ecdhe || dhe;
169 boolean goodCipher = cipher.equals("AES") || cipher.equals("CAMELLIA");
170 if (ecdhe && goodCipher && keySize >= 256) {
173 if (dhe && goodCipher && keySize >= 256) {
176 if (pfs && goodCipher) {
182 if (keyExchange.equals("RSA") || keyExchange.equals("DSA")) {
188 private static final String[] CIPHER_RANKING = new String[] {
189 "CAMELLIA", "AES", "RC4", "3DES", "DES", "DES40"
193 public String toString() {
194 return "CipherInfo [keyExchange=" + keyExchange + ", cipher=" + cipher + ", keySize=" + keySize + ", cipherChaining=" + cipherChaining + ", cipherPadding=" + cipherPadding + ", macName=" + macName + ", macSize=" + macSize + "]";
200 * Cipher {@link #CIPHER_RANKING}<br>
201 * Cipher {@link #keySize}<br>
208 public int compareTo(CipherInfo o) {
209 int myStrength = getStrength();
210 int oStrength = o.getStrength();
211 if (myStrength > oStrength) {
214 if (myStrength < oStrength) {
218 boolean myEcdhe = keyExchange.startsWith("ECDHE");
219 boolean oEcdhe = o.keyExchange.startsWith("ECDHE");
220 if (myEcdhe && !oEcdhe) {
223 if ( !myEcdhe && oEcdhe) {
226 boolean myGCM = "GCM".equals(cipherChaining);
227 boolean oGCM = "GCM".equals(o.cipherChaining);
228 if (myGCM && !oGCM) {
231 if ( !myGCM && oGCM) {
234 if ( !cipher.equals(o.cipher)) {
236 for (String testCipher : CIPHER_RANKING) {
237 if (cipher.equals(testCipher)) {
240 if (o.cipher.equals(testCipher)) {
244 if (cipher.equals("NULL")) {
247 if (o.cipher.equals("NULL")) {
251 if (keySize > o.keySize) {
254 if (keySize < o.keySize) {
257 boolean mySHA = macName.startsWith("SHA");
258 boolean oSHA = o.macName.startsWith("SHA");
259 if ( !mySHA && oSHA) {
262 if (mySHA && !oSHA) {
265 if (macSize > o.macSize) {
268 if (macSize < o.macSize) {
272 return suiteName.compareTo(o.suiteName);
276 public boolean equals(Object o) {
277 if (o instanceof CipherInfo) {
278 return 0 == this.compareTo((CipherInfo) o);
285 public int hashCode() {
286 final int prime = 31;
288 result = prime * result + ((cipher == null) ? 0 : cipher.hashCode());
289 result = prime * result + ((cipherChaining == null) ? 0 : cipherChaining.hashCode());
290 result = prime * result + ((cipherPadding == null) ? 0 : cipherPadding.hashCode());
291 result = prime * result + ((keyExchange == null) ? 0 : keyExchange.hashCode());
292 result = prime * result + keySize;
293 result = prime * result + ((macName == null) ? 0 : macName.hashCode());
294 result = prime * result + macSize;
295 result = prime * result + ((suiteName == null) ? 0 : suiteName.hashCode());
299 static String[] cipherRanking = null;
301 public static String[] getCompleteRanking() {
302 if (cipherRanking == null) {
303 @SuppressWarnings("unchecked")
304 String[] ciphers = filterCiphers((Iterable<String>) cig.names.keySet());
305 cipherRanking = ciphers;
307 return cipherRanking;
310 private static String[] filterCiphers(Iterable<String> toFilter) {
311 TreeSet<CipherInfo> chosenCiphers = new TreeSet<CipherInfo>();
312 for (String o : toFilter) {
314 CipherInfo info = CipherInfo.generateInfo(s);
316 if (info.getStrength() > 1) {
317 chosenCiphers.add(info);
321 String[] ciphers = new String[chosenCiphers.size()];
323 for (CipherInfo i : chosenCiphers) {
324 ciphers[counter++] = i.getSuiteName();
329 public static String[] filter(String[] supportedCipherSuites) {
330 return filterCiphers(Arrays.asList(supportedCipherSuites));