Android11 Wifi 加密类型详解

Android Wifi 加密类型详解

有些项目中是不用原生Settings,是自己开发的设置,连接WPA/WPA2是没有问题的,
但是连接EAP和WPA3居然连接不上,发现自己的代码中并没有对这两种加密类型的wifi就行判断,导致连接不上。

如果你是类似的问题或者自己开发的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 后面有介绍。

二、Android 中Wifi加密类型

1、代码中的声明

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;

2、Wifi加密协议简介

下面是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,但是能提供更加完善的安全保护。

3、代码中配置加密类型

连接对应类型的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);
        }
    }

4、系统源码应用中设置加密类型

在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方法。

(1)原生Settings应用

比如,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的修改。

(2)原生TvSettings应用

也可以对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版本里面支持的加密协议有一定的变化,一般来说是会变多。

5、获取加密类型

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连接

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代码问题。

共勉: 逆水行舟不进则退。其实很多时候,某一方面的知识就那么多,你多学一点,就多会一点。

猜你喜欢

转载自blog.csdn.net/wenzhi20102321/article/details/126219495