有些项目中是不用原生Settings,是自己开发的设置,连接WPA/WPA2是没有问题的,
但是连接EAP和WPA3居然连接不上,发现自己的代码中并没有对这两种加密类型的wifi就行判断,导致连接不上。
如果你是类似的问题或者自己开发的wifi连接,并且设计到多种加密形式,可以看看本文内容。
WEP: Wired Equivalent Privacy(有线等效加密) ,
保护无线网络信息安全的体制,无线网络通过无线电传播,为了保证数据机密性而设计。
但是存在一些漏洞,比如针对EAP的密钥截取攻击,和针对数据完整性的Replay Attack(回放攻击),已经不推荐使用。
WPA: WiFi Protected Access (Wi-Fi访问保护),
是一种保护无线网络访问安全的技术标准,WPA是为了解决EAP的几个严重的弱点而产生的,
通过128位密钥加上48位初向量和可以在使用中动态改变密钥的“临时密钥完整性协议”(TKIP),
使用RC4加密、使用Michael消息验证码,避免针对EAP的密钥截取攻击和Replay Attack。
WPA2:WPA的升级版,
采用CCMP计数器模式密码块链消息完整码协议,使用CBC-MAC替代Michael消息验证码,
使用AES取代RC4加密算法。在身份认证和保证数据完整性上更安全。
Android 10之后有了WPA3 后面有介绍。
WifiConfiguration.java (Android 11)
/** Security type for an open network. */
public static final int SECURITY_TYPE_OPEN = 0;
/** Security type for a WEP network. */
public static final int SECURITY_TYPE_WEP = 1;
/** Security type for a PSK network. */
public static final int SECURITY_TYPE_PSK = 2;
/** Security type for an EAP network. */
public static final int SECURITY_TYPE_EAP = 3;
/** Security type for an SAE network. */
public static final int SECURITY_TYPE_SAE = 4;
/** Security type for an EAP Suite B network. */
public static final int SECURITY_TYPE_EAP_SUITE_B = 5;
/** Security type for an OWE network. */
public static final int SECURITY_TYPE_OWE = 6;
/** Security type for a WAPI PSK network. */
public static final int SECURITY_TYPE_WAPI_PSK = 7;
/** Security type for a WAPI Certificate network. */
public static final int SECURITY_TYPE_WAPI_CERT = 8;
在文件 WifiConfiguration.java搜索"Security types we support.",也能看到当前api支持的类型。
0表示不加密。Android11 存在的wifi加密方式8种。
对比查看了Android9.0只有1-6,后面新增的两种也没用过。
Android11 WiFi相关代码改动记录 https://blog.csdn.net/qq_43804080/article/details/112602931
正常手机和路由器发出热点都是0/1/2;
下面是wifi加密介绍,东拼西凑搞过来的:
SECURITY_TYPE_WEP = 1; //比较旧的加密方式,基本很少用,不太安全
更多文字介绍:https://blog.csdn.net/u013403237/article/details/50663790
SECURITY_TYPE_PSK = 2; //目前常用
包含WPA/WPA2
更多文字介绍:https://www.jianshu.com/p/9316c433ec5f/
SECURITY_TYPE_EAP = 3; //非常安全
EAP 的类型,是一种企业验证的安全类型,EAP 全称叫 802.1X/EAP 他常常给误解成 802.11x。
EAP 的意思就是可扩展认证协议的缩写。
最常部署的 EAP 验证类型包括 EAP-MD-5、EAP-TLS、EAP-LEAP、EAP-TTLS、EAP-Fast、EAP-PEAP;
所以EAP网络也是连接wifi参数最多的网络,还需要添加客户端和服务器端的证书安装。
更多文字介绍:https://blog.csdn.net/hl1293348082/article/details/123888636
SECURITY_TYPE_SAE = 4;
SAE最早是802.11s中为mesh网络提出的基于password的认证和key生成协议[1]。在WPA2-PSK中,PMK就是PSK,直接来源于密钥;
而SAE则保证任何STA pair(AP-STA,nonAP-STA)在不同的session都有不同的PMK,使用SAE认证的个人无线网络通常称为WPA3-PSK/WPA3-Personal。
简单的说SAE,就是WPA2的升级,简称WPA3
更多文字介绍:https://blog.csdn.net/qq_23087099/article/details/113921261
SECURITY_TYPE_EAP_SUITE_B = 5;
EAP网络的另一种加密形式,具体介绍查不出
SECURITY_TYPE_OWE = 6;
未查出相关介绍,在当前类中发现,入下介绍,Opportunististic (共产主义),大概是共享网络的一种加密格式吧。
/**
* Opportunististic Wireless Encryption
*/
public static final int OWE = 9;
最后两种:SECURITY_TYPE_WAPI_X
WAPI(无线网络WLANAuthenticationandPrivacyInfrastructure)是我国自主研发并大力推行的无线网络WLAN安全标准,
它通过了IEEE(注意,不是Wi-Fi)认证和授权,是一种认证和私密性保护协议,其作用类似于802.11b中的WEP,但是能提供更加完善的安全保护。
连接对应类型的wifi需要配置里面的config对象,
可以使用 WifiConfiguration 的 setSecurityParams 进行设置。
WifiConfiguration.java
/**
* Set the various security params to correspond to the provided security type.
* This is accomplished by setting the various BitSets exposed in WifiConfiguration.
*
* @param securityType One of the following security types:
* {@link #SECURITY_TYPE_OPEN},
* {@link #SECURITY_TYPE_WEP},
* {@link #SECURITY_TYPE_PSK},
* {@link #SECURITY_TYPE_EAP},
* {@link #SECURITY_TYPE_SAE},
* {@link #SECURITY_TYPE_EAP_SUITE_B},
* {@link #SECURITY_TYPE_OWE},
* {@link #SECURITY_TYPE_WAPI_PSK}, or
* {@link #SECURITY_TYPE_WAPI_CERT}
*/
public void setSecurityParams(@SecurityType int securityType) {
// Clear all the bitsets.
allowedKeyManagement.clear();
allowedProtocols.clear();
allowedAuthAlgorithms.clear();
allowedPairwiseCiphers.clear();
allowedGroupCiphers.clear();
allowedGroupManagementCiphers.clear();
allowedSuiteBCiphers.clear();
switch (securityType) {
case SECURITY_TYPE_OPEN:
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
break;
case SECURITY_TYPE_WEP:
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
break;
case SECURITY_TYPE_PSK:
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
break;
case SECURITY_TYPE_EAP:
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
break;
case SECURITY_TYPE_SAE:
allowedProtocols.set(WifiConfiguration.Protocol.RSN);
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE);
allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256);
allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
requirePmf = true;
break;
case SECURITY_TYPE_EAP_SUITE_B:
allowedProtocols.set(WifiConfiguration.Protocol.RSN);
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192);
allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256);
allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
allowedGroupManagementCiphers.set(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256);
// Note: allowedSuiteBCiphers bitset will be set by the service once the
// certificates are attached to this profile
requirePmf = true;
break;
case SECURITY_TYPE_OWE:
allowedProtocols.set(WifiConfiguration.Protocol.RSN);
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE);
allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.GCMP_256);
allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256);
requirePmf = true;
break;
case SECURITY_TYPE_WAPI_PSK:
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WAPI_PSK);
allowedProtocols.set(WifiConfiguration.Protocol.WAPI);
allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.SMS4);
allowedGroupCiphers.set(WifiConfiguration.GroupCipher.SMS4);
break;
case SECURITY_TYPE_WAPI_CERT:
allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WAPI_CERT);
allowedProtocols.set(WifiConfiguration.Protocol.WAPI);
allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.SMS4);
allowedGroupCiphers.set(WifiConfiguration.GroupCipher.SMS4);
break;
default:
throw new IllegalArgumentException("unknown security type " + securityType);
}
}
在TvSetting和原生Setting里面会调用自己SettingsLib中的AccessPoint的相关api,而里面的加密类型的数值又有点不一样。
AccessPoint 是对WifiConfiguration 的进一步封装对象。
frameworks\base\packages\SettingsLib\src\com\android\settingslib\wifi\AccessPoint.java
public static final int SECURITY_NONE = 0;
public static final int SECURITY_WEP = 1;
public static final int SECURITY_PSK = 2;
public static final int SECURITY_EAP = 3;
public static final int SECURITY_OWE = 4;
public static final int SECURITY_SAE = 5;
public static final int SECURITY_EAP_SUITE_B = 6;
public static final int SECURITY_MAX_VAL = 7;
普通应用中使用api,扫描到的wifi 列表对象是WifiConfiguration对象;
所以你如果使用SettingsLib的api方式,扫描到的AccessPoint列表,
就需要逐个判断类型设置 WifiConfiguration 的setSecurityParams方法来设置加密类型的配置。
看了下原生Settings里面的代码,是自己判断加密类型,自己写一遍setSecurityParams方法。
比如,Settings的WifiConfigController.java的代码
public WifiConfiguration getConfig() {
if (mMode == WifiConfigUiBase.MODE_VIEW) {
return null;
}
WifiConfiguration config = new WifiConfiguration();
switch (mAccessPointSecurity) {
case AccessPoint.SECURITY_NONE:
config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
break;
case AccessPoint.SECURITY_WEP:
// 加密类型配置
config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP);
// 密码设置
if (mPasswordView.length() != 0) {
int length = mPasswordView.length();
String password = mPasswordView.getText().toString();
// WEP-40, WEP-104, and 256-bit WEP (WEP-232?)
if ((length == 10 || length == 26 || length == 58)
&& password.matches("[0-9A-Fa-f]*")) {
config.wepKeys[0] = password;
} else {
config.wepKeys[0] = '"' + password + '"';
}
}
break;
。。。。。。
}
}
大屏开发人员经常参考的是TvSetting的修改。
也可以对config里面的allowedProtocols直接设置,
比如:TvSettings 的WifiSecurityHelper.java
public static WifiConfiguration getConfig(FragmentActivity context) {
WifiConfiguration config = userChoiceInfo.getWifiConfiguration();
if (TextUtils.isEmpty(config.SSID)) {
// If the user adds a network manually, assume that it is hidden.
config.hiddenSSID = true;
}
if (userChoiceInfo.getPageSummary(UserChoiceInfo.SSID) != null) {
config.SSID = AccessPoint.convertToQuotedString(
userChoiceInfo.getPageSummary(UserChoiceInfo.SSID));
}
int accessPointSecurity = getSecurity(context);
String password = userChoiceInfo.getPageSummary(UserChoiceInfo.PASSWORD);
int length = password != null ? password.length() : 0;
switch (accessPointSecurity) {
case AccessPoint.SECURITY_NONE:
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
break;
case AccessPoint.SECURITY_WEP:
// 加密类型配置
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
//设置密码
// WEP-40, WEP-104, and 256-bit WEP (WEP-232?)
if ((length == 10 || length == 26 || length == 58)
&& password.matches("[0-9A-Fa-f]*")) {
config.wepKeys[0] = password;
} else {
config.wepKeys[0] = '"' + password + '"';
}
break;
case AccessPoint.SECURITY_PSK:
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
if (length != 0) {
if (password.matches("[0-9A-Fa-f]{64}")) {
config.preSharedKey = password;
} else {
config.preSharedKey = '"' + password + '"';
}
}
break;
case AccessPoint.SECURITY_SAE:
config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
if (length != 0) {// DroidLogic modify SWPL-45229
config.preSharedKey = '"' + password + '"';
}
break;
case AccessPoint.SECURITY_OWE:
config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
break;
default: //TVSettings只支持上面六种加密方式的wifi,其他的wifi就要参考原生Settings修改了
return null;
}
config.setIpConfiguration(advancedOptionsFlowInfo.getIpConfiguration()); //配置代理/静态ip
return config;
}
}
所以看到下面不同的代码方式对加密类型的配置不要大惊效果
WifiConfiguration config = new WifiConfiguration();
//方式1 配置加密类型
config.setSecurityParams(XX);
//方式2 配置界面类型
本质也是设置下面的加密类型配置(,下面只是示例),不同的加密类型配置的项不同,具体参考WifiConfiguration.java即可。
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
简单的看,使用 WifiConfiguration 的 setSecurityParams 方法是比较简单和保险的,但是确认下类型对应哦。
值得注意的是不同的加密类型,有的设置密码会有一定的差异。
WEP某些情况,PSK(WAP1/2)、SAE(WPA3)的密码如果有特殊符合是要加双引号""包裹的。
具体的代码可以看Settings的WifiConfigController.java的具体获取config对象的代码,不同的Android版本里面支持的加密协议有一定的变化,一般来说是会变多。
WifiConfiguration 对象居然没有get方法获取到它的加密类型!
系统封装的 AccessPoint 对象是有的方法getSecurity 和getSecurityString 获取加密类型和类型字符串;
那么如何从WifiConfiguration对象中获取加密类型呢?
查看里面的代码发现有个隐藏的方法:
/** @hide
* return the SSID + security type in String format.
*/
public String getSsidAndSecurityTypeString() {
String key;
if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
key = SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
} else if (allowedKeyManagement.get(KeyMgmt.WPA_EAP)
|| allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
key = SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP];
} else if (wepTxKeyIndex >= 0 && wepTxKeyIndex < wepKeys.length
&& wepKeys[wepTxKeyIndex] != null) {
key = SSID + "WEP";
} else if (allowedKeyManagement.get(KeyMgmt.OWE)) {
key = SSID + KeyMgmt.strings[KeyMgmt.OWE];
} else if (allowedKeyManagement.get(KeyMgmt.SAE)) {
key = SSID + KeyMgmt.strings[KeyMgmt.SAE];
} else if (allowedKeyManagement.get(KeyMgmt.SUITE_B_192)) {
key = SSID + KeyMgmt.strings[KeyMgmt.SUITE_B_192];
} else if (allowedKeyManagement.get(KeyMgmt.WAPI_PSK)) {
key = SSID + KeyMgmt.strings[KeyMgmt.WAPI_PSK];
} else if (allowedKeyManagement.get(KeyMgmt.WAPI_CERT)) {
key = SSID + KeyMgmt.strings[KeyMgmt.WAPI_CERT];
} else if (allowedKeyManagement.get(KeyMgmt.OSEN)) {
key = SSID + KeyMgmt.strings[KeyMgmt.OSEN];
} else {
key = SSID + KeyMgmt.strings[KeyMgmt.NONE];
}
return key;
}
隐藏的方法没法用啊!
但是发现 allowedKeyManagement 对象是public 的,是可以在外部普通使用的,所以你懂的!
/**
* The set of key management protocols supported by this configuration.
* See {@link KeyMgmt} for descriptions of the values.
* Defaults to WPA-PSK WPA-EAP.
*/
@NonNull
public BitSet allowedKeyManagement;
你可以自己封装getSecurity 和getSecurityString方法使用。
Wifi 连接的主要代码:
protected void connect(WifiConfiguration config) {
WifiManager wifiManager = (WifiManager) getActivity().getSystemService(Context.WIFI_SERVICE);
wifiManager.connect(config, connectListener); //所有的数据都在config对象里面;connectListener 没啥作用,一般传null参数,不管密码是否正确都是回调success。
}
其中WifiConfiguration就是我们要配置的数据,其中包括加密类型数据,密码,代理和静态ip。
系统应用的连接可以参考我之前写的这个:
https://blog.csdn.net/wenzhi20102321/article/details/123675077
本文的wifi加密协议就介绍到这里,如果自己的wiif不能连接某个加密的网络,还是要用原始的Settings连接试试,确认是系统环境原因还是自己的apk代码问题。