Androidで自分のアプリケーションの署名チェックを追加するにはどうすればいいですか?

9429 ワード

背景:
私は最近、PMSにメーカー独自のアプリケーション署名チェックを追加する必要があるプロジェクトに遭遇しました.本稿では、追加プロセス全体について説明します.
AndroidはJavaのデジタル証明書に関するメカニズムを使用してapkにデジタル証明書を押すが、Androidデジタル証明書を理解するには、以下のデジタル証明書の概念とjavaのデジタル証明書メカニズムを理解する必要がある.
1、基礎概念
デジタル証明書:デジタル証明書は、デジタル手段を用いてユーザーのアイデンティティを確認する方法です.デジタル証明書には、2つのデータが含まれています.一部は対応する主体(単位または個人)の情報であり、もう一部はこの主体が対応する公開鍵です.すなわち、デジタル証明書は、主体とその公開鍵の1つ1つの対応関係を保存し、自己認証(他のユーザーに自分の身分を証明する)に使用されます.
1)証明書の作成方法:
Javaのkeytool.exeは、すべてのデジタル証明書が1つ(別名で区別される)の形式で証明書ライブラリに格納されるデジタル証明書を作成するために使用できます.証明書ライブラリの1つの証明書には、証明書の秘密鍵、公開鍵、および対応するデジタル証明書の情報が含まれています.証明書ライブラリ内の1つの証明書は、マスター情報と対応する公開鍵のみを含むデジタル証明書ファイルをエクスポートします.
各証明書ライブラリはファイル構成で、アクセスパスワードがあり、最初に作成すると自動的に証明書ライブラリが生成され、証明書ライブラリにアクセスするパスワードを指定する必要があります.
証明書を作成するときは、証明書の情報と証明書に対応する秘密鍵のパスワードを記入する必要があります.たとえば、このコマンド:
keytool -genkey -alias testCA -keyalg RSA -keysize 1024 -keystore testCALib -validity 3650

デジタル証明書ライブラリtestCALibには、RSAアルゴリズムを使用して暗号化された、有効期間3650日のデジタル証明書という別名testCAが作成されています.
証明書が生成されると、名前を使用してデジタル証明書をファイルにエクスポートできます.
keytool -export -alias testCA -file testCA.cer -keystore testALib -rfc

2)署名方法:デジタル証明書の生成後、jarsignerツールを使用する生成されたデジタル証明書をパッケージに署名する必要があります.例えばandroidのパッケージtestがあればapk.,生成したばかりのtestCAを使用して、変更パッケージに署名することができます.
jarsigner -keystore testCALib test.apk. testCA.

また、eclipseのようなツールを使用して署名することもできます.
2、androidプラットフォームの修正
インストールプロセスを適用します.コマンドを使用してインストールするか、APKファイルをクリックしてインストールするかは、基本的にpm install->packageManagerServiceまたはpackageManager->packageManagerServiceまたはpackageInstaller->packageManagerServiceです.
署名検証を追加します.一般的にpackageManagerServiceで実行されます.
属性persistで次のコードを追加します.sys.appcheckは、自分のチェックを追加するかどうかを制御します.
private static boolean isAppStoreCheck() {
return SystemProperties.getBoolean("persist.sys.appcheck",false);
}

次はtestCA.cerは/system/etc/security/の下に置いて、読み取り可能な権限があることを確認します.
// flag indicating whether to check the app store signature before installing.
 private static String sAppStoreCertificatePath = "/system/etc/security/testCA.cer";

初期化時にPMSに追加した署名をロードします
//for app store signature check
private static final Signature sAppStoreSignature = loadAppStoreSignature();

private static Signature loadAppStoreSignature() {
 if(!isAppStoreCheck()) return null;
        try {
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            Certificate appStoreCertificate = certificateFactory.generateCertificate(new FileInputStream(sAppStoreCertificatePath));
            return (new Signature(appStoreCertificate.getEncoded()));
        } catch (Exception e) {
            Slog.i(TAG, "loadAppStoreSignature  failed");
            e.printStackTrace();
        }       
        return null;
 }

インストールされたメイン関数installNewPackageLIを呼び出すときは、まず自分の署名チェックを行い、チェックが不合格の場合は、自分で定義したエラーコードをpackageInstallerに返します.
private void installNewPackageLI(PackageParser.Package pkg,
            int parseFlags, int scanMode, UserHandle user,
            String installerPackageName, PackageInstalledInfo res) {
        // Remember this for later, in case we need to rollback this install
        String pkgName = pkg.packageName;

        // Check whether the app was signed by the app store when it is first installed.
        if (!isSystemApp(pkg)&&(isAppStoreCheck()
                && (checkAppStoreSignature(pkg.mSignatures) != PackageManager.APP_STORE_SIGNATURE_SIGNED))) {
            res.returnCode = PackageManager.INSTALL_PARSE_FAILED_INVALID_APP_STORE_CERTIFICATE;
            return;
        }
        ....
 }       

checkAppStoreSignature関数は、適用された署名をそれぞれ私たちの署名と比較します.
//for app store signature check 
    private int checkAppStoreSignature(Signature[] signatures) {
        if (signatures != null && sAppStoreSignature != null) {
            for (Signature s : signatures) {
                if (s != null) {
                    if (s.equals(sAppStoreSignature)) {
                        return PackageManager.APP_STORE_SIGNATURE_SIGNED;
                    }
                }
            }
        }

        return PackageManager.APP_STORE_SIGNATURE_NOT_SIGNED;
    }   

変更後、プロパティpersistを設定.sys.appcheckがtrueの場合、チェックを追加しますが、チェックはPackageManagerに戻ります.INSTALL_PARSE_FAILED_INVALID_APP_STORE_CERTIFICATE; これは私たちが自分で追加したエラーコードで、packageInstallerでは、自分で処理してユーザーに提示しなければなりません.これは言わないで、簡単です.検証が通過すると、通常のインストールプロセスが実行されます.
3、Certificate関連方法の紹介
loadAppStoreSignature関数で証明書の署名を読み込むには、まずCertificateFactoryを作成し、証明書から証明書オブジェクトを生成して初期化し、最後にこの証明書の符号化形式に戻り、Signatureタイプの署名オブジェクトを取得します.
使用する関数について説明します.
getInstance

public static final CertificateFactory getInstance(String type)
throws CertificateException

              CertificateFactory   。                       ,            CertificateFactory   。              ,      。

  :
type -            。           ,   《Java Cryptography Architecture API Specification & ReferenceA
  :
      CertificateFactory   。
  :
CertificateException -                                    。
generateCertificate

public final Certificate generateCertificate(InputStream inStream)
throws CertificateException
                inStream              。
      CertificateFactory            ,                       。  ,    CertificateFactory    X.509   ,                   X509Certificate  。

    X.509     CertificateFactory    ,inStream           DER    ,             (Base64)       。    Base64           ,        -----BEGIN CERTIFICATE-----     ,  -----END CERTIFICATE-----     。

  ,            mark   reset,            。  ,              ,                                 。                     (    EOF),                ,    CertificateException。

  :
inStream -           。
  :
                  。
  :
CertificateException -         。
getEncoded

public abstract byte[] getEncoded()
throws CertificateEncodingException
          。                ;   X.509      ASN.1 DER        。
  :
        
  :
CertificateEncodingException -         。