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