]> WPIA git - gigi.git/blob - src/org/cacert/gigi/util/CipherInfo.java
fix: ResultSet.getDate is often wrong as it fetches day-precision times
[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
14     private static class CipherInfoGenerator {
15
16         private Class<?> cipherSuite;
17
18         private Field cipherSuiteNameMap;
19
20         private Field exchange;
21
22         private Field cipher;
23
24         private Field keySize;
25
26         private Field algortihm;
27
28         private Field transformation;
29
30         private HashMap<?, ?> names;
31
32         private Field macAlg;
33
34         private Field macName;
35
36         private Field macSize;
37
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);
63
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);
71         }
72
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);
78
79             String transform = (String) transformation.get(bulkCipher);
80             String[] transformationParts = transform.split("/");
81             int keysize = keySize.getInt(bulkCipher);
82
83             String macNam = (String) macName.get(mac);
84             int macSiz = macSize.getInt(mac);
85
86             String chaining = null;
87             String padding = null;
88             if (transformationParts.length > 1) {
89                 chaining = transformationParts[1];
90                 padding = transformationParts[2];
91             }
92
93             return new CipherInfo(suiteName, keyExchange, transformationParts[0], keysize * 8, chaining, padding, macNam, macSiz * 8);
94
95         }
96     }
97
98     String keyExchange;
99
100     String cipher;
101
102     int keySize;
103
104     String cipherChaining;
105
106     String cipherPadding;
107
108     String macName;
109
110     int macSize;
111
112     String suiteName;
113
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;
123     }
124
125     static CipherInfoGenerator cig;
126     static {
127         try {
128             cig = new CipherInfoGenerator();
129         } catch (ReflectiveOperationException e) {
130             e.printStackTrace();
131         }
132     }
133
134     public static CipherInfo generateInfo(String name) {
135         if (cig == null) {
136             return null;
137         }
138         try {
139             return cig.generateInfo(name);
140         } catch (IllegalArgumentException e) {
141             e.printStackTrace();
142         } catch (IllegalAccessException e) {
143             e.printStackTrace();
144         }
145         return null;
146     }
147
148     public String getSuiteName() {
149         return suiteName;
150     }
151
152     /**
153      * 5: ECDHE, AES||CAMELLIA, keysize >=256 <br>
154      * 4: DHE, AES||CAMELLIA, keysize >= 256<br>
155      * 3: ECDHE|| DHE, AES||CAMELLIA<br>
156      * 2: ECDHE||DHE<br>
157      * 1: RSA||DSA <br>
158      * 0: Others
159      * 
160      * @return the strength
161      */
162     public int getStrength() {
163         if (cipher.equals("NULL") || cipher.equals("RC4") || cipher.contains("DES")) {
164             return 0;
165         }
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) {
171             return 5;
172         }
173         if (dhe && goodCipher && keySize >= 256) {
174             return 4;
175         }
176         if (pfs && goodCipher) {
177             return 3;
178         }
179         if (pfs) {
180             return 2;
181         }
182         if (keyExchange.equals("RSA") || keyExchange.equals("DSA")) {
183             return 1;
184         }
185         return 0;
186     }
187
188     private static final String[] CIPHER_RANKING = new String[] {
189             "CAMELLIA", "AES", "RC4", "3DES", "DES", "DES40"
190     };
191
192     @Override
193     public String toString() {
194         return "CipherInfo [keyExchange=" + keyExchange + ", cipher=" + cipher + ", keySize=" + keySize + ", cipherChaining=" + cipherChaining + ", cipherPadding=" + cipherPadding + ", macName=" + macName + ", macSize=" + macSize + "]";
195     }
196
197     /**
198      * ECDHE<br>
199      * GCM<br>
200      * Cipher {@link #CIPHER_RANKING}<br>
201      * Cipher {@link #keySize}<br>
202      * HMAC<br>
203      * HMAC size<br>
204      * 
205      * @return
206      */
207     @Override
208     public int compareTo(CipherInfo o) {
209         int myStrength = getStrength();
210         int oStrength = o.getStrength();
211         if (myStrength > oStrength) {
212             return -1;
213         }
214         if (myStrength < oStrength) {
215             return 1;
216         }
217         // TODO sort SSL/TLS
218         boolean myEcdhe = keyExchange.startsWith("ECDHE");
219         boolean oEcdhe = o.keyExchange.startsWith("ECDHE");
220         if (myEcdhe && !oEcdhe) {
221             return -1;
222         }
223         if ( !myEcdhe && oEcdhe) {
224             return 1;
225         }
226         boolean myGCM = "GCM".equals(cipherChaining);
227         boolean oGCM = "GCM".equals(o.cipherChaining);
228         if (myGCM && !oGCM) {
229             return -1;
230         }
231         if ( !myGCM && oGCM) {
232             return 1;
233         }
234         if ( !cipher.equals(o.cipher)) {
235
236             for (String testCipher : CIPHER_RANKING) {
237                 if (cipher.equals(testCipher)) {
238                     return -1;
239                 }
240                 if (o.cipher.equals(testCipher)) {
241                     return 1;
242                 }
243             }
244             if (cipher.equals("NULL")) {
245                 return 1;
246             }
247             if (o.cipher.equals("NULL")) {
248                 return -1;
249             }
250         }
251         if (keySize > o.keySize) {
252             return -1;
253         }
254         if (keySize < o.keySize) {
255             return 1;
256         }
257         boolean mySHA = macName.startsWith("SHA");
258         boolean oSHA = o.macName.startsWith("SHA");
259         if ( !mySHA && oSHA) {
260             return -1;
261         }
262         if (mySHA && !oSHA) {
263             return 1;
264         }
265         if (macSize > o.macSize) {
266             return -1;
267         }
268         if (macSize < o.macSize) {
269             return 1;
270         }
271
272         return suiteName.compareTo(o.suiteName);
273     }
274
275     @Override
276     public boolean equals(Object o) {
277         if (o instanceof CipherInfo) {
278             return 0 == this.compareTo((CipherInfo) o);
279         }
280
281         return false;
282     }
283
284     @Override
285     public int hashCode() {
286         final int prime = 31;
287         int result = 1;
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());
296         return result;
297     }
298
299     static String[] cipherRanking = null;
300
301     public static String[] getCompleteRanking() {
302         if (cipherRanking == null) {
303             @SuppressWarnings("unchecked")
304             String[] ciphers = filterCiphers((Iterable<String>) cig.names.keySet());
305             cipherRanking = ciphers;
306         }
307         return cipherRanking;
308     }
309
310     private static String[] filterCiphers(Iterable<String> toFilter) {
311         TreeSet<CipherInfo> chosenCiphers = new TreeSet<CipherInfo>();
312         for (String o : toFilter) {
313             String s = o;
314             CipherInfo info = CipherInfo.generateInfo(s);
315             if (info != null) {
316                 if (info.getStrength() > 1) {
317                     chosenCiphers.add(info);
318                 }
319             }
320         }
321         String[] ciphers = new String[chosenCiphers.size()];
322         int counter = 0;
323         for (CipherInfo i : chosenCiphers) {
324             ciphers[counter++] = i.getSuiteName();
325         }
326         return ciphers;
327     }
328
329     public static String[] filter(String[] supportedCipherSuites) {
330         return filterCiphers(Arrays.asList(supportedCipherSuites));
331     }
332 }