Android11 WAPI证书安装流程

2021-08-10 11:13:44 浏览数 (1)

最近遇到几个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 errorn" , __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证书了,如果匹配成功则安装证书,安装成功。

0 人点赞