最近遇到几个WAPI证书安装的问题,看了几天WAPI的相关代码,这里总结一下。
user证书是以"-----BEGIN CERTIFICATE-----“和”-----BEGIN EC PRIVATE KEY-----“开头,ca证书只有”-----BEGIN CERTIFICATE-----",没有"-----BEGIN EC PRIVATE KEY-----"
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
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。
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,否则弹出没有可用的证书。
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
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
onCreate->onLayout->setLayout->onReferenceViews->setUserCertSpinnerAdapter
protected void onCreate(Bundle savedInstanceState)
{
onLayout();
super.onCreate(savedInstanceState);
}
setUserCertSpinnerAdapter就是遍历SD卡并将其中的user证书、ca证书和p12证书分别放到一个list中。最后再设置一下选择器。
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());
}
}
六、用户选择要安装的证书,然后点击安装
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。
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证书匹配。
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
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
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
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证书了,如果匹配成功则安装证书,安装成功。