前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android11 WAPI证书安装流程

Android11 WAPI证书安装流程

作者头像
用户7557625
发布2021-08-10 11:13:44
1.8K0
发布2021-08-10 11:13:44
举报

最近遇到几个WAPI证书安装的问题,看了几天WAPI的相关代码,这里总结一下。

user证书是以"-----BEGIN CERTIFICATE-----“和”-----BEGIN EC PRIVATE KEY-----“开头,ca证书只有”-----BEGIN CERTIFICATE-----",没有"-----BEGIN EC PRIVATE KEY-----"

代码语言:javascript
复制
private static final String CERT_BEGIN = "-----BEGIN CERTIFICATE-----";
private static final String CERT_END = "-----END CERTIFICATE-----";
private static final String PRIKEY_BEGIN = "-----BEGIN EC PRIVATE KEY-----";
private static final String PRIKEY_END = "-----END EC PRIVATE KEY-----";

indexCertBegin = certContent.indexOf(CERT_BEGIN);
indexCertEnd = certContent.indexOf(CERT_END);
indexPriKeyBegin = certContent.indexOf(PRIKEY_BEGIN);
indexPriKeyEnd = certContent.indexOf(PRIKEY_END);

if(indexCertBegin >= 0 && indexCertEnd > 0)
{
    if(indexPriKeyBegin > 0 && indexPriKeyEnd > 0)
    {
        Log.d(TAG, "user cert file");
        return 1;
    }
    else if(indexPriKeyBegin <= 0 && indexPriKeyEnd <= 0)
    {
        Log.d(TAG, "ca cert file");
        return 2;
    }
    else
    {
        Log.d(TAG, "other cert file 1");
        return 0;
    }
}

Android中WAPI证书管理虽然在设置中,但是他是一个单独的app,在packages/apps/WapiCertManage下面。

一、点击WAPI证书管理,会进入这个activity。然后点击右上角加载证书。 packages/apps/WapiCertManage/src/com/wapi/wapicertmanage/WapiCertManageActivity.java

代码语言:javascript
复制
public boolean onOptionsItemSelected(MenuItem item) {
    // TODO Auto-generated method stub
    super.onOptionsItemSelected(item);

    switch (item.getItemId())
    {
    case MENU_ID_ADD_CERT:
        onAddWapiCertItem("");
        return true;

    default:
        return false;
    }
}

二、这里可以看到,点击按钮以后会先去从SD卡中find证书。如果onFindWapiCertFromSDRoot找到了证书,则弹出安装证书的Dialog。

代码语言:javascript
复制
public boolean onAddWapiCertItem(String userCertName)
{
    if(onFindWapiCertFromSDRoot())
    {
        onShowAddWapiCertDialog(userCertName);
        return true;
    }
    else
    {
        return false;
    }
}

三、接下来看怎么查找WAPI证书,先判断有没有SD卡,注意这里的SD卡是内置SD卡,安装WAPI证书必须把证书放在内置SD卡,不支持手动选择目录。Environment.getExternalStorageDirectory()就是获取内置SD卡。 如果在/sdcard/下发现cer证书或者p12证书,则返回true,否则弹出没有可用的证书。

代码语言:javascript
复制
private boolean onFindWapiCertFromSDRoot() {
    if (! Environment.getExternalStorageState().equals(
        Environment.MEDIA_MOUNTED))
    {
        Log.e(TAG, "No SD card found.");
        Toast.makeText(WapiCertManageActivity.this, R.string.text_not_access_to_SD, Toast.LENGTH_LONG).show();
        return false;
    }

    File sdRoot = Environment.getExternalStorageDirectory();
    if(sdRoot == null)
    {
        Log.e(TAG, "sd root file is null.");
        return false;
    }

    try
    {
        boolean bFind = false;
        File[] fileList = sdRoot.listFiles();
        int length = fileList.length;
        for (int i = 0; i < length; i++)
        {
            if (WapiCertUtil.isTheSuffix(fileList[i].getAbsoluteFile().toString(), ".cer"))
            {
                if (!fileList[i].isDirectory() &&
                    (WapiCertUtil.getCertificateType(fileList[i]) == 1))
                {
                    Log.d(TAG, "Find wapi user cert");
                    bFind = true;
                    break;
                }
            }
            else if(WapiCertUtil.isTheSuffix(fileList[i].getAbsoluteFile().toString(), ".p12"))
            {
                Log.v(TAG, "find a p12 cert ");
                bFind = true;
                break;
            }
            else
            {
                continue;
y            }
        }

        if(bFind)
        {
            return true;
        }
        else
        {
            Toast.makeText(this, R.string.text_not_find_valid_cert_in_SD, Toast.LENGTH_LONG).show();
            return false;
        }
    }
    catch (Exception e)
    {
        e.printStackTrace();
        return false;
    }
}

四、由第二步可知,第三步找到证书以后会弹出安装证书的Dialog

代码语言:javascript
复制
private void onShowAddWapiCertDialog(String userCertName)
{
    WapiCertManageDlg dialog = new WapiCertManageDlg(this, this, userCertName);
    dialog.setWapiCertStore(mWapiCertStore);

    dialog.setMode(WapiCertManageDlg.MODE_ADD_CERT);
    dialog.setTitle(R.string.add_cert_dlg_title_name);
    mAddCertDlg = dialog;
    dialog.show();
}

五、我们看WapiCertManageDlg的代码 packages/apps/WapiCertManage/src/com/wapi/wapicertmanage/WapiCertManageDlg.java

代码语言:javascript
复制
onCreate->onLayout->setLayout->onReferenceViews->setUserCertSpinnerAdapter
代码语言:javascript
复制
protected void onCreate(Bundle savedInstanceState)
{
    onLayout();
    super.onCreate(savedInstanceState);
}

setUserCertSpinnerAdapter就是遍历SD卡并将其中的user证书、ca证书和p12证书分别放到一个list中。最后再设置一下选择器。

代码语言:javascript
复制
private void setUserCertSpinnerAdapter()
{
    Context context = getContext();
    File certificateList [];
    int i = 0;

    mUserCertArray.clear();
    mIssuerCertArray.clear();

    //File certificatePath = WapiCertUtil.getSdCardCertificateFile(null);
    File certificatePath = Environment.getExternalStorageDirectory();
    try{
        if (certificatePath != null)
        {
            certificateList = certificatePath.listFiles();
            for (i = 0; i < certificateList.length; i++)
            {
                //Log.v(TAG, "certificateList[i].getAbsoluteFile().toString():"+certificateList[i].getAbsoluteFile().toString());

                if (WapiCertUtil.isTheSuffix(certificateList[i].getAbsoluteFile().toString(), ".cer"))
                {
                    Log.v(TAG, "certificateList[" + i + "]: " +certificateList[i].getAbsoluteFile().toString());
                      if (!certificateList[i].isDirectory() &&
                        //isUserCertificate(certificateList[i]))
                        (WapiCertUtil.getCertificateType(certificateList[i]) == 1))
                    {
                        Log.d(TAG, "add user cert");
                        mUserCertArray.add(certificateList[i].getName());
                    }
                    else if(!certificateList[i].isDirectory() &&
                        //isUserCertificate(certificateList[i]))
                        (WapiCertUtil.getCertificateType(certificateList[i]) == 2))
                    {
                        Log.d(TAG, "add ca cert");
                        mIssuerCertArray.add(certificateList[i].getName());
                    }
                }
                else if(WapiCertUtil.isTheSuffix(certificateList[i].getAbsoluteFile().toString(), ".p12"))
                {
                    Log.v(TAG, "find a p12 cert ");
                    //if(certificateList[i].length<2048)
                    {
                        mUserCertArray.add(certificateList[i].getName());
                    }
                }
                else
                {
                    //Log.v(TAG, "not rhgit  cert");
                    continue;
                }
            }
        }

        ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(context,
                android.R.layout.simple_spinner_item,
                (String [])mUserCertArray.toArray(new String[0]));
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        mUserCertSpinner.setAdapter(adapter);

        if(! TextUtils.isEmpty(mUserCertName))
        {
            Log.d(TAG, "init, mUserCertName is not empty, set selection");
            setSelection(mUserCertSpinner, mUserCertName);
        }
    }
    catch (Exception e)
    {
        setMessage(e.toString());
    }
}

六、用户选择要安装的证书,然后点击安装

代码语言:javascript
复制
public void onClick(DialogInterface dialog, int which)
{
    Log.v(TAG, "onClick which " + which);

    if (which == mInstallCertButtonPos)
    {
        if(handleInstallCert())
        {
            mWapiCertListenner.notifyInstallCert(mUserCertName, mUserCertPath, mCaCertpath, mUserCert, mPriKey, mCaCert);
        }
        else
        {
            mWapiCertListenner.notifyReopenDlg(mUserCertName);
        }
    }
    else if (which == mCancelButtonPos)
    {
        handleCancle();
    }
}

看一下如果处理安装证书。 如果是p12证书,会去解析证书。如果是ca证书,则会直接读取证书内容。目的都是为了获得证书的user cert和私钥。拿到信息以后会去匹配信息matchWapiCert。

代码语言:javascript
复制
private boolean handleInstallCert()
 {
     Log.d(TAG, "Handle install user cert: " + mUserCertName);

     if (null == mUserCertName || TextUtils.isEmpty(mUserCertName)) {
         Toast.makeText(getContext(), R.string.text_not_find_valid_user_cert_in_SD,Toast.LENGTH_LONG).show();
         return false;
     }

     mUserCertPath = SDCARD_ROOT_PATH + "/" + mUserCertName;
     Log.d(TAG, "user cert path: " + mUserCertPath);

     if(mbP12Cert){
         mP12Pwd = mP12EditText.getText().toString();
         if((mP12Pwd == null) || TextUtils.isEmpty(mP12Pwd)) {
             Toast.makeText(getContext(), R.string.text_p12_password_empty,Toast.LENGTH_LONG).show();
             return false;
         }

         File userCertFile = new File(mUserCertPath);
         byte[] userCertData = WapiCertUtil.readFile(userCertFile);
         UserCertParam userCertParam = mWapiCertStore.parseP12Cert(userCertData, mP12Pwd);

         if(userCertParam.cert.length == 0 || userCertParam.prikey.length == 0) {
             Toast.makeText(getContext(), R.string.text_p12_password_error, Toast.LENGTH_LONG).show();
             return false;
         }
         String strTemp = new String(userCertParam.cert);
         mUserCert = WapiCertUtil.addCertHeader(strTemp);
         mPriKey = userCertParam.prikey;
     }
     else
     {
         File userCertFile = new File(mUserCertPath);
         byte[] userCertData = WapiCertUtil.readFile(userCertFile);
         String strTemp = new String(userCertData);
         mUserCert = WapiCertUtil.getCertElement(strTemp);
         mPriKey = WapiCertUtil.getPriKeyElement(strTemp);
     }

     Log.d(TAG, "user cert: " + (new String(mUserCert)));
     Log.d(TAG, "private key: " + (new String(mPriKey)));

     return matchWapiCert(mUserCert, mPriKey);
 }

七、匹配证书 这里也是遍历之前查到的所以CA证书,看哪个与选的user证书匹配。

代码语言:javascript
复制
private boolean matchWapiCert(byte[] userCert, byte[] priKey) {
    int index = 0;
    int caCertCount = 0;

    caCertCount = mIssuerCertArray.size();
    Log.d(TAG, "ca cert count: " + caCertCount);

    for( index = 0; index < caCertCount; index ++) {
        String caCertName = mIssuerCertArray.get(index);
        String caCertPath = SDCARD_ROOT_PATH + "/" + caCertName;
        Log.d(TAG, "ca cert path: " + caCertPath);

        File caCertFile = new File(caCertPath);
        byte[] caCertData = WapiCertUtil.readFile(caCertFile);
        //String strTemp = new String(caCertData);
        //mCaCert = WapiCertUtil.getCertElement(strTemp);
        if(mWapiCertStore.checkUserCaCert(userCert, priKey, caCertData)) {
            mCaCertpath = caCertPath;
            mCaCert = caCertData;
            return true;
        }
    }

    Toast.makeText(getContext(), R.string.text_not_match_valid_ca_cert_in_SD, Toast.LENGTH_LONG).show();
    return false;
}

八、 packages/apps/WapiCertStore/src/com/wapi/wapicertstore/WapiCertStore.java

代码语言:javascript
复制
public boolean checkUserCaCert(byte[] userCert, byte[] priKey, byte[] caCert) {
    if(checkUserCaCertNative(userCert, priKey, caCert) == 0) {
        return true;
    }
    else {
        return false;
    }
}

真正的实现在这里 external/wpa_supplicant_8/wpa_supplicant/wapi/libwapi_cert/wapi_cert_jni.c

代码语言:javascript
复制
jint
Java_com_wapi_wapicertstore_WapiCertStore_checkUserCaCertNative ( JNIEnv* env ,
        jobject clazz , jbyteArray userCert , jbyteArray priKey ,
        jbyteArray caCert )
{
    (void) (clazz);
    unsigned char *byteUserCert = (unsigned char *) (*env)->GetByteArrayElements ( env ,
            userCert , 0 );
    int userCertLen = (*env)->GetArrayLength ( env , userCert );
    unsigned char *bytePriKey = (unsigned char *) (*env)->GetByteArrayElements ( env , priKey ,
            0 );
    int priKeyLen = (*env)->GetArrayLength ( env , priKey );
    unsigned char *byteCaCert = (unsigned char *) (*env)->GetByteArrayElements ( env , caCert ,
            0 );
    int caCertLen = (*env)->GetArrayLength ( env , caCert );

    int ret = Check_Asue_Asu_Cert ( byteUserCert , userCertLen , bytePriKey ,
            priKeyLen , byteCaCert , caCertLen );
    if ( ret != 0 ) {
        ALOGD("in '%s':'%d' Get UsrCert Or Prikey error\n" , __func__ ,
                __LINE__ );
        return -1;
    }
    return 0;
}

external/wpa_supplicant_8/wpa_supplicant/wapi/libwapi_cert/wapi_cert.c

代码语言:javascript
复制
int
Check_Asue_Asu_Cert ( const unsigned char *user_cert , int user_cert_len ,
        const unsigned char *pri_key , int pri_key_len ,
        const unsigned char *as_cert , int as_cert_len )
{
    unsigned short unpackcert_len = user_cert_len;
    unsigned short prikey_outlen = pri_key_len;
    int asu_outlen = as_cert_len;

    unsigned char *unpack_cert = malloc ( unpackcert_len );
    unsigned char *prikey_out = malloc ( prikey_outlen );
    unsigned char *asucert_out = malloc ( asu_outlen );
    int ret;

    ret = Unpack_AsueCert ( user_cert , user_cert_len , unpack_cert ,
            &unpackcert_len );
    if ( ret != 0 )
    {
        goto error;
    }

    ret = Unpack_AsuePrikey ( pri_key , pri_key_len , prikey_out ,
            &prikey_outlen );
    if ( ret != 0 )
    {
        goto error;
    }

    ret = Unpack_AsuCert ( as_cert , as_cert_len , asucert_out , &asu_outlen );
    if ( ret != 0 )
    {
        goto error;
    }

    if ( IWN_Check_UserCert_by_CACert ( unpack_cert , unpackcert_len ,
            asucert_out , asu_outlen , ECC_P192 ) != 1 )
    {
        goto error;
    }

    if ( IWN_Match_Pub_Pri_key ( unpack_cert , unpackcert_len , prikey_out ,
            prikey_outlen , ECC_P192 ) != 1 )
    {

        goto error;
    }

	free ( asucert_out );
    free ( prikey_out );
    free ( unpack_cert );

	return 0;

error:

    free ( asucert_out );
    free ( prikey_out );
    free ( unpack_cert );

    return -1;
}

再后面就是通过加密算法验证user证书和ca证书了,如果匹配成功则安装证书,安装成功。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-08-05 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档