Credential.java revision 820d73615f338d6c71f2d75aba0ad8410e9eed3e
1package com.android.server.wifi.hotspot2.pps; 2 3import android.net.wifi.WifiEnterpriseConfig; 4import android.security.Credentials; 5import android.security.KeyStore; 6import android.text.TextUtils; 7import android.util.Base64; 8import android.util.Log; 9 10import com.android.server.wifi.anqp.eap.EAP; 11import com.android.server.wifi.anqp.eap.EAPMethod; 12import com.android.server.wifi.anqp.eap.NonEAPInnerAuth; 13import com.android.server.wifi.hotspot2.Utils; 14import com.android.server.wifi.hotspot2.omadm.OMAException; 15 16import java.io.IOException; 17import java.nio.charset.StandardCharsets; 18import java.security.GeneralSecurityException; 19import java.security.MessageDigest; 20import java.util.Arrays; 21 22public class Credential { 23 public enum CertType {IEEE, x509v3} 24 25 public static final String CertTypeX509 = "x509v3"; 26 public static final String CertTypeIEEE = "802.1ar"; 27 28 private final long mCtime; 29 private final long mExpTime; 30 private final String mRealm; 31 private final boolean mCheckAAACert; 32 33 private final String mUserName; 34 private final String mPassword; 35 private final boolean mDisregardPassword; 36 private final boolean mMachineManaged; 37 private final String mSTokenApp; 38 private final boolean mShare; 39 private final EAPMethod mEAPMethod; 40 41 private final CertType mCertType; 42 private final byte[] mFingerPrint; 43 44 private final String mImsi; 45 46 public Credential(long ctime, long expTime, String realm, boolean checkAAACert, 47 EAPMethod eapMethod, String userName, String password, 48 boolean machineManaged, String stApp, boolean share) { 49 mCtime = ctime; 50 mExpTime = expTime; 51 mRealm = realm; 52 mCheckAAACert = checkAAACert; 53 mEAPMethod = eapMethod; 54 mUserName = userName; 55 56 if (!TextUtils.isEmpty(password)) { 57 byte[] pwOctets = Base64.decode(password, Base64.DEFAULT); 58 mPassword = new String(pwOctets, StandardCharsets.UTF_8); 59 } else { 60 mPassword = null; 61 } 62 mDisregardPassword = false; 63 64 mMachineManaged = machineManaged; 65 mSTokenApp = stApp; 66 mShare = share; 67 68 mCertType = null; 69 mFingerPrint = null; 70 71 mImsi = null; 72 } 73 74 public Credential(long ctime, long expTime, String realm, boolean checkAAACert, 75 EAPMethod eapMethod, Credential.CertType certType, byte[] fingerPrint) { 76 mCtime = ctime; 77 mExpTime = expTime; 78 mRealm = realm; 79 mCheckAAACert = checkAAACert; 80 mEAPMethod = eapMethod; 81 mCertType = certType; 82 mFingerPrint = fingerPrint; 83 84 mUserName = null; 85 mPassword = null; 86 mDisregardPassword = false; 87 mMachineManaged = false; 88 mSTokenApp = null; 89 mShare = false; 90 91 mImsi = null; 92 } 93 94 public Credential(long ctime, long expTime, String realm, boolean checkAAACert, 95 EAPMethod eapMethod, String imsi) { 96 mCtime = ctime; 97 mExpTime = expTime; 98 mRealm = realm; 99 mCheckAAACert = checkAAACert; 100 mEAPMethod = eapMethod; 101 mImsi = imsi; 102 103 mCertType = null; 104 mFingerPrint = null; 105 106 mUserName = null; 107 mPassword = null; 108 mDisregardPassword = false; 109 mMachineManaged = false; 110 mSTokenApp = null; 111 mShare = false; 112 } 113 114 public Credential(Credential other, String password) { 115 mCtime = other.mCtime; 116 mExpTime = other.mExpTime; 117 mRealm = other.mRealm; 118 mCheckAAACert = other.mCheckAAACert; 119 mUserName = other.mUserName; 120 mPassword = password; 121 mDisregardPassword = other.mDisregardPassword; 122 mMachineManaged = other.mMachineManaged; 123 mSTokenApp = other.mSTokenApp; 124 mShare = other.mShare; 125 mEAPMethod = other.mEAPMethod; 126 mCertType = other.mCertType; 127 mFingerPrint = other.mFingerPrint; 128 mImsi = other.mImsi; 129 } 130 131 public Credential(WifiEnterpriseConfig enterpriseConfig, KeyStore keyStore, boolean update) 132 throws IOException { 133 mCtime = 0; 134 mExpTime = 0; 135 mRealm = enterpriseConfig.getRealm(); 136 mCheckAAACert = true; 137 mEAPMethod = mapEapMethod(enterpriseConfig.getEapMethod(), 138 enterpriseConfig.getPhase2Method()); 139 mCertType = mEAPMethod.getEAPMethodID() == EAP.EAPMethodID.EAP_TLS ? CertType.x509v3 : null; 140 byte[] fingerPrint; 141 142 if (enterpriseConfig.getClientCertificate() != null) { 143 // !!! Not sure this will be true in any practical instances: 144 try { 145 MessageDigest digester = MessageDigest.getInstance("SHA-256"); 146 fingerPrint = digester.digest(enterpriseConfig.getClientCertificate().getEncoded()); 147 } catch (GeneralSecurityException gse) { 148 Log.e(Utils.HS20_TAG, "Failed to generate certificate fingerprint: " + gse); 149 fingerPrint = null; 150 } 151 } else if (enterpriseConfig.getClientCertificateAlias() != null) { 152 String alias = enterpriseConfig.getClientCertificateAlias(); 153 byte[] octets = keyStore.get(Credentials.USER_CERTIFICATE + alias); 154 if (octets != null) { 155 try { 156 MessageDigest digester = MessageDigest.getInstance("SHA-256"); 157 fingerPrint = digester.digest(octets); 158 } catch (GeneralSecurityException gse) { 159 Log.e("HS2J", "Failed to construct digest: " + gse); 160 fingerPrint = null; 161 } 162 } else // !!! The current alias is *not* derived from the fingerprint... 163 { 164 try { 165 fingerPrint = Base64.decode(enterpriseConfig.getClientCertificateAlias(), 166 Base64.DEFAULT); 167 } catch (IllegalArgumentException ie) { 168 Log.e(Utils.HS20_TAG, "Bad base 64 alias"); 169 fingerPrint = null; 170 } 171 } 172 } else { 173 fingerPrint = null; 174 } 175 mFingerPrint = fingerPrint; 176 mImsi = enterpriseConfig.getPlmn(); 177 mUserName = enterpriseConfig.getIdentity(); 178 mPassword = enterpriseConfig.getPassword(); 179 mDisregardPassword = update && mPassword.length() < 2; 180 mMachineManaged = false; 181 mSTokenApp = null; 182 mShare = false; 183 } 184 185 public static CertType mapCertType(String certType) throws OMAException { 186 if (certType.equalsIgnoreCase(CertTypeX509)) { 187 return CertType.x509v3; 188 } else if (certType.equalsIgnoreCase(CertTypeIEEE)) { 189 return CertType.IEEE; 190 } else { 191 throw new OMAException("Invalid cert type: '" + certType + "'"); 192 } 193 } 194 195 private static EAPMethod mapEapMethod(int eapMethod, int phase2Method) throws IOException { 196 switch (eapMethod) { 197 case WifiEnterpriseConfig.Eap.TLS: 198 return new EAPMethod(EAP.EAPMethodID.EAP_TLS, null); 199 case WifiEnterpriseConfig.Eap.TTLS: 200 /* keep this table in sync with WifiEnterpriseConfig.Phase2 enum */ 201 NonEAPInnerAuth inner; 202 switch (phase2Method) { 203 case WifiEnterpriseConfig.Phase2.PAP: 204 inner = new NonEAPInnerAuth(NonEAPInnerAuth.NonEAPType.PAP); 205 break; 206 case WifiEnterpriseConfig.Phase2.MSCHAP: 207 inner = new NonEAPInnerAuth(NonEAPInnerAuth.NonEAPType.MSCHAP); 208 break; 209 case WifiEnterpriseConfig.Phase2.MSCHAPV2: 210 inner = new NonEAPInnerAuth(NonEAPInnerAuth.NonEAPType.MSCHAPv2); 211 break; 212 default: 213 throw new IOException("TTLS phase2 method " + 214 phase2Method + " not valid for Passpoint"); 215 } 216 return new EAPMethod(EAP.EAPMethodID.EAP_TTLS, inner); 217 case WifiEnterpriseConfig.Eap.SIM: 218 return new EAPMethod(EAP.EAPMethodID.EAP_SIM, null); 219 case WifiEnterpriseConfig.Eap.AKA: 220 return new EAPMethod(EAP.EAPMethodID.EAP_AKA, null); 221 case WifiEnterpriseConfig.Eap.AKA_PRIME: 222 return new EAPMethod(EAP.EAPMethodID.EAP_AKAPrim, null); 223 default: 224 String methodName; 225 if (eapMethod >= 0 && eapMethod < WifiEnterpriseConfig.Eap.strings.length) { 226 methodName = WifiEnterpriseConfig.Eap.strings[eapMethod]; 227 } else { 228 methodName = Integer.toString(eapMethod); 229 } 230 throw new IOException("EAP method id " + methodName + " is not valid for Passpoint"); 231 } 232 } 233 234 public EAPMethod getEAPMethod() { 235 return mEAPMethod; 236 } 237 238 public String getRealm() { 239 return mRealm; 240 } 241 242 public String getImsi() { 243 return mImsi; 244 } 245 246 public String getUserName() { 247 return mUserName; 248 } 249 250 public String getPassword() { 251 return mPassword; 252 } 253 254 public boolean hasDisregardPassword() { 255 return mDisregardPassword; 256 } 257 258 public CertType getCertType() { 259 return mCertType; 260 } 261 262 public byte[] getFingerPrint() { 263 return mFingerPrint; 264 } 265 266 public long getCtime() { 267 return mCtime; 268 } 269 270 public long getExpTime() { 271 return mExpTime; 272 } 273 274 @Override 275 public boolean equals(Object o) { 276 if (this == o) return true; 277 if (o == null || getClass() != o.getClass()) return false; 278 279 Credential that = (Credential) o; 280 281 if (mCheckAAACert != that.mCheckAAACert) return false; 282 if (mCtime != that.mCtime) return false; 283 if (mExpTime != that.mExpTime) return false; 284 if (mMachineManaged != that.mMachineManaged) return false; 285 if (mShare != that.mShare) return false; 286 if (mCertType != that.mCertType) return false; 287 if (!mEAPMethod.equals(that.mEAPMethod)) return false; 288 if (!Arrays.equals(mFingerPrint, that.mFingerPrint)) return false; 289 if (!safeEquals(mImsi, that.mImsi)) { 290 return false; 291 } 292 293 if (!mDisregardPassword && !safeEquals(mPassword, that.mPassword)) { 294 return false; 295 } 296 297 if (!mRealm.equals(that.mRealm)) return false; 298 if (!safeEquals(mSTokenApp, that.mSTokenApp)) { 299 return false; 300 } 301 if (!safeEquals(mUserName, that.mUserName)) { 302 return false; 303 } 304 305 return true; 306 } 307 308 private static boolean safeEquals(String s1, String s2) { 309 if (s1 == null) { 310 return s2 == null; 311 } 312 else { 313 return s2 != null && s1.equals(s2); 314 } 315 } 316 317 @Override 318 public int hashCode() { 319 int result = (int) (mCtime ^ (mCtime >>> 32)); 320 result = 31 * result + (int) (mExpTime ^ (mExpTime >>> 32)); 321 result = 31 * result + mRealm.hashCode(); 322 result = 31 * result + (mCheckAAACert ? 1 : 0); 323 result = 31 * result + (mUserName != null ? mUserName.hashCode() : 0); 324 result = 31 * result + (mPassword != null ? mPassword.hashCode() : 0); 325 result = 31 * result + (mMachineManaged ? 1 : 0); 326 result = 31 * result + (mSTokenApp != null ? mSTokenApp.hashCode() : 0); 327 result = 31 * result + (mShare ? 1 : 0); 328 result = 31 * result + mEAPMethod.hashCode(); 329 result = 31 * result + (mCertType != null ? mCertType.hashCode() : 0); 330 result = 31 * result + (mFingerPrint != null ? Arrays.hashCode(mFingerPrint) : 0); 331 result = 31 * result + (mImsi != null ? mImsi.hashCode() : 0); 332 return result; 333 } 334 335 @Override 336 public String toString() { 337 return "Credential{" + 338 "mCtime=" + Utils.toUTCString(mCtime) + 339 ", mExpTime=" + Utils.toUTCString(mExpTime) + 340 ", mRealm='" + mRealm + '\'' + 341 ", mCheckAAACert=" + mCheckAAACert + 342 ", mUserName='" + mUserName + '\'' + 343 ", mPassword='" + mPassword + '\'' + 344 ", mDisregardPassword=" + mDisregardPassword + 345 ", mMachineManaged=" + mMachineManaged + 346 ", mSTokenApp='" + mSTokenApp + '\'' + 347 ", mShare=" + mShare + 348 ", mEAPMethod=" + mEAPMethod + 349 ", mCertType=" + mCertType + 350 ", mFingerPrint=" + Utils.toHexString(mFingerPrint) + 351 ", mImsi='" + mImsi + '\'' + 352 '}'; 353 } 354} 355