1ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistpackage com.android.hotspot2.osu; 2ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 3ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport android.util.Log; 4ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 5ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.io.IOException; 6ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.net.Socket; 7ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.security.GeneralSecurityException; 8ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.security.KeyStore; 9ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.security.KeyStoreException; 10ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.security.Principal; 11ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.security.PrivateKey; 12ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.security.cert.Certificate; 13ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.security.cert.X509Certificate; 14ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.ArrayList; 15ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.Collections; 16ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.Enumeration; 17ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.HashMap; 18ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.HashSet; 19ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.List; 20ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.Map; 21ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport java.util.Set; 22ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 23ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport javax.net.ssl.X509KeyManager; 24ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistimport javax.security.auth.x500.X500Principal; 25ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 26ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvistpublic class WiFiKeyManager implements X509KeyManager { 27ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final KeyStore mKeyStore; 28ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final Map<X500Principal, String[]> mAliases = new HashMap<>(); 29ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 30ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public WiFiKeyManager(KeyStore keyStore) throws IOException { 31ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mKeyStore = keyStore; 32ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 33ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 34ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public void enableClientAuth(List<String> issuerNames) throws GeneralSecurityException, 35ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist IOException { 36ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 37ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Set<X500Principal> acceptedIssuers = new HashSet<>(); 38ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist for (String issuerName : issuerNames) { 39ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist acceptedIssuers.add(new X500Principal(issuerName)); 40ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 41ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 42ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Enumeration<String> aliases = mKeyStore.aliases(); 43ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist while (aliases.hasMoreElements()) { 44ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist String alias = aliases.nextElement(); 45ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Certificate cert = mKeyStore.getCertificate(alias); 46ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if ((cert instanceof X509Certificate) && mKeyStore.getKey(alias, null) != null) { 47ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist X509Certificate x509Certificate = (X509Certificate) cert; 48ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist X500Principal issuer = x509Certificate.getIssuerX500Principal(); 49ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (acceptedIssuers.contains(issuer)) { 50ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mAliases.put(issuer, new String[]{alias, cert.getPublicKey().getAlgorithm()}); 51ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 52ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 53ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 54ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 55ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (mAliases.isEmpty()) { 56ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist throw new IOException("No aliases match requested issuers: " + issuerNames); 57ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 58ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 59ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 60ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private static class AliasEntry implements Comparable<AliasEntry> { 61ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final int mPreference; 62ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private final String mAlias; 63ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 64ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist private AliasEntry(int preference, String alias) { 65ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mPreference = preference; 66ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist mAlias = alias; 67ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 68ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 69ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public int getPreference() { 70ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mPreference; 71ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 72ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 73ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public String getAlias() { 74ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return mAlias; 75ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 76ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 77ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist @Override 78ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public int compareTo(AliasEntry other) { 79ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return Integer.compare(getPreference(), other.getPreference()); 80ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 81ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 82ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 83ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist @Override 84ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) { 85ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 86ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Map<String, Integer> keyPrefs = new HashMap<>(keyTypes.length); 87ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist int pref = 0; 88ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist for (String keyType : keyTypes) { 89ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist keyPrefs.put(keyType, pref++); 90ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 91ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 92ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist List<AliasEntry> aliases = new ArrayList<>(); 93ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (issuers != null) { 94ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist for (Principal issuer : issuers) { 95ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (issuer instanceof X500Principal) { 96ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist String[] aliasAndKey = mAliases.get((X500Principal) issuer); 97ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (aliasAndKey != null) { 98ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Integer preference = keyPrefs.get(aliasAndKey[1]); 99ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (preference != null) { 100ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist aliases.add(new AliasEntry(preference, aliasAndKey[0])); 101ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 102ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 103ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 104ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 105ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } else { 106ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist for (String[] aliasAndKey : mAliases.values()) { 107ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Integer preference = keyPrefs.get(aliasAndKey[1]); 108ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (preference != null) { 109ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist aliases.add(new AliasEntry(preference, aliasAndKey[0])); 110ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 111ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 112ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 113ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Collections.sort(aliases); 114ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return aliases.isEmpty() ? null : aliases.get(0).getAlias(); 115ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 116ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 117ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist @Override 118ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public String[] getClientAliases(String keyType, Principal[] issuers) { 119ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist List<String> aliases = new ArrayList<>(); 120ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (issuers != null) { 121ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist for (Principal issuer : issuers) { 122ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (issuer instanceof X500Principal) { 123ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist String[] aliasAndKey = mAliases.get((X500Principal) issuer); 124ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (aliasAndKey != null) { 125ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist aliases.add(aliasAndKey[0]); 126ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 127ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 128ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 129ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } else { 130ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist for (String[] aliasAndKey : mAliases.values()) { 131ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist aliases.add(aliasAndKey[0]); 132ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 133ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 134ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return aliases.isEmpty() ? null : aliases.toArray(new String[aliases.size()]); 135ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 136ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 137ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist @Override 138ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { 139ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist throw new UnsupportedOperationException(); 140ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 141ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 142ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist @Override 143ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public String[] getServerAliases(String keyType, Principal[] issuers) { 144ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist throw new UnsupportedOperationException(); 145ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 146ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 147ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist @Override 148ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public X509Certificate[] getCertificateChain(String alias) { 149ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist try { 150ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist List<X509Certificate> certs = new ArrayList<>(); 151ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist for (Certificate certificate : mKeyStore.getCertificateChain(alias)) { 152ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist if (certificate instanceof X509Certificate) { 153ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist certs.add((X509Certificate) certificate); 154ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 155ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 156ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return certs.toArray(new X509Certificate[certs.size()]); 157ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } catch (KeyStoreException kse) { 158ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Log.w(OSUManager.TAG, "Failed to retrieve certificates: " + kse); 159ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return null; 160ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 161ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 162ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist 163ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist @Override 164ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist public PrivateKey getPrivateKey(String alias) { 165ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist try { 166ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return (PrivateKey) mKeyStore.getKey(alias, null); 167ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } catch (GeneralSecurityException gse) { 168ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist Log.w(OSUManager.TAG, "Failed to retrieve private key: " + gse); 169ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist return null; 170ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 171ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist } 172ee699a61a5687d7c8518b639a940c8e9d1b384ddJan Nordqvist} 173