]> WPIA git - gigi.git/blob - src/org/cacert/gigi/util/CipherInfo.java
Merge branch 'libs/jetty/upstream' into libs/jetty/local
[gigi.git] / src / org / cacert / gigi / util / CipherInfo.java
1 package org.cacert.gigi.util;
2
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;
9
10 import sun.security.ssl.SSLContextImpl;
11
12 public class CipherInfo implements Comparable<CipherInfo> {
13         private static class CipherInfoGenerator {
14                 private Class<?> cipherSuite;
15                 private Field cipherSuiteNameMap;
16                 private Field exchange;
17                 private Field cipher;
18                 private Field keySize;
19                 private Field algortihm;
20                 private Field transformation;
21                 private HashMap<?, ?> names;
22                 private Field macAlg;
23                 private Field macName;
24                 private Field macSize;
25
26                 public CipherInfoGenerator() throws ReflectiveOperationException {
27                         SSLContextImpl sc = new SSLContextImpl.TLS12Context();
28                         Method m = SSLContextImpl.class
29                                         .getDeclaredMethod("getSupportedCipherSuiteList");
30                         m.setAccessible(true);
31                         Object o = m.invoke(sc);
32                         Class<?> cipherSuiteList = o.getClass();
33                         Method collection = cipherSuiteList.getDeclaredMethod("collection");
34                         collection.setAccessible(true);
35                         Collection<?> suites = (Collection<?>) collection.invoke(o);
36                         Object oneSuite = suites.iterator().next();
37                         cipherSuite = oneSuite.getClass();
38                         cipherSuiteNameMap = cipherSuite.getDeclaredField("nameMap");
39                         cipherSuiteNameMap.setAccessible(true);
40                         names = (HashMap<?, ?>) cipherSuiteNameMap.get(null);
41                         exchange = cipherSuite.getDeclaredField("keyExchange");
42                         exchange.setAccessible(true);
43                         cipher = cipherSuite.getDeclaredField("cipher");
44                         cipher.setAccessible(true);
45                         Class<?> bulkCipher = cipher.getType();
46                         keySize = bulkCipher.getDeclaredField("keySize");
47                         keySize.setAccessible(true);
48                         algortihm = bulkCipher.getDeclaredField("algorithm");
49                         algortihm.setAccessible(true);
50                         transformation = bulkCipher.getDeclaredField("transformation");
51                         transformation.setAccessible(true);
52
53                         macAlg = cipherSuite.getDeclaredField("macAlg");
54                         macAlg.setAccessible(true);
55                         Class<?> mac = macAlg.getType();
56                         macName = mac.getDeclaredField("name");
57                         macName.setAccessible(true);
58                         macSize = mac.getDeclaredField("size");
59                         macSize.setAccessible(true);
60                 }
61                 public CipherInfo generateInfo(String suiteName)
62                                 throws IllegalArgumentException, IllegalAccessException {
63                         Object suite = names.get(suiteName);
64                         String keyExchange = exchange.get(suite).toString();
65                         Object bulkCipher = cipher.get(suite);
66                         Object mac = macAlg.get(suite);
67
68                         String transform = (String) transformation.get(bulkCipher);
69                         String[] transformationParts = transform.split("/");
70                         int keysize = keySize.getInt(bulkCipher);
71
72                         String macNam = (String) macName.get(mac);
73                         int macSiz = macSize.getInt(mac);
74
75                         String chaining = null;
76                         String padding = null;
77                         if (transformationParts.length > 1) {
78                                 chaining = transformationParts[1];
79                                 padding = transformationParts[2];
80                         }
81
82                         return new CipherInfo(suiteName, keyExchange,
83                                         transformationParts[0], keysize * 8, chaining, padding,
84                                         macNam, macSiz * 8);
85
86                 }
87         }
88         String keyExchange;
89         String cipher;
90         int keySize;
91         String cipherChaining;
92         String cipherPadding;
93         String macName;
94         int macSize;
95         String suiteName;
96
97         private CipherInfo(String suiteName, String keyExchange, String cipher,
98                         int keySize, String cipherChaining, String cipherPadding,
99                         String macName, int macSize) {
100                 this.suiteName = suiteName;
101                 this.keyExchange = keyExchange;
102                 this.cipher = cipher;
103                 this.keySize = keySize;
104                 this.cipherChaining = cipherChaining;
105                 this.cipherPadding = cipherPadding;
106                 this.macName = macName;
107                 this.macSize = macSize;
108         }
109
110         static CipherInfoGenerator cig;
111         static {
112                 try {
113                         cig = new CipherInfoGenerator();
114                 } catch (ReflectiveOperationException e) {
115                         e.printStackTrace();
116                 }
117         }
118
119         public static CipherInfo generateInfo(String name) {
120                 if (cig == null) {
121                         return null;
122                 }
123                 try {
124                         return cig.generateInfo(name);
125                 } catch (IllegalArgumentException e) {
126                         e.printStackTrace();
127                 } catch (IllegalAccessException e) {
128                         e.printStackTrace();
129                 }
130                 return null;
131         }
132         public String getSuiteName() {
133                 return suiteName;
134         }
135         /**
136          * 5: ECDHE, AES||CAMELLIA, keysize >=256 <br>
137          * 4: DHE, AES||CAMELLIA, keysize >= 256<br>
138          * 3: ECDHE|| DHE, AES||CAMELLIA<br>
139          * 2: ECDHE||DHE<br>
140          * 1: RSA||DSA <br>
141          * 0: Others
142          * 
143          * @return the strength
144          */
145         public int getStrength() {
146                 if (cipher.equals("NULL") || cipher.equals("RC4")
147                                 || cipher.contains("DES")) {
148                         return 0;
149                 }
150                 boolean ecdhe = keyExchange.startsWith("ECDHE");
151                 boolean dhe = keyExchange.startsWith("DHE");
152                 boolean pfs = ecdhe || dhe;
153                 boolean goodCipher = cipher.equals("AES") || cipher.equals("CAMELLIA");
154                 if (ecdhe && goodCipher && keySize >= 256) {
155                         return 5;
156                 }
157                 if (dhe && goodCipher && keySize >= 256) {
158                         return 4;
159                 }
160                 if (pfs && goodCipher) {
161                         return 3;
162                 }
163                 if (pfs) {
164                         return 2;
165                 }
166                 if (keyExchange.equals("RSA") || keyExchange.equals("DSA")) {
167                         return 1;
168                 }
169                 return 0;
170         }
171         private static final String[] CIPHER_RANKING = new String[]{"CAMELLIA",
172                         "AES", "RC4", "3DES", "DES", "DES40"};
173
174         @Override
175         public String toString() {
176                 return "CipherInfo [keyExchange=" + keyExchange + ", cipher=" + cipher
177                                 + ", keySize=" + keySize + ", cipherChaining=" + cipherChaining
178                                 + ", cipherPadding=" + cipherPadding + ", macName=" + macName
179                                 + ", macSize=" + macSize + "]";
180         }
181         /**
182          * ECDHE<br>
183          * GCM<br>
184          * Cipher {@link #CIPHER_RANKING}<br>
185          * Cipher {@link #keySize}<br>
186          * HMAC<br>
187          * HMAC size<br>
188          * 
189          * @return
190          */
191         @Override
192         public int compareTo(CipherInfo o) {
193                 int myStrength = getStrength();
194                 int oStrength = o.getStrength();
195                 if (myStrength > oStrength) {
196                         return -1;
197                 }
198                 if (myStrength < oStrength) {
199                         return 1;
200                 }
201                 // TODO sort SSL/TLS
202                 boolean myEcdhe = keyExchange.startsWith("ECDHE");
203                 boolean oEcdhe = o.keyExchange.startsWith("ECDHE");
204                 if (myEcdhe && !oEcdhe) {
205                         return -1;
206                 }
207                 if (!myEcdhe && oEcdhe) {
208                         return 1;
209                 }
210                 boolean myGCM = "GCM".equals(cipherChaining);
211                 boolean oGCM = "GCM".equals(o.cipherChaining);
212                 if (myGCM && !oGCM) {
213                         return -1;
214                 }
215                 if (!myGCM && oGCM) {
216                         return 1;
217                 }
218                 if (!cipher.equals(o.cipher)) {
219
220                         for (String testCipher : CIPHER_RANKING) {
221                                 if (cipher.equals(testCipher)) {
222                                         return -1;
223                                 }
224                                 if (o.cipher.equals(testCipher)) {
225                                         return 1;
226                                 }
227                         }
228                         if (cipher.equals("NULL")) {
229                                 return 1;
230                         }
231                         if (o.cipher.equals("NULL")) {
232                                 return -1;
233                         }
234                 }
235                 if (keySize > o.keySize) {
236                         return -1;
237                 }
238                 if (keySize < o.keySize) {
239                         return 1;
240                 }
241                 boolean mySHA = macName.startsWith("SHA");
242                 boolean oSHA = o.macName.startsWith("SHA");
243                 if (mySHA && !oSHA) {
244                         return -1;
245                 }
246                 if (mySHA && !oSHA) {
247                         return 1;
248                 }
249                 if (macSize > o.macSize) {
250                         return -1;
251                 }
252                 if (macSize < o.macSize) {
253                         return 1;
254                 }
255
256                 return suiteName.compareTo(o.suiteName);
257         }
258         static String[] cipherRanking = null;
259         public static String[] getCompleteRanking() {
260                 if (cipherRanking == null) {
261                         String[] ciphers = filterCiphers((Iterable<String>) cig.names
262                                         .keySet());
263                         cipherRanking = ciphers;
264                 }
265                 return cipherRanking;
266         }
267         private static String[] filterCiphers(Iterable<String> toFilter) {
268                 TreeSet<CipherInfo> chosenCiphers = new TreeSet<CipherInfo>();
269                 for (String o : toFilter) {
270                         String s = o;
271                         CipherInfo info = CipherInfo.generateInfo(s);
272                         if (info != null) {
273                                 if (info.getStrength() > 1) {
274                                         chosenCiphers.add(info);
275                                 }
276                         }
277                 }
278                 String[] ciphers = new String[chosenCiphers.size()];
279                 int counter = 0;
280                 for (CipherInfo i : chosenCiphers) {
281                         ciphers[counter++] = i.getSuiteName();
282                 }
283                 return ciphers;
284         }
285         public static String[] filter(String[] supportedCipherSuites) {
286                 return filterCiphers(Arrays.asList(supportedCipherSuites));
287         }
288 }