【问题标题】:Display Android Certificate显示 Android 证书
【发布时间】:2016-09-06 19:26:51
【问题描述】:

我有一个简短的问题:这个调用究竟返回了什么?

context.getPackageManager().getPackageInfo(context.getPackageName(), GET_SIGNATURES).signatures[0].toByteArray();

我知道它会返回应用程序的第一个应用程序证书,即META-INF 文件夹中的CERT.RSA,但它返回的具体内容是什么?只是一个将整个证书表示为文件或其他字节数组的字节数组?我对证书的结构及其包含的数据不太了解,所以我真的没有任何线索。

最好的答案是openssl 的指令,我从上面的代码行中得到返回值。

【问题讨论】:

  • “我对证书的结构和它们包含的数据了解不多,所以我真的没有任何线索。”也许,是时候让您自己研究 openssl 并了解证书是如何创建的了。
  • 一开始我真的很想理解它并找到解决这个问题的“明显”解决方案,但没有成功。我决定问,因为我现在真的不需要这些知识,而且我现在没有任何动力去深入研究这个问题。我只是想知道答案和对这些东西的或多或少的简要解释,也许还有一个链接到更好的解释。

标签: java android ssl openssl x509certificate


【解决方案1】:

我终于自己在安卓模拟器上测试了一下,得到了最终答案。一旦我意识到 PKCS7 只是一种存储形式,或者更确切地说是各种签名类型的容器,实际上就不难理解了。

应用内

调用返回CERT.RSA 文件中的第一个签名。这是一个嵌入 X.509 证书的 PKCS7 文件,据我所知,它始终只是 android 应用程序的一个签名。

Signature sig = context.getPackageManager().getPackageInfo(context.getPackageName(), GET_SIGNATURES).signatures[0];

从上面得到的这个Signature可以直接用来生成一个像这样的工作X.509-certificate(取自here):

 byte[] rawCert = sig.toByteArray();
 InputStream certStream = new ByteArrayInputStream(rawCert);

 CertificateFactory certFactory;
 X509Certificate x509Cert;
 try {
      certFactory = CertificateFactory.getInstance("X509");
      x509Cert = (X509Certificate) certFactory.generateCertificate(certStream);

      //do stuff with your certificate
 } catch(Exception ex) {
      //handle exception
 }

其他任何地方

如果您在自己的 android 应用程序之外拥有证书并且想要相同的字节流,由上面的函数提供,您可以使用这样的简单 Java 程序来做同样的事情:

 FileInputStream is = new FileInputStream("CERT.RSA");
 CertificateFactory cf = CertificateFactory.getInstance("X.509");
 X509Certificate c = (X509Certificate) cf.generateCertificates(is).toArray()[0];
 byte[] rawCert = c.getEncoded();

此代码首先读取文件,创建CertificateFactory,然后是隔离PKCS7 容器中的第一个证书的重要步骤。然后c.getEncoded() 最终为您提供与上述方法完全相同的表示。

openssl

最后但并非最不重要的openssl-命令(取自here):

 openssl pkcs7 -inform DER -in CERT.RSA -print_certs -text

它会给你一个包含信息的漂亮概述,最后

 -----BEGIN CERTIFICATE-----
 ...
 -----END CERTIFICATE-----

块。它包含与上面相同的数据。如果您解析此块的内容并使用 base64 对其进行解码,它将为您提供与上面两个示例完全相同的字节数组。

【讨论】:

    【解决方案2】:
    context.getPackageManager().getPackageInfo(context.getPackageName(), GET_SIGNATURES).signatures[0]
    

    将返回与应用程序包关联的签名证书的表示。它将是文档中定义的 here 的 Signature 类的一个实例。 (“签名”这个名称有点误导,甚至在文档本身中也提到了)。

    context.getPackageManager().getPackageInfo(context.getPackageName(), GET_SIGNATURES).signatures[0].toByteArray();
    

    将返回其字节数组表示。 ie:证书文件的字节数组表示。

    除此之外,证书只是一个文本文件并不是什么新鲜事,但数据被结构化为通用格式并进行了编码。 X.509 是使用最广泛的格式之一。你可以找到它here。 X.509 证书的 RFC 是 here。证书的结构使得它们可以在被第三方更改时轻松检查。

    没有任何直接返回字节[]的openssl命令。我能想到的最接近的是 openssl 命令,您可以使用它来获取证书的文本表示。

    $ openssl x509 -in <your-certificate> -noout -text
    

    【讨论】:

    • 您能否针对原始问题编辑您的答案并明确回答问题?还参考 openssl 以便于显示?如果您这样做,我会将您标记为已接受:)
    • 嗨 SkryptX 我添加了你的更改!
    • 这里的所有事实都没有针对真正的 android CERT.RS 文件进行双重检查和测试,因此不起作用。使用旧版帖子和其中提供的链接找到了解决方案。
    • 对不起,伙计。所以你建议我删除我的帖子吗?
    • 好吧,我不知道...如果您想更正它,请继续。我会再等一会儿,直到我发布我的解决方案。我设置了一个赏金,如果有人想索取它,他们应该索取,因为无论如何积分都会在一周内消失,而且我不能给自己积分......所以你仍在争夺它^^跨度>
    【解决方案3】:

    PackageInfo 的签名成员只是从包文件中读取的所有签名的数组。

    public Signature[] signatures;
    

    android.content.pm.Signature 类以形式存储签名

    private final byte[] mSignature;
    

    这就是 toByteArray() 的作用:

    /**
     * @return the contents of this signature as a byte array.
     */
    public byte[] toByteArray() {
        byte[] bytes = new byte[mSignature.length];
        System.arraycopy(mSignature, 0, bytes, 0, mSignature.length);
        return bytes;
    }
    

    所以本质上,这段代码只是为您提供了用于对包进行签名的签名字节数组。

    假设CERT.RSA是apk的META-INF文件夹下的RSA文件,可以通过以下方式获取包的MD5、SHA1、SHA256签名:

    keytool -printcert -file CERT.RSA
    

    【讨论】:

    • 根据对问题的更好理解修改答案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-15
    • 2013-09-15
    • 2019-04-11
    • 2011-08-08
    • 1970-01-01
    相关资源
    最近更新 更多