chenpengcong / blog

14 stars 3 forks source link

获取签名apk的证书 #20

Open chenpengcong opened 6 years ago

chenpengcong commented 6 years ago

本文介绍获取签名.apk文件的证书的几种方式,重点介绍从.RSA文件中获取

1. 使用keytool工具从keystrore文件中获取

$ keytool -export -alias key0 -file my.cer -keystore my.jks

2. 调用Android API

<context_instance>.getPackageManager().getPackageInfo(<context_instance>.getPackageName(),PackageManager.GET_SIGNATURES).signatures[0].toCharsString();//context_instance表示是一个Context实例

3. 从.RSA文件中获取

将被签名过的.apk文件解压即可得到/META-INF/CERT.RSA文件,接下来我们需要分析CERT.RSA是什么格式文件,里面包含什么信息。

通过阅读Android源码SignApk.java 中生成.RSA的代码段

/** Sign data and write the digital signature to 'out'. */
    private static void writeSignatureBlock(
        CMSTypedData data, X509Certificate publicKey, PrivateKey privateKey,
        OutputStream out)
        throws IOException,
               CertificateEncodingException,
               OperatorCreationException,
               CMSException {
        ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>(1);
        certList.add(publicKey);
        JcaCertStore certs = new JcaCertStore(certList);
        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
        ContentSigner signer = new JcaContentSignerBuilder(getSignatureAlgorithm(publicKey))
            .setProvider(sBouncyCastleProvider)
            .build(privateKey);
        gen.addSignerInfoGenerator(
            new JcaSignerInfoGeneratorBuilder(
                new JcaDigestCalculatorProviderBuilder()
                .setProvider(sBouncyCastleProvider)
                .build())
            .setDirectSignature(true)
            .build(signer, publicKey));
        gen.addCertificates(certs);
        CMSSignedData sigData = gen.generate(data, false);
        ASN1InputStream asn1 = new ASN1InputStream(sigData.getEncoded());
        DEROutputStream dos = new DEROutputStream(out);
        dos.writeObject(asn1.readObject());
    }

可以看到代码使用了CMSSignedDataGenerator类去生成.RSA文件,根据CMSSignedDataGenerator的说明文档描述:"general class for generating a pkcs7-signature message",可知该类是用来生成PKCS7签名信息的,所以生成的CERT.RSA遵循PKCS#7标准,PKCS#7文件格式如下(使用ASN.1语法)

SignedData ::= SEQUENCE {
     version Version,
     digestAlgorithms DigestAlgorithmIdentifiers,
     contentInfo ContentInfo,
     certificates[0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,
     crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
     signerInfos SignerInfos 
}

该ASN.1表示法来自RFC2315

可以看到PKCS7中包含了证书(certificates)

如何从PKCS#7文件中获取证书呢,这里介绍使用openssl command方式和openssl api方式

openssl command

$ openssl pkcs7 -inform DER -in CERT.RSA -print_certs | openssl x509 -outform DER -out CERT.cer

openssl api

$ cat get_cer_from_pkcs7.c 
#include <stdio.h>
#include <openssl/pkcs7.h>
#include <openssl/x509.h>
#include <openssl/bio.h>
#include <openssl/pem.h>
#include <openssl/err.h>

int main(int argc, char **argv)
{
    PKCS7 *p7 = NULL;
    FILE *f;
    int i;
    BIO *in;
    BIO *out;
    STACK_OF(X509) *certs = NULL;
    f = fopen("my.cer", "wb");//将certificate保存在my.cer文件
    if (f == NULL) {
        perror("fopen"); 
        exit(1);
    }

    CRYPTO_malloc_init();                                               \
    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();

    in = BIO_new(BIO_s_file());
    BIO_read_filename(in, argv[1]);

    p7 = d2i_PKCS7_bio(in, NULL); 
    i = OBJ_obj2nid(p7->type);
    if(i == NID_pkcs7_signed) {
        certs = p7->d.sign->cert;
    } else if(i == NID_pkcs7_signedAndEnveloped) {
        certs = p7->d.signed_and_enveloped->cert;
    }

    for (i = 0; certs && i < sk_X509_num(certs); i++) {
        X509 *x = sk_X509_value(certs,i);
        if(i2d_X509_fp(f, x) == 0) {
            perror("i2d_X509_fp");
        }
    }
    fclose(f);
}
$ gcc get_cer_from_pkcs7.c -lcrypto
$ ldd a.out
    linux-vdso.so.1 (0x00007ffd77782000)
    libcrypto.so.1.0.2 => /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.2 (0x00007f5defb39000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5def79a000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f5def596000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f5df01a0000)

最终放几张输出截图证明这几种方式提取的证书内容是一样的

Android API 使用Android API获取证书

keytool 使用keytool获取证书

openssl command 使用openssl command获取证书

使用openssl api openssl_api

参考: Android中的签名和签名文件的生成过程 how to Read the certificates file from the PKCS7.p7b certificate file usind openssl? Regarding: PKCS7, X509 and DER

拓展阅读: getting apk signature outside of android