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