Credential.java revision 4e940bb499f65b4305dbeb2e01237c43c2b0b42d
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;
20
21public class Credential {
22    public enum CertType {IEEE, x509v3}
23
24    public static final String CertTypeX509 = "x509v3";
25    public static final String CertTypeIEEE = "802.1ar";
26
27    private final long mCtime;
28    private final long mExpTime;
29    private final String mRealm;
30    private final boolean mCheckAAACert;
31
32    private final String mUserName;
33    private final String mPassword;
34    private final boolean mMachineManaged;
35    private final String mSTokenApp;
36    private final boolean mShare;
37    private final EAPMethod mEAPMethod;
38
39    private final CertType mCertType;
40    private final byte[] mFingerPrint;
41
42    private final String mImsi;
43
44    public Credential(long ctime, long expTime, String realm, boolean checkAAACert,
45                      EAPMethod eapMethod, String userName, String password,
46                      boolean machineManaged, String stApp, boolean share) {
47        mCtime = ctime;
48        mExpTime = expTime;
49        mRealm = realm;
50        mCheckAAACert = checkAAACert;
51        mEAPMethod = eapMethod;
52        mUserName = userName;
53
54        if (!TextUtils.isEmpty(password)) {
55            byte[] pwOctets = Base64.decode(password, Base64.DEFAULT);
56            mPassword = new String(pwOctets, StandardCharsets.UTF_8);
57        } else {
58            mPassword = null;
59        }
60
61        mMachineManaged = machineManaged;
62        mSTokenApp = stApp;
63        mShare = share;
64
65        mCertType = null;
66        mFingerPrint = null;
67
68        mImsi = null;
69    }
70
71    public Credential(long ctime, long expTime, String realm, boolean checkAAACert,
72                      EAPMethod eapMethod, Credential.CertType certType, byte[] fingerPrint) {
73        mCtime = ctime;
74        mExpTime = expTime;
75        mRealm = realm;
76        mCheckAAACert = checkAAACert;
77        mEAPMethod = eapMethod;
78        mCertType = certType;
79        mFingerPrint = fingerPrint;
80
81        mUserName = null;
82        mPassword = null;
83        mMachineManaged = false;
84        mSTokenApp = null;
85        mShare = false;
86
87        mImsi = null;
88    }
89
90    public Credential(long ctime, long expTime, String realm, boolean checkAAACert,
91                      EAPMethod eapMethod, String imsi) {
92        mCtime = ctime;
93        mExpTime = expTime;
94        mRealm = realm;
95        mCheckAAACert = checkAAACert;
96        mEAPMethod = eapMethod;
97        mImsi = imsi;
98
99        mCertType = null;
100        mFingerPrint = null;
101
102        mUserName = null;
103        mPassword = null;
104        mMachineManaged = false;
105        mSTokenApp = null;
106        mShare = false;
107    }
108
109    public Credential(WifiEnterpriseConfig enterpriseConfig, KeyStore keyStore) throws IOException {
110        mCtime = 0;
111        mExpTime = 0;
112        mRealm = enterpriseConfig.getRealm();
113        mCheckAAACert = true;
114        mEAPMethod = mapEapMethod(enterpriseConfig.getEapMethod(),
115                enterpriseConfig.getPhase2Method());
116        mCertType = mEAPMethod.getEAPMethodID() == EAP.EAPMethodID.EAP_TLS ? CertType.x509v3 : null;
117        byte[] fingerPrint;
118
119        if (enterpriseConfig.getClientCertificate() != null) {
120            // !!! Not sure this will be true in any practical instances:
121            try {
122                MessageDigest digester = MessageDigest.getInstance("SHA-256");
123                fingerPrint = digester.digest(enterpriseConfig.getClientCertificate().getEncoded());
124            }
125            catch (GeneralSecurityException gse) {
126                Log.e("CRED", "Failed to generate certificate fingerprint: " + gse);
127                fingerPrint = null;
128            }
129        }
130        else if (enterpriseConfig.getClientCertificateAlias() != null) {
131            String alias = enterpriseConfig.getClientCertificateAlias();
132            Log.d("HS2J", "Client alias '" + alias + "'");
133            byte[] octets = keyStore.get(Credentials.USER_CERTIFICATE + alias);
134            Log.d("HS2J", "DER: " + (octets == null ? "-" : Integer.toString(octets.length)));
135            if (octets != null) {
136                try {
137                    MessageDigest digester = MessageDigest.getInstance("SHA-256");
138                    fingerPrint = digester.digest(octets);
139                }
140                catch (GeneralSecurityException gse) {
141                    Log.e("HS2J", "Failed to construct digest: " + gse);
142                    fingerPrint = null;
143                }
144            }
145            else // !!! The current alias is *not* derived from the fingerprint...
146            {
147                try {
148                    fingerPrint = Base64.decode(enterpriseConfig.getClientCertificateAlias(),
149                            Base64.DEFAULT);
150                } catch (IllegalArgumentException ie) {
151                    Log.e("CRED", "Bad base 64 alias");
152                    fingerPrint = null;
153                }
154            }
155        }
156        else {
157            fingerPrint = null;
158        }
159        mFingerPrint = fingerPrint;
160        mImsi = enterpriseConfig.getPlmn();
161        mUserName = enterpriseConfig.getIdentity();
162        mPassword = enterpriseConfig.getPassword();
163        mMachineManaged = false;
164        mSTokenApp = null;
165        mShare = false;
166    }
167
168    public static CertType mapCertType(String certType) throws OMAException {
169        if (certType.equalsIgnoreCase(CertTypeX509)) {
170            return CertType.x509v3;
171        } else if (certType.equalsIgnoreCase(CertTypeIEEE)) {
172            return CertType.IEEE;
173        } else {
174            throw new OMAException("Invalid cert type: '" + certType + "'");
175        }
176    }
177
178    private static EAPMethod mapEapMethod(int eapMethod, int phase2Method) throws IOException {
179        if (eapMethod == WifiEnterpriseConfig.Eap.TLS) {
180            return new EAPMethod(EAP.EAPMethodID.EAP_TLS, null);
181        } else if (eapMethod == WifiEnterpriseConfig.Eap.TTLS) {
182            /* keep this table in sync with WifiEnterpriseConfig.Phase2 enum */
183            NonEAPInnerAuth inner;
184            switch (phase2Method) {
185                case WifiEnterpriseConfig.Phase2.PAP:
186                    inner = new NonEAPInnerAuth(NonEAPInnerAuth.NonEAPType.PAP);
187                    break;
188                case WifiEnterpriseConfig.Phase2.MSCHAP:
189                    inner = new NonEAPInnerAuth(NonEAPInnerAuth.NonEAPType.MSCHAP);
190                    break;
191                case WifiEnterpriseConfig.Phase2.MSCHAPV2:
192                    inner = new NonEAPInnerAuth(NonEAPInnerAuth.NonEAPType.MSCHAPv2);
193                    break;
194                default:
195                    throw new IOException("TTLS phase2 method " +
196                            phase2Method + " not valid for Passpoint");
197            }
198            return new EAPMethod(EAP.EAPMethodID.EAP_TTLS, inner);
199        } else if (eapMethod == WifiEnterpriseConfig.Eap.PEAP) {
200            /* restricting passpoint implementation from using PEAP */
201            return null;
202        } else if (eapMethod == WifiEnterpriseConfig.Eap.PWD) {
203            /* restricting passpoint implementation from using EAP_PWD */
204            return null;
205        } else if (eapMethod == WifiEnterpriseConfig.Eap.SIM) {
206            return new EAPMethod(EAP.EAPMethodID.EAP_SIM, null);
207        } else if (eapMethod == WifiEnterpriseConfig.Eap.AKA) {
208            return new EAPMethod(EAP.EAPMethodID.EAP_AKA, null);
209        }
210        /*
211            TODO: Uncomment this when AKA_PRIME is defined in WifiEnterpriseConfig
212        else if (eapMethod == WifiEnterpriseConfig.Eap.AKA_PRIME) {
213            return new EAPMethod(EAP.EAPMethodID.EAP_AKAPrim, null);
214        }
215        */
216
217        Log.d("PARSE-LOG", "Invalid eap method");
218        return null;
219    }
220
221    public EAPMethod getEAPMethod() {
222        return mEAPMethod;
223    }
224
225    public String getRealm() {
226        return mRealm;
227    }
228
229    public String getImsi() {
230        return mImsi;
231    }
232
233    public String getUserName() {
234        return mUserName;
235    }
236
237    public String getPassword() {
238        return mPassword;
239    }
240
241    public CertType getCertType() {
242        return mCertType;
243    }
244
245    public byte[] getFingerPrint() {
246        return mFingerPrint;
247    }
248
249    @Override
250    public String toString() {
251        return "Credential{" +
252                "mCtime=" + Utils.toUTCString(mCtime) +
253                ", mExpTime=" + Utils.toUTCString(mExpTime) +
254                ", mRealm='" + mRealm + '\'' +
255                ", mCheckAAACert=" + mCheckAAACert +
256                ", mUserName='" + mUserName + '\'' +
257                ", mPassword='" + mPassword + '\'' +
258                ", mMachineManaged=" + mMachineManaged +
259                ", mSTokenApp='" + mSTokenApp + '\'' +
260                ", mShare=" + mShare +
261                ", mEAPMethod=" + mEAPMethod +
262                ", mCertType=" + mCertType +
263                ", mFingerPrint=" + Utils.toHexString(mFingerPrint) +
264                ", mImsi='" + mImsi + '\'' +
265                '}';
266    }
267}
268