No peer certificate Exception - Volley and Android

2019-04-07 22:55发布

I'm trying to make my app communicate with my server via https. As I don't want to pay to get my server certificate signed by a trusted CA, the solution is to use a self signed certificate.

So, I've created my caconfig.cnf as follows:

[ ca ]
default_ca              = CA_default                    # The default ca section

[ CA_default ]
dir                     = ./demoCA                      # top dir
database                = $dir/index.txt                # index file.
new_certs_dir           = $dir/newcerts                 # new certs dir

certificate             = $dir/cacert.pem               # The CA cert
serial                  = $dir/serial                   # serial no file
private_key             = $dir/private/cakey.pem        # CA private key
RANDFILE                = $dir/private/.rand            # random number file

default_days            = 365                           # how long to certify for
default_crl_days        = 30                            # how long before next CRL
default_md              = md5                           # md to use
policy                  = policy_any                    # default policy
email_in_dn             = no                            # Don't add the email into cert DN
name_opt                = ca_default                    # Subject name display option
cert_opt                = ca_default                    # Certificate display option
copy_extensions         = none                          # Don't copy extensions from request

[ policy_any ]
countryName             = optional
stateOrProvinceName     = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

Then, I've created and signed my certificate using the following commands:

$ mkdir myCA myCA/private myCA/newcerts
$ echo "01" > myCA/serial
$ touch demoCA/index.txt

$ openssl genrsa -des3 -out myCA/private/cakey.pem 1024
$ openssl req -new -x509 -days 3650 -key myCA/private/cakey.pem -out myCA/cacert.pem

$ openssl req -sha1 -newkey rsa:2048 -keyout server-key.pem -out server-cert-req.pem -subj '/CN=myhost/' -nodes
$ openssl ca -config caconfig.cnf -in server-cert-req.pem -out server-cert.pem
$ openssl x509 -inform PEM -in cacert.pem -outform DER -out certificate.cer
$ rm server-cert-req.pem

In my nodejs server code, I'm creating the https server like this:

var express = require('express');
var https = require('https');

var PORT = 443;
var app = express();

app.get('/', function (req, res) {
    res.send("Server is working");
});

var httpsOptions = {
    key: fs.readFileSync('server-key.pem'),
    cert: fs.readFileSync('server-cert.pem')
};

https.createServer(httpsOptions, app).listen(PORT, function() {
    console.log('%s: Node server started on port %d ...', Date(Date.now() ), PORT);
});

In order to test if everything is correct, I've also created a node client script, which makes a request to my server. Here is the code for my node client:

var https = require('https');
var fs = require('fs');

var request = https.request({
    host: 'myhost',
    port: 443,
    path: '/',
    method: 'GET',
    rejectUnauthorized: true,

    // Once it is self signed, I'm using my server certificate (public key).
    ca: [fs.readFileSync('cacert.pem').toString()]
}, function(response) {
    response.on('data', function(data) {
        console.log(data.toString());
    });
});

request.on('error', function(err) {
    console.log(err);
})

request.end();

When I run my node client script, it works perfectly. On the other hand, the Android Volley Examples app, which I'm using to check how Volley works with https, is not working. Below, I'm describing all the steps I've followed in order to try to make it work.

I've created a .bks file using my certificate.cer file using the following command:

keytool -importcert -v -trustcacerts -file "certificate.cer" -alias IntermediateCA -keystore "res/raw/my_keystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-146.jar" -storetype BKS -storepass mysecret

Then, I've verified if the certificate was imported correctly into the .bks as follows:

keytool -list -keystore "res/raw/my_keystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-146.jar" -storetype BKS -storepass mysecret

And I got the following output, that means it's correct:

Keystore type: BKS
Keystore provider: BC

Your keystore contains 1 entry

imeto_alias, Oct 16, 2014, trustedCertEntry, 
Certificate fingerprint (SHA1): 03:DC:A1:6A:9B:1D:AD:59:A9:9B:1F:C2:43:7E:80:07:3B:B6:BE:CB

I've came across to this tutorial, and, as I'm using Volley, I've decided to follow it. So, below are the following changes I've made to the example project.

Got Volley from git clone https://android.googlesource.com/platform/frameworks/volley

Got Android Volley Examples project from git clone git://github.com/ogrebgr/android_volley_examples.git

Copied my_keystore.bks containing the self-signed public key in res/raw;

Opened Act_SsSslHttpClient in the examples project, found "R.raw.test" and replaced it with R.raw.my_keystore;

Found "new SslHttpClient(" and replaced the default password "test123″ with "mysecret";

Replaced "44400" with the HTTPS port of my server/virtualhost ("443"). (I could also remove this parameter since "443" is the default port;

Replaced "https://tp.bolyartech.com:44400/https_test.html" with my server URL.

Started the app, went to "HTTPS with self-signed cert", then "Execute HTTPS request"

But, when I pressed the button, I got the following exception:

javax.net.ssl.SSLPeerUnverifiedException: No peer certificate

Below, is my JavaCode...

public class Act_SsSslHttpClient extends Activity {
    private TextView mTvResult;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.act__ss_ssl_http_client);

        mTvResult = (TextView) findViewById(R.id.tv_result);

        Button btnSimpleRequest = (Button) findViewById(R.id.btn_simple_request);
        btnSimpleRequest.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {

                // Replace R.raw.test with your keystore 
                InputStream keyStore = getResources().openRawResource(R.raw.my_keystore);


                // Usually getting the request queue shall be in singleton like in {@seeAct_SimpleRequest}
                // Current approach is used just for brevity
                RequestQueue queue = Volley.newRequestQueue(Act_SsSslHttpClient.this,
                                     new ExtHttpClientStack(new SslHttpClient(keyStore,
                                         "mysecret")));

                StringRequest myReq = new StringRequest(Method.GET,
                                                    "https://myServerURL/",
                                                    createMyReqSuccessListener(),
                                                    createMyReqErrorListener());

                queue.add(myReq);
            }
        });
    }

...

}

Does anybody know the solution? Thank you.

1条回答
Deceive 欺骗
2楼-- · 2019-04-07 23:22

I am using self signed certificate in my test environment. To make it work I call this method in my Application class in onCreate method. It makes all certificates accepted. It is not save, but for test purposes is ok.

@Override
public void onCreate() {
    nuke();
    super.onCreate();
}

public static void nuke() {
    try {
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() {
                X509Certificate[] myTrustedAnchors = new X509Certificate[0];
                return myTrustedAnchors;
            }

            @Override
            public void checkClientTrusted(X509Certificate[] certs,
                    String authType) {}

            @Override
            public void checkServerTrusted(X509Certificate[] certs,
                    String authType) {}
        } };

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection
                .setDefaultSSLSocketFactory(sc.getSocketFactory());
        HttpsURLConnection
                .setDefaultHostnameVerifier(new HostnameVerifier() {

                    @Override
                    public boolean verify(String arg0, SSLSession arg1) {
                        return true;
                    }
                });
    } catch (Exception e) {}
}
查看更多
登录 后发表回答