没有对等证书例外 – 排除和Android与自签名证书

我试图让我的应用程序通过https与我的服务器通信。 由于我不想支付让我的服务器证书由可信任的CA签名,解决scheme是使用自签名证书。

所以,我创build了我的caconfig.cnf,如下所示:

[ 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 

然后,我使用以下命令创build并签署了证书:

 $ 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 

在我的nodejs服务器代码中,我创build了如下的https服务器:

 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); }); 

为了testing一切是否正确,我还创build了一个节点客户端脚本,向我的服务器发出请求。 这里是我的节点客户端的代码:

 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(); 

当我运行我的节点客户端脚本时,它完美的工作。 另一方面,我使用Android Volley示例应用程序来检查Volley如何使用https,不能正常工作。 在下面,我正在描述为了使其工作而遵循的所有步骤。

我使用以下命令使用我的certificate.cer文件创build了一个.bks文件:

 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 

然后,我已validation证书是否正确导入到.bks中,如下所示:

 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 

我得到了以下输出,这意味着它是正确的:

 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 

我已经接触到了这个教程 ,而且,因为我在使用Volley,所以我决定遵循它。 所以,下面是我对示例项目所作的以下更改。

 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" 

但是,当我按下button,我得到了以下exception:

 javax.net.ssl.SSLPeerUnverifiedException: No peer certificate 

下面是我的Java代码

 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); } }); } ... } 

有人知道解决scheme吗? 谢谢。

我在testing环境中使用自签名证书。 为了使其工作,我在onCreate方法的我的应用程序类中调用此方法。 它使所有的证书都被接受。 这不是保存,但为了testing目的是可以的。

 @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) {} }