Android将当前包的签名与debug.keystore进行比较

时间:2011-05-25 09:42:48

标签: android apk signature

正如我们所做的那样,当它处于开发模式(构建)时,我有一个由debug.keystore(默认情况下)签名的应用程序。当它投入生产时,我们用我们的私钥签名。 有没有办法在运行时确定当前包是用debug.keystore签名的(处于开发模式)还是用我们的私钥签名(处于生产模式)。

我尝试了类似

的内容
    PackageManager packageManager = getPackageManager();
    try {
        Signature[] signs = packageManager.getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES).signatures;
        for (Signature signature : signs) {
            Log.d(TAG, "sign = " + signature.toCharsString());
        }
    } catch (NameNotFoundException e) {
        e.printStackTrace();
    }

我不知道接下来该做什么?这是正确的做法吗?如何获得类似的debug.keystore签名?

我知道存在MD5指纹keytool -list -keystore ~/.android/debug.keystore,但在Signature类中没有“md5指纹”式方法。 我想这样做是因为MapView Key,Logging,LicenseChecker和类似的东西。

2 个答案:

答案 0 :(得分:17)

PackageInfo中的签名似乎没有很好地命名,因为该字段不包含包签名而是包含签名者X509证书链。请注意(大多数情况下)此链似乎仅限于一个自签名证书。

根据Android开发人员页面Signing Your Applications,使用此DN生成调试签名证书:CN=Android Debug,O=Android,C=US

因此,很容易测试应用程序是否已在调试模式下签名:

private static final X500Principal DEBUG_DN = new X500Principal("CN=Android Debug,O=Android,C=US");
/* ... */
Signature raw = packageManager.getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES).signatures[0];
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(raw.toByteArray()));
boolean debug = cert.getSubjectX500Principal().equals(DEBUG_DN);

答案 1 :(得分:3)

基于Jcs' answer,我们使用它来在运行时找出构建运行包的人:

private enum BuildSigner {
    unknown,
    Joe,
    Carl,
    Linda
}

private BuildSigner whoBuiltThis() {
    try {
        PackageManager packageManager = getPackageManager();
        PackageInfo info = packageManager.getPackageInfo(getPackageName(),
                PackageManager.GET_SIGNATURES);
        Signature[] signs = info.signatures;
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        X509Certificate cert = (X509Certificate)cf.generateCertificate(
                new ByteArrayInputStream(signs[0].toByteArray()));
        PublicKey key = cert.getPublicKey();
        int modulusHash = ((RSAPublicKey)key).getModulus().hashCode();
        switch (modulusHash) {
            case 123456789:
                return BuildSigner.Joe;
            case 424242424:
                return BuildSigner.Carl;
            case -975318462:
                return BuildSigner.Linda;
        }
    } catch (Exception e) {
    }

    return BuildSigner.unknown;
}

对于任何涉及的证书,您只需要找到一次哈希并将其添加到列表中。

“一次查找哈希”的最简单方法可能是在显示modulusHash的switch语句之前添加弹出式Toast,编译应用程序,运行它,记下哈希,删除toast代码和将哈希添加到列表中。

或者,当我实现此功能时,我创建了一个带有单个活动的样板应用程序,并在主布局中创建了一个ID为TextView的{​​{1}},并将其放入活动中:

tv

(将@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); int hash = 0; try{ PackageManager packageManager = getPackageManager(); PackageInfo info = packageManager.getPackageInfo( "com.stackexchange.marvin", PackageManager.GET_SIGNATURES); Signature[] signs = info.signatures; CertificateFactory cf = CertificateFactory.getInstance("X.509"); X509Certificate cert = (X509Certificate) cf.generateCertificate( new ByteArrayInputStream(signs[0].toByteArray())); PublicKey key = cert.getPublicKey(); hash = ((RSAPublicKey) key).getModulus().hashCode(); }catch(Exception e){} TextView tv = ((TextView)findViewById(R.id.tv)); tv.setText("The Stack Exchange app's signature hash is " + hash + "."); tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 24); } 更改为您应用的名称),编译此迷你应用,并将APK发送给所有相关开发人员,要求他们在他们的开发设备上运行它,让我知道显示的哈希值。