]> WPIA git - gigi.git/blob - src/org/cacert/gigi/util/CipherInfo.java
Merge branch 'changePasswordForm'
[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.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);
51
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);
59                 }
60
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);
66
67                         String transform = (String) transformation.get(bulkCipher);
68                         String[] transformationParts = transform.split("/");
69                         int keysize = keySize.getInt(bulkCipher);
70
71                         String macNam = (String) macName.get(mac);
72                         int macSiz = macSize.getInt(mac);
73
74                         String chaining = null;
75                         String padding = null;
76                         if (transformationParts.length > 1) {
77                                 chaining = transformationParts[1];
78                                 padding = transformationParts[2];
79                         }
80
81                         return new CipherInfo(suiteName, keyExchange, transformationParts[0], keysize * 8, chaining, padding,
82                                 macNam, macSiz * 8);
83
84                 }
85         }
86
87         String keyExchange;
88         String cipher;
89         int keySize;
90         String cipherChaining;
91         String cipherPadding;
92         String macName;
93         int macSize;
94         String suiteName;
95
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;
106         }
107
108         static CipherInfoGenerator cig;
109         static {
110                 try {
111                         cig = new CipherInfoGenerator();
112                 } catch (ReflectiveOperationException e) {
113                         e.printStackTrace();
114                 }
115         }
116
117         public static CipherInfo generateInfo(String name) {
118                 if (cig == null) {
119                         return null;
120                 }
121                 try {
122                         return cig.generateInfo(name);
123                 } catch (IllegalArgumentException e) {
124                         e.printStackTrace();
125                 } catch (IllegalAccessException e) {
126                         e.printStackTrace();
127                 }
128                 return null;
129         }
130
131         public String getSuiteName() {
132                 return suiteName;
133         }
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") || cipher.contains("DES")) {
147                         return 0;
148                 }
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) {
154                         return 5;
155                 }
156                 if (dhe && goodCipher && keySize >= 256) {
157                         return 4;
158                 }
159                 if (pfs && goodCipher) {
160                         return 3;
161                 }
162                 if (pfs) {
163                         return 2;
164                 }
165                 if (keyExchange.equals("RSA") || keyExchange.equals("DSA")) {
166                         return 1;
167                 }
168                 return 0;
169         }
170
171         private static final String[] CIPHER_RANKING = new String[] { "CAMELLIA", "AES", "RC4", "3DES", "DES", "DES40" };
172
173         @Override
174         public String toString() {
175                 return "CipherInfo [keyExchange=" + keyExchange + ", cipher=" + cipher + ", keySize=" + keySize
176                         + ", cipherChaining=" + cipherChaining + ", cipherPadding=" + cipherPadding + ", macName=" + macName
177                         + ", macSize=" + macSize + "]";
178         }
179
180         /**
181          * ECDHE<br>
182          * GCM<br>
183          * Cipher {@link #CIPHER_RANKING}<br>
184          * Cipher {@link #keySize}<br>
185          * HMAC<br>
186          * HMAC size<br>
187          * 
188          * @return
189          */
190         @Override
191         public int compareTo(CipherInfo o) {
192                 int myStrength = getStrength();
193                 int oStrength = o.getStrength();
194                 if (myStrength > oStrength) {
195                         return -1;
196                 }
197                 if (myStrength < oStrength) {
198                         return 1;
199                 }
200                 // TODO sort SSL/TLS
201                 boolean myEcdhe = keyExchange.startsWith("ECDHE");
202                 boolean oEcdhe = o.keyExchange.startsWith("ECDHE");
203                 if (myEcdhe && !oEcdhe) {
204                         return -1;
205                 }
206                 if (!myEcdhe && oEcdhe) {
207                         return 1;
208                 }
209                 boolean myGCM = "GCM".equals(cipherChaining);
210                 boolean oGCM = "GCM".equals(o.cipherChaining);
211                 if (myGCM && !oGCM) {
212                         return -1;
213                 }
214                 if (!myGCM && oGCM) {
215                         return 1;
216                 }
217                 if (!cipher.equals(o.cipher)) {
218
219                         for (String testCipher : CIPHER_RANKING) {
220                                 if (cipher.equals(testCipher)) {
221                                         return -1;
222                                 }
223                                 if (o.cipher.equals(testCipher)) {
224                                         return 1;
225                                 }
226                         }
227                         if (cipher.equals("NULL")) {
228                                 return 1;
229                         }
230                         if (o.cipher.equals("NULL")) {
231                                 return -1;
232                         }
233                 }
234                 if (keySize > o.keySize) {
235                         return -1;
236                 }
237                 if (keySize < o.keySize) {
238                         return 1;
239                 }
240                 boolean mySHA = macName.startsWith("SHA");
241                 boolean oSHA = o.macName.startsWith("SHA");
242                 if (mySHA && !oSHA) {
243                         return -1;
244                 }
245                 if (mySHA && !oSHA) {
246                         return 1;
247                 }
248                 if (macSize > o.macSize) {
249                         return -1;
250                 }
251                 if (macSize < o.macSize) {
252                         return 1;
253                 }
254
255                 return suiteName.compareTo(o.suiteName);
256         }
257
258         static String[] cipherRanking = null;
259
260         public static String[] getCompleteRanking() {
261                 if (cipherRanking == null) {
262                         String[] ciphers = filterCiphers((Iterable<String>) cig.names.keySet());
263                         cipherRanking = ciphers;
264                 }
265                 return cipherRanking;
266         }
267
268         private static String[] filterCiphers(Iterable<String> toFilter) {
269                 TreeSet<CipherInfo> chosenCiphers = new TreeSet<CipherInfo>();
270                 for (String o : toFilter) {
271                         String s = o;
272                         CipherInfo info = CipherInfo.generateInfo(s);
273                         if (info != null) {
274                                 if (info.getStrength() > 1) {
275                                         chosenCiphers.add(info);
276                                 }
277                         }
278                 }
279                 String[] ciphers = new String[chosenCiphers.size()];
280                 int counter = 0;
281                 for (CipherInfo i : chosenCiphers) {
282                         ciphers[counter++] = i.getSuiteName();
283                 }
284                 return ciphers;
285         }
286
287         public static String[] filter(String[] supportedCipherSuites) {
288                 return filterCiphers(Arrays.asList(supportedCipherSuites));
289         }
290 }