]> WPIA git - gigi.git/blobdiff - src/club/wpia/gigi/util/CipherInfo.java
upd: rename package name and all references to it
[gigi.git] / src / club / wpia / gigi / util / CipherInfo.java
diff --git a/src/club/wpia/gigi/util/CipherInfo.java b/src/club/wpia/gigi/util/CipherInfo.java
new file mode 100644 (file)
index 0000000..9fda8ca
--- /dev/null
@@ -0,0 +1,332 @@
+package club.wpia.gigi.util;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.TreeSet;
+
+import sun.security.ssl.SSLContextImpl;
+
+public class CipherInfo implements Comparable<CipherInfo> {
+
+    private static class CipherInfoGenerator {
+
+        private Class<?> cipherSuite;
+
+        private Field cipherSuiteNameMap;
+
+        private Field exchange;
+
+        private Field cipher;
+
+        private Field keySize;
+
+        private Field algortihm;
+
+        private Field transformation;
+
+        private HashMap<?, ?> names;
+
+        private Field macAlg;
+
+        private Field macName;
+
+        private Field macSize;
+
+        public CipherInfoGenerator() throws ReflectiveOperationException {
+            SSLContextImpl sc = new SSLContextImpl.TLS12Context();
+            Method m = SSLContextImpl.class.getDeclaredMethod("getSupportedCipherSuiteList");
+            m.setAccessible(true);
+            Object o = m.invoke(sc);
+            Class<?> cipherSuiteList = o.getClass();
+            Method collection = cipherSuiteList.getDeclaredMethod("collection");
+            collection.setAccessible(true);
+            Collection<?> suites = (Collection<?>) collection.invoke(o);
+            Object oneSuite = suites.iterator().next();
+            cipherSuite = oneSuite.getClass();
+            cipherSuiteNameMap = cipherSuite.getDeclaredField("nameMap");
+            cipherSuiteNameMap.setAccessible(true);
+            names = (HashMap<?, ?>) cipherSuiteNameMap.get(null);
+            exchange = cipherSuite.getDeclaredField("keyExchange");
+            exchange.setAccessible(true);
+            cipher = cipherSuite.getDeclaredField("cipher");
+            cipher.setAccessible(true);
+            Class<?> bulkCipher = cipher.getType();
+            keySize = bulkCipher.getDeclaredField("keySize");
+            keySize.setAccessible(true);
+            algortihm = bulkCipher.getDeclaredField("algorithm");
+            algortihm.setAccessible(true);
+            transformation = bulkCipher.getDeclaredField("transformation");
+            transformation.setAccessible(true);
+
+            macAlg = cipherSuite.getDeclaredField("macAlg");
+            macAlg.setAccessible(true);
+            Class<?> mac = macAlg.getType();
+            macName = mac.getDeclaredField("name");
+            macName.setAccessible(true);
+            macSize = mac.getDeclaredField("size");
+            macSize.setAccessible(true);
+        }
+
+        public CipherInfo generateInfo(String suiteName) throws IllegalArgumentException, IllegalAccessException {
+            Object suite = names.get(suiteName);
+            String keyExchange = exchange.get(suite).toString();
+            Object bulkCipher = cipher.get(suite);
+            Object mac = macAlg.get(suite);
+
+            String transform = (String) transformation.get(bulkCipher);
+            String[] transformationParts = transform.split("/");
+            int keysize = keySize.getInt(bulkCipher);
+
+            String macNam = (String) macName.get(mac);
+            int macSiz = macSize.getInt(mac);
+
+            String chaining = null;
+            String padding = null;
+            if (transformationParts.length > 1) {
+                chaining = transformationParts[1];
+                padding = transformationParts[2];
+            }
+
+            return new CipherInfo(suiteName, keyExchange, transformationParts[0], keysize * 8, chaining, padding, macNam, macSiz * 8);
+
+        }
+    }
+
+    String keyExchange;
+
+    String cipher;
+
+    int keySize;
+
+    String cipherChaining;
+
+    String cipherPadding;
+
+    String macName;
+
+    int macSize;
+
+    String suiteName;
+
+    private CipherInfo(String suiteName, String keyExchange, String cipher, int keySize, String cipherChaining, String cipherPadding, String macName, int macSize) {
+        this.suiteName = suiteName;
+        this.keyExchange = keyExchange;
+        this.cipher = cipher;
+        this.keySize = keySize;
+        this.cipherChaining = cipherChaining;
+        this.cipherPadding = cipherPadding;
+        this.macName = macName;
+        this.macSize = macSize;
+    }
+
+    static CipherInfoGenerator cig;
+    static {
+        try {
+            cig = new CipherInfoGenerator();
+        } catch (ReflectiveOperationException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static CipherInfo generateInfo(String name) {
+        if (cig == null) {
+            return null;
+        }
+        try {
+            return cig.generateInfo(name);
+        } catch (IllegalArgumentException e) {
+            e.printStackTrace();
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public String getSuiteName() {
+        return suiteName;
+    }
+
+    /**
+     * 5: ECDHE, AES||CAMELLIA, keysize >=256 <br>
+     * 4: DHE, AES||CAMELLIA, keysize >= 256<br>
+     * 3: ECDHE|| DHE, AES||CAMELLIA<br>
+     * 2: ECDHE||DHE<br>
+     * 1: RSA||DSA <br>
+     * 0: Others
+     * 
+     * @return the strength
+     */
+    public int getStrength() {
+        if (cipher.equals("NULL") || cipher.equals("RC4") || cipher.contains("DES")) {
+            return 0;
+        }
+        boolean ecdhe = keyExchange.startsWith("ECDHE");
+        boolean dhe = keyExchange.startsWith("DHE");
+        boolean pfs = ecdhe || dhe;
+        boolean goodCipher = cipher.equals("AES") || cipher.equals("CAMELLIA");
+        if (ecdhe && goodCipher && keySize >= 256) {
+            return 5;
+        }
+        if (dhe && goodCipher && keySize >= 256) {
+            return 4;
+        }
+        if (pfs && goodCipher) {
+            return 3;
+        }
+        if (pfs) {
+            return 2;
+        }
+        if (keyExchange.equals("RSA") || keyExchange.equals("DSA")) {
+            return 1;
+        }
+        return 0;
+    }
+
+    private static final String[] CIPHER_RANKING = new String[] {
+            "CAMELLIA", "AES", "RC4", "3DES", "DES", "DES40"
+    };
+
+    @Override
+    public String toString() {
+        return "CipherInfo [keyExchange=" + keyExchange + ", cipher=" + cipher + ", keySize=" + keySize + ", cipherChaining=" + cipherChaining + ", cipherPadding=" + cipherPadding + ", macName=" + macName + ", macSize=" + macSize + "]";
+    }
+
+    /**
+     * ECDHE<br>
+     * GCM<br>
+     * Cipher {@link #CIPHER_RANKING}<br>
+     * Cipher {@link #keySize}<br>
+     * HMAC<br>
+     * HMAC size<br>
+     * 
+     * @return
+     */
+    @Override
+    public int compareTo(CipherInfo o) {
+        int myStrength = getStrength();
+        int oStrength = o.getStrength();
+        if (myStrength > oStrength) {
+            return -1;
+        }
+        if (myStrength < oStrength) {
+            return 1;
+        }
+        // TODO sort SSL/TLS
+        boolean myEcdhe = keyExchange.startsWith("ECDHE");
+        boolean oEcdhe = o.keyExchange.startsWith("ECDHE");
+        if (myEcdhe && !oEcdhe) {
+            return -1;
+        }
+        if ( !myEcdhe && oEcdhe) {
+            return 1;
+        }
+        boolean myGCM = "GCM".equals(cipherChaining);
+        boolean oGCM = "GCM".equals(o.cipherChaining);
+        if (myGCM && !oGCM) {
+            return -1;
+        }
+        if ( !myGCM && oGCM) {
+            return 1;
+        }
+        if ( !cipher.equals(o.cipher)) {
+
+            for (String testCipher : CIPHER_RANKING) {
+                if (cipher.equals(testCipher)) {
+                    return -1;
+                }
+                if (o.cipher.equals(testCipher)) {
+                    return 1;
+                }
+            }
+            if (cipher.equals("NULL")) {
+                return 1;
+            }
+            if (o.cipher.equals("NULL")) {
+                return -1;
+            }
+        }
+        if (keySize > o.keySize) {
+            return -1;
+        }
+        if (keySize < o.keySize) {
+            return 1;
+        }
+        boolean mySHA = macName.startsWith("SHA");
+        boolean oSHA = o.macName.startsWith("SHA");
+        if ( !mySHA && oSHA) {
+            return -1;
+        }
+        if (mySHA && !oSHA) {
+            return 1;
+        }
+        if (macSize > o.macSize) {
+            return -1;
+        }
+        if (macSize < o.macSize) {
+            return 1;
+        }
+
+        return suiteName.compareTo(o.suiteName);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof CipherInfo) {
+            return 0 == this.compareTo((CipherInfo) o);
+        }
+
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((cipher == null) ? 0 : cipher.hashCode());
+        result = prime * result + ((cipherChaining == null) ? 0 : cipherChaining.hashCode());
+        result = prime * result + ((cipherPadding == null) ? 0 : cipherPadding.hashCode());
+        result = prime * result + ((keyExchange == null) ? 0 : keyExchange.hashCode());
+        result = prime * result + keySize;
+        result = prime * result + ((macName == null) ? 0 : macName.hashCode());
+        result = prime * result + macSize;
+        result = prime * result + ((suiteName == null) ? 0 : suiteName.hashCode());
+        return result;
+    }
+
+    static String[] cipherRanking = null;
+
+    public static String[] getCompleteRanking() {
+        if (cipherRanking == null) {
+            @SuppressWarnings("unchecked")
+            String[] ciphers = filterCiphers((Iterable<String>) cig.names.keySet());
+            cipherRanking = ciphers;
+        }
+        return cipherRanking;
+    }
+
+    private static String[] filterCiphers(Iterable<String> toFilter) {
+        TreeSet<CipherInfo> chosenCiphers = new TreeSet<CipherInfo>();
+        for (String o : toFilter) {
+            String s = o;
+            CipherInfo info = CipherInfo.generateInfo(s);
+            if (info != null) {
+                if (info.getStrength() > 1) {
+                    chosenCiphers.add(info);
+                }
+            }
+        }
+        String[] ciphers = new String[chosenCiphers.size()];
+        int counter = 0;
+        for (CipherInfo i : chosenCiphers) {
+            ciphers[counter++] = i.getSuiteName();
+        }
+        return ciphers;
+    }
+
+    public static String[] filter(String[] supportedCipherSuites) {
+        return filterCiphers(Arrays.asList(supportedCipherSuites));
+    }
+}