9

我正在尝试使用充气城堡来读取 Android apk 文件中 CERT.RSA 的内容。

通过使用 :openssl pkcs7 -inform DER -in CERT.RSA -noout -print_certs -text

我得到以下信息,这似乎是正确的:

Certificate:
Data:
    Version: 3 (0x2)
    Serial Number:
        93:6e:ac:be:07:f2:01:df
    Signature Algorithm: sha1WithRSAEncryption
    Issuer: C=US, ST=California, L=Mountain View, O=Android, OU=Android, CN=Android/emailAddress=android@android.com
    Validity
        Not Before: Feb 29 01:33:46 2008 GMT
        Not After : Jul 17 01:33:46 2035 GMT
    Subject: C=US, ST=California, L=Mountain View, O=Android, OU=Android, CN=Android/emailAddress=android@android.com
    Subject Public Key Info:
        Public Key Algorithm: rsaEncryption
            Public-Key: (2048 bit)
            Modulus:
                00:d6:93:19:04:de:c6:0b:24:b1:ed:c7:62:e0:d9:
                d8:25:3e:3e:cd:6c:eb:1d:e2:ff:06:8c:a8:e8:bc:
                a8:cd:6b:d3:78:6e:a7:0a:a7:6c:e6:0e:bb:0f:99:
                35:59:ff:d9:3e:77:a9:43:e7:e8:3d:4b:64:b8:e4:
                fe:a2:d3:e6:56:f1:e2:67:a8:1b:bf:b2:30:b5:78:
                c2:04:43:be:4c:72:18:b8:46:f5:21:15:86:f0:38:
                a1:4e:89:c2:be:38:7f:8e:be:cf:8f:ca:c3:da:1e:
                e3:30:c9:ea:93:d0:a7:c3:dc:4a:f3:50:22:0d:50:
                08:07:32:e0:80:97:17:ee:6a:05:33:59:e6:a6:94:
                ec:2c:b3:f2:84:a0:a4:66:c8:7a:94:d8:3b:31:09:
                3a:67:37:2e:2f:64:12:c0:6e:6d:42:f1:58:18:df:
                fe:03:81:cc:0c:d4:44:da:6c:dd:c3:b8:24:58:19:
                48:01:b3:25:64:13:4f:bf:de:98:c9:28:77:48:db:
                f5:67:6a:54:0d:81:54:c8:bb:ca:07:b9:e2:47:55:
                33:11:c4:6b:9a:f7:6f:de:ec:cc:8e:69:e7:c8:a2:
                d0:8e:78:26:20:94:3f:99:72:7d:3c:04:fe:72:99:
                1d:99:df:9b:ae:38:a0:b2:17:7f:a3:1d:5b:6a:fe:
                e9:1f
            Exponent: 3 (0x3)
    X509v3 extensions:
        X509v3 Subject Key Identifier: 
            48:59:00:56:3D:27:2C:46:AE:11:86:05:A4:74:19:AC:09:CA:8C:11
        X509v3 Authority Key Identifier: 
            keyid:48:59:00:56:3D:27:2C:46:AE:11:86:05:A4:74:19:AC:09:CA:8C:11
            DirName:/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com
            serial:93:6E:AC:BE:07:F2:01:DF

        X509v3 Basic Constraints: 
            CA:TRUE
Signature Algorithm: sha1WithRSAEncryption
    7a:af:96:8c:eb:50:c4:41:05:51:18:d0:da:ab:af:01:5b:8a:
    76:5a:27:a7:15:a2:c2:b4:4f:22:14:15:ff:da:ce:03:09:5a:
    bf:a4:2d:f7:07:08:72:6c:20:69:e5:c3:6e:dd:ae:04:00:be:
    29:45:2c:08:4b:c2:7e:b6:a1:7e:ac:9d:be:18:2c:20:4e:b1:
    53:11:f4:55:d8:24:b6:56:db:e4:dc:22:40:91:2d:75:86:fe:
    88:95:1d:01:a8:fe:b5:ae:5a:42:60:53:5d:f8:34:31:05:24:
    22:46:8c:36:e2:2c:2a:5e:f9:94:d6:1d:d7:30:6a:e4:c9:f6:
    95:1b:a3:c1:2f:1d:19:14:dd:c6:1f:1a:62:da:2d:f8:27:f6:
    03:fe:a5:60:3b:2c:54:0d:bd:7c:01:9c:36:ba:b2:9a:42:71:
    c1:17:df:52:3c:db:c5:f3:81:7a:49:e0:ef:a6:0c:bd:7f:74:
    17:7e:7a:4f:19:3d:43:f4:22:07:72:66:6e:4c:4d:83:e1:bd:
    5a:86:08:7c:f3:4f:2d:ec:21:e2:45:ca:6c:2b:b0:16:e6:83:
    63:80:50:d2:c4:30:ee:a7:c2:6a:1c:49:d3:76:0a:58:ab:7f:
    1a:82:cc:93:8b:48:31:38:43:24:bd:04:01:fa:12:16:3a:50:
    57:0e:68:4d

但是当我使用 Bouncy Castle 时,我没有得到公钥模数和其他必要的东西。pubkey 只是空的。我想我在代码中犯了一些错误,但很奇怪我得到了我想要的一切,除了公钥。

X509CertParser certParser = new X509CertParser();
FileInputStream stream;
X509CertificateObject cert= null;
try {
stream = new FileInputStream("CERT.RSA");
    certParser.engineInit(stream);
    cert =  (X509CertificateObject) certParser.engineRead();
    stream.close();

    if(cert.getPublicKey()==null)System.out.println("NULL");

    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

此代码打印 NULL,而 openssl 调用告诉我模数等。

我应该怎么做才能在 Java 中获取 pubkey?(也许可以使用 android sdk 或其他东西来获取数据而不是充气城堡)

编辑:

我忘了提到我已经尝试过 David Grants 代码,它给了我错误消息,即 DerInputStream 太大:

java.security.cert.CertificateException: Unable to initialize, java.io.IOException: DerInputStream.getLength(): lengthTag=127, too big.
at sun.security.x509.X509CertImpl.<init>(X509CertImpl.java:199)
at sun.security.provider.X509Factory.engineGenerateCertificate(X509Factory.java:107)
at java.security.cert.CertificateFactory.generateCertificate(CertificateFactory.java:322)
4

6 回答 6

11

你不需要 BC 来从 DER 流中读取证书,你可以在 Java SE 中很好地做到这一点:

InputStream in = new FileInputStream("CERT.RSA");
CertificateFactory factory = CertificateFactory.getInstance("X.509")
X509Certificate cert = (X509Certificate) factory.generateCertificate(in);
于 2012-10-09T10:03:21.767 回答
2

如果您打算使用 BouncyCastle,则可以尝试使用PEMReader该类。(显然,您的证书需要采用 PEM 格式,而不是 DER)。

FileReader fileReader = new FileReader("/path/to/cert.pem");
PEMReader pemReader = new PEMReader(fileReader);
Object obj = pemReader.readObject();
pemReader.close(); // sloppy IO handling, be thorough in production code
X509CertificateObject certObj = (X509CertificateObject) obj;
System.out.println(certObj.getPublicKey());

出于某种原因,您发布的代码不会吸出公钥。我不知道为什么。

于 2012-10-09T10:14:44.183 回答
2

APK 只是一个 jar 文件。使用JarFile对其进行解析,然后列出JarEntry的,调用该getCertifiates()方法以获取签名证书。通常只有一个。您可以强制转换X509Certifiate以获取所有证书信息。请注意,您不必提取 CERT.RSA 文件,只需将 APK 文件传递​​给JarFile构造函数即可。

于 2012-10-10T03:16:49.140 回答
1

CERT.RSA您可以使用以下方法读取文件PKCS7

PKCS7 p7 = new PKCS7(new FileInputStream("CERT.RSA"));
p7.getCertificates();

这将为您返回一个X509Certificate.

于 2013-01-22T12:26:00.110 回答
1

您可以使用 SpongyCastle 正确获取。我在下面做了一些代码,你实际上可以得到像 openssl 这样的东西。SpongyCastle 罐子可以从下面下载:- core , prov , pkix

/*
 * This program is used for parsing META-INF/CERT.[RSA|DSA|EC] file and converting it to x509.pem.
 * Ref :- net.sourceforge.dkartaschew.halimede.data.PKCS7Decoder.java
 *
 * Created : 5th February 2020
 * Author  : HemanthJabalpuri
 *
 * This file has been put into the public domain.
 * You can do whatever you want with this file.
 */

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.StringWriter;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.Collection;

// need core.jar, prov.jar, bcpkix-jdk15on.jar of spongycastle
import org.spongycastle.cert.X509CertificateHolder;
import org.spongycastle.cert.jcajce.JcaX509CertificateConverter;
import org.spongycastle.cms.CMSSignedData;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.openssl.jcajce.JcaPEMWriter;
import org.spongycastle.util.encoders.Base64;

public class ParseCERT {
    private static void usage() {
        System.err.println("Usage : CERT.[RSA|EC|DSA] out.x509.pem");
        System.exit(2);
    }

    public static void main(String[] args) throws Exception {
        if (args.length != 2) usage();

        FileInputStream fis = new FileInputStream(args[0]);
        byte[] data = new byte[fis.available()];
        fis.read(data);
        fis.close();

        Security.addProvider(new BouncyCastleProvider());

        ArrayList<Certificate> certs = new ArrayList<Certificate>();
        CMSSignedData cms = new CMSSignedData(data);
        Collection<?> collection = cms.getCertificates().getMatches(null);
        for (Object o : collection) {
            if (o instanceof X509CertificateHolder) {
                certs.add(
                    new JcaX509CertificateConverter()
                    .setProvider(BouncyCastleProvider.PROVIDER_NAME)
                    .getCertificate((X509CertificateHolder) o)
                );
            }
        }
        if (certs.isEmpty()) throw new RuntimeException("No X509 certs found");

        Certificate cert1 = certs.get(0);
        System.out.println(" Type      : " + cert1.getType());
        PublicKey publicKey = cert1.getPublicKey();
        String algorithm = publicKey.getAlgorithm();
        System.out.println(" Algorithm : " + algorithm);

        FileOutputStream fos = new FileOutputStream(args[1]);
        byte[] pembytes = convertToPem(cert1);
        fos.write(pembytes);
        fos.close();
    }

    private static byte[] convertToPem(Certificate cert) throws Exception {
        StringWriter sw = new StringWriter();
        JcaPEMWriter pw = new JcaPEMWriter(sw);
        pw.writeObject(cert);
        pw.flush();
        pw.close();
        return sw.toString().getBytes();
    }

    public static byte[] convertToPem2(Certificate cert) throws Exception {
        String cert_begin = "-----BEGIN CERTIFICATE-----\n";
        String end_cert = "-----END CERTIFICATE-----\n";
        String b64 = Base64.toBase64String(cert.getEncoded()).replaceAll("(.{64})", "$1\n");
        if (b64.charAt(b64.length() - 1) != '\n') end_cert = "\n" + end_cert;
        String outpem = cert_begin + b64 + end_cert;
        return outpem.getBytes();
    }
}
于 2020-09-19T05:42:52.653 回答
0

在 Android 中,您可以使用X509Certificate抽象类读取包的数字证书( META-INF/CERT.RSA ) 的内容。

final String appPackage = "com.example"  // TODO: Add here the package name!!

try {
    final PackageManager pm = getPackageManager();
    final ApplicationInfo ai = pm.getApplicationInfo(appPackage, PackageManager.GET_META_DATA);

    if ( ai != null ) {
        final PackageInfo pi = pm.getPackageInfo(this.packageName, PackageManager.GET_PERMISSIONS);

        if ( pi != null ) {
            final Signature[] signatures = pi.signatures;

            if ( (pi.signatures != null) && (pi.signatures.length > 0) ) {
                for ( final Signature signature : signatures ) {
                    if ( signature != null ) {
                        final InputStream certInputStream = new ByteArrayInputStream(signature.toByteArray());
                        final CertificateFactory certFactory;
                        final X509Certificate x509Cert;

                        try {
                            certFactory = CertificateFactory.getInstance("X509");

                            if ( certFactory != null ) {
                                x509Cert = (X509Certificate) certFactory.generateCertificate(certInputStream);

                                if ( x509Cert != null ) {
                                    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

                                    Log.d(TAG, "Certificate Owner: " + x509Cert.getSubjectDN().toString());
                                    Log.d(TAG, "Certificate Issuer: " + x509Cert.getIssuerDN().toString());
                                    Log.d(TAG, "Certificate Serial Number: " + x509Cert.getSerialNumber().toString());
                                    Log.d(TAG, "Certificate Algorithm: " + x509Cert.getSigAlgName());
                                    Log.d(TAG, "Certificate Version: " + x509Cert.getVersion());
                                    Log.d(TAG, "Certificate OID: " + x509Cert.getSigAlgOID());
                                    Log.d(TAG, "Certificate Valid From: " + dateFormat.format( x509Cert.getNotBefore() ));
                                    Log.d(TAG, "Certificate Valid To: " + dateFormat.format( x509Cert.getNotAfter() ));

                                    try {
                                        final MessageDigest md = MessageDigest.getInstance("SHA-256");
                                        md.update( x509Cert.getEncoded() );

                                        Log.d(TAG, "Certificate SHA-256: " + getHex(md.digest()));
                                    }
                                    catch ( NoSuchAlgorithmException e ) {
                                        //Debug:
                                        Log.e(TAG, "MessageDigest ERROR: " + e.getMessage() + "\n");
                                        //e.printStackTrace();
                                    }
                                }
                            }
                        }
                        catch ( final CertificateException e ) {
                            //Debug:
                            Log.e(TAG, "CertificateFactory ERROR: " + e.getMessage() + "\n");
                            //e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}
catch ( final PackageManager.NameNotFoundException e ) {
    //Debug:
    Log.e(TAG, "ApplicationInfo ERROR: " + e.getMessage() + "\n");
    //e.printStackTrace();
}

其中用于提取数字证书的SHA-256 哈希的getHex()方法如下:

/**
 * Get the hex value of a raw byte array.
 *
 * @param raw  the raw byte array.
 * @return the hex value.
 */
public static String getHex( byte[] raw ) {
    final String HEXES = "0123456789abcdef";

    if ( (raw == null) || (raw.length == 0) ) {
        return null;
    }

    final StringBuilder hex = new StringBuilder( 2 * raw.length );
    for ( final byte b : raw ) {
        hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
    }

    return hex.toString();
}

此外,当它们不为空时,您可以提取所有者颁发者字段的所有条目(分别使用X509Certificate的getSubjectDN()getIssuerDN()方法检索),例如,使用以下代码:

for ( final String field : (x509Cert.getSubjectDN().toString()).split(", ") ) {
    if ( field.startsWith("CN=") ) {
        Log.d(TAG, "Common Name: " + field.substring(3));
    }
    if ( field.startsWith("OU=") ) {
        Log.d(TAG, "Organization Unit: " + field.substring(3));
    }
    if ( field.startsWith("O=") ) {
        Log.d(TAG, "Organization name: " + field.substring(2));
    }
    if ( field.startsWith("L=") ) {
        Log.d(TAG, "Locality name: " + field.substring(2));
    }
    if ( field.startsWith("ST=") ) {
        Log.d(TAG, "State or province Name: " + field.substring(3));
    }
    if ( field.startsWith("C=") ) {
        Log.d(TAG, "Country: " + field.substring(2));
    }
    if ( field.startsWith("DC=") ) {
        Log.d(TAG, "Domain Component: " + field.substring(3));
    }
}
于 2014-04-15T15:57:26.693 回答