Loading X509Certificate from /assets/filename publ

2019-03-22 08:36发布

问题:

I have the following code running in java on windows and it works like a charm. I am using a certificate with a 2048 RSA key that i generated with OpenSSL. The important piece to look at is the mServerPublicKey = cert.getPublicKey(); I need the public key for my use case.

    String serverCertFile = "C:\\Users\\Me\\Documents\\cert.pem";
    CertificateFactory certFactory;
    FileInputStream inStream;
    try {
        certFactory = CertificateFactory
                .getInstance("X.509");
        inStream = new FileInputStream (serverCertFile);
        X509Certificate cer = (X509Certificate) certFactory.generateCertificate(inStream);
        mServerPublicKey = cer.getPublicKey();
        inStream.close();
    } catch (CertificateException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        Log.d("SERVER CERTIFICATE","Unable to load certificate " + e.getMessage());
    } catch (FileNotFoundException e){
        e.printStackTrace();
        Log.d("SERVER CERTIFICATE","Server certificate file missing " + e.getMessage());
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

It loads my certificate and I can use the public key to encrypt a short message to my server. However, the equivalent on android does not return the right public key. Anybody see this before?

    try {
        CertificateFactory certFactory;
        certFactory = CertificateFactory.getInstance("X.509");
        InputStream inputStream = getClass().getResourceAsStream(
                "/assets/cert.pem");
        X509Certificate cert = (X509Certificate) certFactory
                .generateCertificate(inputStream); 
        mServerPublicKey = cer.getPublicKey();
     }

On Android, the certificate loads, has all the right fields in it, with the right values, except the public key is not right on android. Just wasted two days trying to figure this one out. I did notice that the providers were different on the two platforms. I was getting and instance of the certificate from Sun on windows and from BouncyCastle/spongycastle on android. Is this broken in BC on android?

I have also confirmed that the issue exists when i exstract just the public key

-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAszxAbWjxIJHIxs/5DNJ3 oNa8mYz9hdr0SZJaQDhaNsjS+R3RCO4CUAmCZUvGEaMyHfW78ykC26ssExlxSCju uoeHGGthM6+oSmlDFPDTItC3g4teEI8hyyRfyfN771CXi8DIKP12MN75jkYQoF3+ YrW4lIs1X0GMt2Fi6JxFnHvrhxWZLWrnabMxOyhYDXsvqVwWUx8w1I5dwep6aCb4 Km9gkOJKXs4+3nKjuUREydsXgZ1SEq7/vHWH3yiR4bIvsyqMSD0ndfBmbk+0+ml5 +9Gsv0+lg6d2cQRmbqh9qK6slYrBLKbZvwnBVn4iXNk/ZOVpN+TjZzKPfD3Q4grO QwIDAQAB -----END PUBLIC KEY-----

Then just use the following code to load it. No errors are thrown but the public key does not match between the windows and android. Really don't know what I am doing wrong.

        InputStream inputStream2 = getClass().getResourceAsStream(
                "/assets/certpk.pem");
        InputStreamReader reader = new InputStreamReader(inputStream2);
        BufferedReader br = new BufferedReader(reader);
        StringBuffer sb = new StringBuffer();
        String line;
        while ((line = br.readLine()) != null) {
            sb.append(line);
        }
        String pk = sb.toString();
        String publicKeyPEM = pk.replace("-----BEGIN PUBLIC KEY-----", "");
        publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "");
        byte[] decoded = Base64.decode(publicKeyPEM, Base64.DEFAULT);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        RSAPublicKey pubKey = (RSAPublicKey) kf.generatePublic(new X509EncodedKeySpec(decoded));
        mServerPublicKey = pubKey;

I also tested the certificate loading code with .der files and got the same results.

回答1:

Ok, all the code I used from above is correct. It is how you load keys and certificates. My issue was elsewhere. I was also misinterpreting what I was seeing in the debugger. My issue was when i created the cipher for encryption. I was using this to create the cipher object

Cipher c = Cipher.getInstance("RSA");

and I should have used:

Cipher c = Cipher.getInstance("RSA/None/PKCS1Padding");

Well live and learn.