我仅在(可能是某些)2.3.x设备上收到此错误。它适用于运行Android版本的任何其他设备。
这是我的HTTPRequestController:
public class HttpRequestController {
private final static String TAG = "HttpRequestController";
private static HttpRequestController instance;
public enum Method {
PUT, POST, DELETE, GET
}
private HttpRequestController() {
}
public static HttpRequestController getInstance() {
if (instance == null)
instance = new HttpRequestController();
return instance;
}
public String doRequest(String url, HashMap<Object, Object> data,
Method method, String token) throws Exception {
InputStream certificateInputStream = null;
if (MyApplication.PRODUCTION) {
certificateInputStream = MyApplication.context
.getResources().openRawResource(R.raw.production_cert);
LogUtils.log("using production SSL certificate");
} else {
certificateInputStream = MyApplication.context
.getResources().openRawResource(R.raw.staging_cert);
LogUtils.log("using staging SSL certificate");
}
KeyStore trustStore = KeyStore.getInstance("BKS");
try{
trustStore.load(certificateInputStream,
"re3d6Exe5HBsdskad8efj8CxZwv".toCharArray());
} finally {
certificateInputStream.close();
}
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(trustStore);
LogUtils.log("SSL: did init TrustManagerFactory with trust keyStore");
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
LogUtils.log("SSL: did init context with trust keyStore");
URL request = new URL(url);
HttpsURLConnection urlConnection = (HttpsURLConnection) request
.openConnection();
LogUtils.log("SSL: did open HttpsURLConnection");
urlConnection.setHostnameVerifier(new StrictHostnameVerifier());
urlConnection.setSSLSocketFactory(context.getSocketFactory());
urlConnection.setConnectTimeout(15000);
LogUtils.log("SSL: did set Factory and Timeout.");
if (method != Method.GET){
urlConnection.setDoOutput(true);
}
urlConnection.setDoInput(true);
urlConnection.setRequestProperty("Content-Type", "application/json");
urlConnection.setRequestProperty("Accept", "application/json");
LogUtils.log("SSL: urlConnection did set request properties.");
if (token != null) {
urlConnection.setRequestProperty("Authorization", "Token " + token);
}
urlConnection.setRequestMethod(method.toString());
urlConnection.connect();
LogUtils.log("SSL: urlConnection did connect.");
if (method != Method.GET) {
ObjectMapper mapper = new ObjectMapper();
String jsonValue = mapper.writeValueAsString(data);
OutputStream os = urlConnection.getOutputStream();
os.write(jsonValue.getBytes());
os.flush();
LogUtils.log(TAG, "Params: " + jsonValue);
}
LogUtils.log(TAG, method.toString() + ": " + url);
InputStream in = null;
if (urlConnection.getResponseCode() == 200) {
in = urlConnection.getInputStream();
} else {
in = urlConnection.getErrorStream();
}
String response = convertStreamToString(in);
LogUtils.log(TAG, "Got response : " + url);
LogUtils.log(TAG, "Response : " + response);
return response;
}
public String convertStreamToString(InputStream inputStream) {
BufferedReader buffReader = new BufferedReader(new InputStreamReader(
inputStream));
StringBuilder stringBuilder = new StringBuilder();
String line = null;
try {
while ((line = buffReader.readLine()) != null) {
stringBuilder.append(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return stringBuilder.toString();
}
public HttpClient retrieveHttpClient() {
return new MyHttpClient(MyApplication.context);
}
}
当我运行命令时:
openssl s_client -debug -connect www.mysitedomain.com:443
我收到回复:
--
some key stuff
--
Certificate chain
0 s:/OU=Domain Control Validated/CN=www.mydomainname.com
i:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Domain Validation CA - G2
1 s:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Domain Validation CA - G2
i:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA
2 s:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA
i:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA
---
Server certificate
-----BEGIN CERTIFICATE-----
some more certificate stuff
-----END CERTIFICATE-----
ubject=/OU=Domain Control Validated/CN=www.mydomainname.com
issuer=/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Domain Validation CA - G2
---
No client certificate CA names sent
---
SSL handshake has read 4091 bytes and written 328 bytes
---
New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1
Cipher : DHE-RSA-AES256-SHA
Session-ID: 57C379C59483809A7FE1BF8E235C5BFA7789E62AAEBCA9BC14B5273F5D1304E7
Session-ID-ctx:
Master-Key: 6FCD498D1294415A42B57420F0C05AB903EF8E56CB6F1530390F73AF5E4CBC22B359D5CDA09811E075A5C598002C380D
Key-Arg : None
Start Time: 1390473282
Timeout : 300 (sec)
Verify return code: 0 (ok)
---
所以它返回没问题......但是它仍然为我测试的2.3.x设备提供了这个错误。
在此之后我得到了一个例外:
LogUtils.log("SSL: urlConnection did set request properties.");
以下是例外:
01-23 10:20:28.459: W/System.err(1623): javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
01-23 10:20:28.459: W/System.err(1623): at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:477)
01-23 10:20:28.459: W/System.err(1623): at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:328)
01-23 10:20:28.459: W/System.err(1623): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection.setupSecureSocket(HttpConnection.java:185)
01-23 10:20:28.459: W/System.err(1623): at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:433)
01-23 10:20:28.459: W/System.err(1623): at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl$HttpsEngine.makeConnection(HttpsURLConnectionImpl.java:378)
01-23 10:20:28.459: W/System.err(1623): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:205)
01-23 10:20:28.459: W/System.err(1623): at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:152)
我打电话的方式就在这里:
String response = HttpRequestController
.getInstance()
.doRequest(ApiUrls.LOGIN, params, Method.POST, null);
适用于运行2.3.x以上Android版本的任何其他设备(根据我测试的内容)。
Android文档似乎没有关于2.3兼容性的主题。
答案 0 :(得分:4)
您必须告诉Android系统信任您的证书。您的问题是2.3之后的Android接受了您的证书,因为它包含在可信证书列表中,但是之前的版本不包括在内,因此,存在问题。
我建议您在Android documentation上执行此操作:
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// From https://www.washington.edu/itconnect/security/ca/load-der.crt
InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
// Tell the URLConnection to use a SocketFactory from our SSLContext
URL url = new URL("https://certs.cac.washington.edu/CAtest/");
HttpsURLConnection urlConnection =
(HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);
我也在做同样的事情,它在每台设备上运行正常,使用Android 2.3及更低版本,我的网站证书是私有的。
试试吧,告诉我它现在是否正常工作。
希望它可以帮到你!
答案 1 :(得分:1)
如果有人需要答案,我终于在谷歌2天后找到了答案。基本上我们需要使用自定义TrustManager来信任KeyStore中的CA.感谢CustomTrustManager的https://github.com/delgurth。
请参阅:https://github.com/ikust/hello-pinnedcerts/issues/2
<强> KeyPinStore.java 强>
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
public class KeyPinStore {
private static final String[] certificates = {"certificate1.crt", "certificate2.crt", "certificate3.crt", "certificate4.crt"};
private static KeyPinStore instance = null;
private SSLContext sslContext = SSLContext.getInstance("TLS");
public static synchronized KeyPinStore getInstance() throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
if (instance == null) {
instance = new KeyPinStore();
}
return instance;
}
private KeyPinStore() throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
for (int i = 0; i < certificates.length; i++) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new BufferedInputStream(Application.context.getAssets().open("certificate/" + certificates[i]));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
// Create a KeyStore containing our trusted CAs
keyStore.setCertificateEntry("ca" + i, ca);
}
// Use custom trust manager to trusts the CAs in our KeyStore
TrustManager[] trustManagers = {new CustomTrustManager(keyStore)};
// Create an SSLContext that uses our TrustManager
// SSLContext context = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagers, null);
}
public SSLContext getContext() {
return sslContext;
}
}
<强> CustomTrustManager.java 强>
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
/**
* A custom X509TrustManager implementation that trusts a specified server certificate in addition
* to those that are in the system TrustStore.
* Also handles an out-of-order certificate chain, as is often produced by Apache's mod_ssl
*/
public class CustomTrustManager implements X509TrustManager {
private final TrustManager[] originalTrustManagers;
private final KeyStore trustStore;
/**
* @param trustStore A KeyStore containing the server certificate that should be trusted
* @throws NoSuchAlgorithmException
* @throws KeyStoreException
*/
public CustomTrustManager(KeyStore trustStore) throws NoSuchAlgorithmException, KeyStoreException {
this.trustStore = trustStore;
final TrustManagerFactory originalTrustManagerFactory = TrustManagerFactory.getInstance("X509");
originalTrustManagerFactory.init(trustStore);
originalTrustManagers = originalTrustManagerFactory.getTrustManagers();
}
/**
* No-op. Never invoked by client, only used in server-side implementations
* @return
*/
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
/**
* No-op. Never invoked by client, only used in server-side implementations
* @return
*/
public void checkClientTrusted(X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {
}
/**
* Given the partial or complete certificate chain provided by the peer,
* build a certificate path to a trusted root and return if it can be validated and is trusted
* for client SSL authentication based on the authentication type. The authentication type is
* determined by the actual certificate used. For instance, if RSAPublicKey is used, the authType should be "RSA".
* Checking is case-sensitive.
* Defers to the default trust manager first, checks the cert supplied in the ctor if that fails.
* @param chain the server's certificate chain
* @param authType the authentication type based on the client certificate
* @throws java.security.cert.CertificateException
*/
public void checkServerTrusted(X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {
try {
for (TrustManager originalTrustManager : originalTrustManagers) {
((X509TrustManager) originalTrustManager).checkServerTrusted(chain, authType);
}
} catch(CertificateException originalException) {
try {
// Ordering issue?
X509Certificate[] reorderedChain = reorderCertificateChain(chain);
if (! Arrays.equals(chain, reorderedChain)) {
checkServerTrusted(reorderedChain, authType);
return;
}
for (int i = 0; i < chain.length; i++) {
if (validateCert(reorderedChain[i])) {
return;
}
}
throw originalException;
} catch(Exception ex) {
ex.printStackTrace();
throw originalException;
}
}
}
/**
* Checks if we have added the certificate in the trustStore, if that's the case we trust the certificate
* @param x509Certificate the certificate to check
* @return true if we know the certificate, false otherwise
* @throws KeyStoreException on problems accessing the key store
*/
private boolean validateCert(final X509Certificate x509Certificate) throws KeyStoreException {
return trustStore.getCertificateAlias(x509Certificate) != null;
}
/**
* Puts the certificate chain in the proper order, to deal with out-of-order
* certificate chains as are sometimes produced by Apache's mod_ssl
* @param chain the certificate chain, possibly with bad ordering
* @return the re-ordered certificate chain
*/
private X509Certificate[] reorderCertificateChain(X509Certificate[] chain) {
X509Certificate[] reorderedChain = new X509Certificate[chain.length];
List<X509Certificate> certificates = Arrays.asList(chain);
int position = chain.length - 1;
X509Certificate rootCert = findRootCert(certificates);
reorderedChain[position] = rootCert;
X509Certificate cert = rootCert;
while((cert = findSignedCert(cert, certificates)) != null && position > 0) {
reorderedChain[--position] = cert;
}
return reorderedChain;
}
/**
* A helper method for certificate re-ordering.
* Finds the root certificate in a possibly out-of-order certificate chain.
* @param certificates the certificate change, possibly out-of-order
* @return the root certificate, if any, that was found in the list of certificates
*/
private X509Certificate findRootCert(List<X509Certificate> certificates) {
X509Certificate rootCert = null;
for(X509Certificate cert : certificates) {
X509Certificate signer = findSigner(cert, certificates);
if(signer == null || signer.equals(cert)) { // no signer present, or self-signed
rootCert = cert;
break;
}
}
return rootCert;
}
/**
* A helper method for certificate re-ordering.
* Finds the first certificate in the list of certificates that is signed by the sigingCert.
*/
private X509Certificate findSignedCert(X509Certificate signingCert, List<X509Certificate> certificates) {
X509Certificate signed = null;
for(X509Certificate cert : certificates) {
Principal signingCertSubjectDN = signingCert.getSubjectDN();
Principal certIssuerDN = cert.getIssuerDN();
if(certIssuerDN.equals(signingCertSubjectDN) && !cert.equals(signingCert)) {
signed = cert;
break;
}
}
return signed;
}
/**
* A helper method for certificate re-ordering.
* Finds the certificate in the list of certificates that signed the signedCert.
*/
private X509Certificate findSigner(X509Certificate signedCert, List<X509Certificate> certificates) {
X509Certificate signer = null;
for(X509Certificate cert : certificates) {
Principal certSubjectDN = cert.getSubjectDN();
Principal issuerDN = signedCert.getIssuerDN();
if(certSubjectDN.equals(issuerDN)) {
signer = cert;
break;
}
}
return signer;
}
}
要使用它,只需获取SSLSocketFactory并应用它,例如:
使用HttpsURLConnection
KeyPinStore keystore = KeyPinStore.getInstance();
SSLSocketFactory sslSF = keystore.getContext().getSocketFactory();
URL url = new URL("https://certs.cac.washington.edu/CAtest/");
HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(sslSF);
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);
与排球
KeyPinStore keystore = KeyPinStore.getInstance();
SSLSocketFactory sslSF = keystore.getContext().getSocketFactory();
RequestQueue mRequestQueue = Volley.newRequestQueue(context, new HurlStack(null, sslSF));
答案 2 :(得分:-1)
请注意使用正确的 CA 证书(不要使用站点证书):