Ionic签名校验验证
1. 为什么要进行签名校验
-
有些应用会使用工具对应用进行扫描进行判断所存在的风险,特别一些国企,金融,安全类等应用,其中扫描有一项是应用签名未校验风险。
-
危害的风险描述: 签名证书是对App开发者身份的唯一标识,开发者可利用签名证书有效降低App的盗版率,。未进行签名证书的App,可能被反编译后进行二次打包。重新打包签名的应用,可能导致App被仿冒盗版,影响其合法收入,甚至可能被添钓鱼代码、病毒代码、恶意代码,导致用户敏感信息泄露或者恶意攻击。
-
因此针对这个需求需要对应用进行处理,可以检测App程序启动时是否校验签名证书。增加签名证书的校验代码,降低App被二次打包的几率。
-
通常会对应用进行几步操作: 1.增加签名校验。 2.对App进行加固。3.对加固的App进行二次签名。
2. 查看安卓证书信息
Ionic移动开发完成后,需要进行打包操作,此时需要使用证书进行打包。
证书可以自己进行生成。那如何查看证书生成后的信息呢?
# 在证书的文件根目录运行
keytool -list -v -keystore mykeystorefile(证书名称)
# 输入密钥库口令,这时请输入证书生成时所用到的密码
钥库类型: JKS
密钥库提供方: SUN
您的密钥库包含 1 个条目
别名: com.zhangguoye.app
创建日期: 2018-1-26
条目类型: PrivateKeyEntry
证书链长度: 1
证书[1]:
所有者: CN=com.zhangguoye.app, OU=com.zhangguoye.app, O=com.zhangguoye.app
发布者: CN=com.zhangguoye.app, OU=com.zhangguoye.app, O=com.zhangguoye.app
序列号: 1230f945
有效期开始日期: Tue Feb 26 15:04:52 CST 2018, 截止日期: Sat Feb 20 15:04:52 CST 2020
证书指纹:
MD5: 12:34:56:68:6A:78:22:90:12:FE:12:95:12F:6E:29:12
SHA1: 12:23:45:E1:D1:01:A2:12:37:B2:E3:B1:79:21:16:FD:0B:8D:3E:EB
SHA256: 12:10:37:15:73:D1:11:F1:32:23:33:63:12:9A:12:12:ED:12:12:12:12:2D:02:12:22:4A:52:62:92:51:22:01
签名算法名称: S*********
版本: 3
扩展:
.....
*******************************************
*******************************************
3. 编写检验文件
3.1 编写校验文件SignCheck.java
package com.zhangguoye.app; // 你的包名
import android.app.AlertDialog;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.content.DialogInterface;
import android.util.Log;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
public class SignCheck {
private Context context;
private String cer = null;
private String realCer = null;
private static final String TAG = "SignCheck";
public SignCheck(Context context) {
this.context = context;
this.cer = getCertificateSHA1Fingerprint();
}
public SignCheck(Context context, String realCer) {
this.context = context;
this.realCer = realCer;
this.cer = getCertificateSHA1Fingerprint();
}
public String getRealCer() {
return realCer;
}
/**
* 设置正确的签名
*
* @param realCer
*/
public void setRealCer(String realCer) {
this.realCer = realCer;
}
/**
* 获取应用的签名
*
* @return
*/
public String getCertificateSHA1Fingerprint() {
//获取包管理器
PackageManager pm = context.getPackageManager();
//获取当前要获取 SHA1 值的包名,也可以用其他的包名,但需要注意,
//在用其他包名的前提是,此方法传递的参数 Context 应该是对应包的上下文。
String packageName = context.getPackageName();
//返回包括在包中的签名信息
int flags = PackageManager.GET_SIGNATURES;
PackageInfo packageInfo = null;
try {
//获得包的所有内容信息类
packageInfo = pm.getPackageInfo(packageName, flags);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
//签名信息
Signature[] signatures = packageInfo.signatures;
byte[] cert = signatures[0].toByteArray();
//将签名转换为字节数组流
InputStream input = new ByteArrayInputStream(cert);
//证书工厂类,这个类实现了出厂合格证算法的功能
CertificateFactory cf = null;
try {
cf = CertificateFactory.getInstance("X509");
} catch (Exception e) {
e.printStackTrace();
}
//X509 证书,X.509 是一种非常通用的证书格式
X509Certificate c = null;
try {
c = (X509Certificate) cf.generateCertificate(input);
} catch (Exception e) {
e.printStackTrace();
}
String hexString = null;
try {
//加密算法的类,这里的参数可以使 MD4,MD5 等加密算法
MessageDigest md = MessageDigest.getInstance("SHA1");
//获得公钥
byte[] publicKey = md.digest(c.getEncoded());
//字节到十六进制的格式转换
hexString = byte2HexFormatted(publicKey);
} catch (NoSuchAlgorithmException e1) {
e1.printStackTrace();
} catch (CertificateEncodingException e) {
e.printStackTrace();
}
return hexString;
}
//这里是将获取到得编码进行16 进制转换
private String byte2HexFormatted(byte[] arr) {
StringBuilder str = new StringBuilder(arr.length * 2);
for (int i = 0; i <arr.length; i++) {
String h = Integer.toHexString(arr[i]);
int l =h.length();
if (l == 1)
h = "0" + h;
if (l > 2)
h = h.substring(l - 2, l);
str.append(h.toUpperCase());
if (i < (arr.length - 1))
str.append(':');
}
return str.toString();
}
/**
* 检测签名是否正确
* @return true 签名正常 false 签名不正常
*/
public boolean check() {
if (this.realCer != null) {
cer = cer.trim();
realCer = realCer.trim();
if (this.cer.equals(this.realCer)) {
return true;
}
}else {
Log.e(TAG, "未给定真实的签名 SHA-1 值");
}
return false;
}
public void checkSuccess() {
new AlertDialog.Builder(context)
.setTitle("签名校验成功")
.setMessage("成功!")
.setPositiveButton("确定", null)
.show();
}
public void showCheckErrorTips() {
new AlertDialog.Builder(context)
.setTitle("签名校验失败")
.setMessage("存在签名异常,请在官方下载最新的APP!http://zhangguoye.com")
.setCancelable(false)
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
System.exit(0);
}
})
.show();
}
}
3.2 修改MainActivity.java
修改MainActivity.java,增加检验签名方法
package com.zhangguoye.app; // 你的包名
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Base64;
import org.apache.cordova.*;
import java.io.UnsupportedEncodingException;
public class MainActivity extends CordovaActivity {
public static String param = "";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
// 此处增加方法调用
singCheck();
// ...
}
/**
* 增加签名检验方法
**/
private void singCheck() {
SignCheck signCheck = new SignCheck(this, "12:23:45:E1:D1:01:A2:12:37:B2:E3:B1:79:21:16:FD:0B:8D:3E:EB"); // 第2步查看签名指纹的SHA1
if (signCheck.check()) {
// 签名正常
} else {
// 签名异常
signCheck.showCheckErrorTips();
}
}
}
3.3 其他方案
可以编写安卓的方法,通过ionic层使用javascript调用进行通信,同样实现运行时进行签名的校验。
可以封装成签名校验的cordova插件,供ionic项目进行使用。
4. 打包
4.1 移除android平台
ionic cordova platform rm android
4.2 增加android平台版本
ionic cordova platform add android
若需要指定版本则
ionic cordova platform add android@6.5.0
4.3 覆盖MainActivity.java
-
在目录下找到此文件并覆盖
platforms/android/src/com/zhangguoye
因为目录可能跟你的不同,你可以直接在项目里搜索此文件进行覆盖 -
在
MainActivity.java
当前目录加入文件SignCheck.java
4.4 使用证书进行打包
ionic cordova build android --device --minifycss --minfyjs --optimizejs --prod --release -- -- --keystore=./certification/keystore/zhangguoye --alias=com.zhangguoye.app
- 注意
keystore=./certification/keystore/zhangguoye --alias=com.zhangguoye.app
这一段是配置成你自己的证书路径和证书名称、证书别名
5. 打包完成安装apk
打包完成,安装apk并启动。
此时若是App被人使用其他证书进行二次签名后,会有对应的提示。
确定后会关闭APP
6. 加固原理
这里有一篇介绍安卓加固的原理文章,可了解下:
https://www.cnblogs.com/my-testing-life/articles/12613496.html