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