Android App安装启动时签名校验

时间:2024-04-15 20:26:01

Android发布时签名

  • 分发:即部署,需要签名。若要发布 .APK,必须首先使用签名密钥(也称为证书)对它进行签名。创建新证书(里面包含一对公私钥,个人/公司的信息)

    秘钥的位置默认【加固需要用到】:C:\Users\USERNAME\AppData\Local\Xamarin\Mono for Android\Keystore\

         参考:对 Android 应用程序包进行签名

Android KeyStore以及打包签名

Android KeyStore + FingerprintManager 存储密码

安卓密钥库(允许你存储加密密钥),指纹识别中也有创建key

证书Certificate以及android打包签名

Android上App升级更新

安卓系统本身在安装app时会对app验证签名。

测试步骤: 在本机上有一个低版本的app,然后安装一个高版本的app(分发时用不同的证书签名),

安装提示:安装失败“更新包与已安装应用的签名不一致”

建议卸载,卸载当前版本后,可以安装成功!(这个没法控制用户,,所以可以考虑在app启动时验证签名)

App启动时检测签名是否正确

对比apk签名证书的SHA1值是否自己用的签名证书的值,而不是被别人篡改过的证书的值(防止自己的应用被反编译后重新打包)

1、获取自己对app分发时签名证书的SHA1值

首先进入CMD,然后输入:
keytool -exportcert -list -v -alias huyNew -keystore "C:\Users\huy\AppData\Local\Xamarin\Mono for Android\Keystore\huyNew\huyNew.keystore"
回车

在提示下输入密钥口令(是自己设置的密码) (注:此时输入字符cmd是不显示的,请输入完整后直接回车。如果失败说明你输错了)

参考:

如何使用 keytool 获取签名证书的 SHA-1 哈希值

查找密钥存储的签名

2、Xamarin.Android下获取app证书SHA1值

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Java.IO;
using Java.Security;
using Java.Security.Cert;
using Signature = Android.Content.PM.Signature;

namespace AndroidDevice.Droid
{
    /// <summary>
    /// 验证签名
    /// </summary>
    public class SignCheck
    {
        private Context context;
        /// <summary>
        /// 应用的签名
        /// </summary>
        public String cer = null;
        /// <summary>
        /// 真实的证书
        /// </summary>
        private String realCer = null;
        private static 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;
        }

        /// <summary>
        /// 获取应用的签名
        /// </summary>
        /// <returns></returns>
        public String GetCertificateSHA1Fingerprint()
        {
            //获取包管理器
            PackageManager pm = context.PackageManager;

            //获取当前要获取 SHA1 值的包名,也可以用其他的包名,但需要注意,
            //在用其他包名的前提是,此方法传递的参数 Context 应该是对应包的上下文。
            String packageName = context.PackageName;

            //返回包括在包中的签名信息。。还可以获取签名信息等 PackageInfoFlags.SigningCertificates
            var packageInfo = pm.GetPackageInfo(context.PackageName, PackageInfoFlags.Signatures);

            //签名信息
            List<Signature> signatures = packageInfo.Signatures?.ToList();
            if (signatures == null || signatures.Count == 0)
            {
                return "";
            }
            byte[] cert = signatures[0].ToByteArray();

            //将签名转换为字节数组流
            //InputStream input = new ByteArrayInputStream(cert);
            System.IO.Stream input = new MemoryStream(cert);

            //证书工厂类,这个类实现了出厂合格证算法的功能
            CertificateFactory cf = null;

            try
            {
                cf = CertificateFactory.GetInstance("X509");
            }
            catch (Exception e)
            {
                System.Diagnostics.Debug.WriteLine(TAG + e.StackTrace);
            }

            //X509 证书,X.509 是一种非常通用的证书格式
            X509Certificate c = null;

            try
            {
                c = (X509Certificate)cf.GenerateCertificate(input);
            }
            catch (Exception e)
            {
                System.Diagnostics.Debug.WriteLine(TAG + e.StackTrace);
            }

            String hexString = null;

            try
            {
                //加密算法的类,这里的参数可以使 MD4,MD5 等加密算法
                MessageDigest md = MessageDigest.GetInstance("SHA1");

                //获得公钥
                byte[] publicKey = md.Digest(c.GetEncoded());

                //字节到十六进制的格式转换
                hexString = byte2HexFormatted(publicKey);

            }
            catch (NoSuchAlgorithmException e)
            {
                System.Diagnostics.Debug.WriteLine(TAG + e.StackTrace);
            }
            catch (CertificateEncodingException e)
            {
                System.Diagnostics.Debug.WriteLine(TAG + e.StackTrace);
            }
            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 = arr[i].ToString("X2");
                int l = h.Length;
                if (l == 1)
                    h = "0" + h;
                if (l > 2)
                    h = h.Substring(l - 2, l);
                str.Append(h.ToUpper());
                if (i < (arr.Length - 1))
                    str.Append(\':\');
            }
            return str.ToString();
        }


        /// <summary>
        /// 检测签名是否正确
        /// </summary>
        /// <returns></returns>
        public bool check()
        {
            if (this.realCer != null)
            {
                cer = cer.Trim();
                realCer = realCer.Trim();
                if (this.cer.Equals(this.realCer))
                {
                    return true;
                }
            }
            else
            {
                System.Diagnostics.Debug.WriteLine(TAG + "未给定真实的签名 SHA-1 值");
            }
            return false;
        }
    }
}
View Code

测试

//测试签名
SignCheck signCheck = new SignCheck(this, "27:19:6E:38:6B:87:5E:76:AD:F7:00:E7:EA:84:E4:C6:EE:E3:3D:FA");
if (signCheck.check())
{
//TODO 签名正常
}
else
{
//TODO 签名不正确
Toast.MakeText(this, "请前往官方渠道下载正版 app", ToastLength.Long);
}

LoadApplication(new App(signCheck.cer));
signCheck.cer 为当前app的签名证书SHA1值。判断它与SignCheck构造函数中传入的真实值是否相等即可

参考:Android App运行时签名校验