我正在尝试从Andorid版本4.1.1中的Android应用程序连接到一个URL,我收到了问题标题中指出的错误,但是当我尝试从Andorid版本4.0连接相同的URL时。 4或3.1,一切正常。
代码片段:
try {
.
.
.
URL url = new URL(urlStr);
Log.i(TAG,"[ URL ] " + urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
int size = conn.getContentLength();
int responsecode = conn.getResponseCode();
Log.d(TAG, "Responsecode: " + responsecode);
.
.
.
} catch (Exception e) {
e.printStackTrace();
}
private static void trustAllHosts() {
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[] {};
}
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
} };
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection
.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
System.out.println("IOException : HTTPSRequest::trustAllHosts");
e.printStackTrace();
}
}
但在这里我清楚一点是“可能证书是自签名证书而不是将它们包含在KeyStore中。
我不明白为什么只有在Android Verison 4.1.1操作系统中才会出现这种情况 感谢。
完全堆积痕迹
01-31 10:26:08.348: W/System.err(3158): java.io.IOException: Hostname <URL> was not verified
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpConnection.verifySecureSocketHostname(HttpConnection.java:223)
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:446)
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:289)
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:239)
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:273)
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpURLConnectionImpl.getHeaderField(HttpURLConnectionImpl.java:130)
01-31 10:26:08.348: W/System.err(3158): at java.net.URLConnection.getHeaderFieldInt(URLConnection.java:544)
01-31 10:26:08.348: W/System.err(3158): at java.net.URLConnection.getContentLength(URLConnection.java:316)
01-31 10:26:08.348: W/System.err(3158): at libcore.net.http.HttpsURLConnectionImpl.getContentLength(HttpsURLConnectionImpl.java:191)
01-31 10:26:08.348: W/System.err(3158): at com.ih.util.HelpVideoServices$downloadTask.run(HelpVideoServices.java:172)
答案 0 :(得分:58)
如果您运行的证书没有任何意义,并且您想要绕过它们,您还需要添加一个空主机名验证程序以使此代码正常工作
HttpsURLConnection.setDefaultHostnameVerifier(new NullHostNameVerifier());
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new X509TrustManager[]{new NullX509TrustManager()}, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
主持人的代码:
import javax.net.ssl.HostnameVerifier ;
import javax.net.ssl.SSLSession;
public class NullHostNameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
Log.i("RestUtilImpl", "Approving certificate for " + hostname);
return true;
}
}
这需要运行一次,但是如果要对连接对象进行更改,则可能需要再次运行它。
答案 1 :(得分:19)
除了@Noam的回答,这是一个完整的例子:
/**
* Disables the SSL certificate checking for new instances of {@link HttpsURLConnection} This has been created to
* aid testing on a local box, not for use on production.
*/
private static void disableSSLCertificateChecking() {
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException {
// not implemented
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException {
// not implemented
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
}
};
try {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
希望有所帮助
答案 2 :(得分:10)
可能会发生这种情况,因为您在SSL上声明的CN(公用名)也不会发送您发送HTTP请求的实际URL。
如果是,请创建一个新SSL并输入当前CN。这应该可以解决问题。
答案 3 :(得分:6)
我在4.1.1和4.1.2中使用HTTPSUrlConnection遇到了这个问题。
经过一番探讨之后,我发现这是因为我正在处理的Apache服务器有多个虚拟主机服务于https流量,导致Android中出现SNI问题 - 至少在JellyBean之前(我有未经证实的报告,它在JB)工作。
在我的情况下,有3个虚拟主机提供https流量:
使用openssl_client探测api。*,如下所示:
openssl s_client -debug -connect api.mydomain.com:443
...总是返回根域的证书 - 埋在输出中的是:
Certificate chain
0 s:/OU=Domain Control Validated/CN=mydomain.com
...
...在openssl_client命令行中指定服务器名称:
openssl s_client -debug -servername api.mydomain.com -connect api.mydomain.com:443
...返回了我期待看到的证书:
Certificate chain
0 s:/OU=Domain Control Validated/CN=api.mydomain.com
我能够通过将根域虚拟主机移动到其他物理主机来解决问题。
似乎Android HostnameVerifier可以与多个子域并排作为虚拟主机使用,但在同一个apache中将 root 域作为虚拟主机会导致问题。< / p>
我不是sys-admin / dev-ops,所以有可能有Apache配置选项可以解决我不知道的问题。
答案 4 :(得分:3)
请注意,SSL证书只能通过域工作,不能按IP地址工作。
如果您使用IP,请在代码
下面插入HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier()
{
@Override
public boolean verify(String hostname, SSLSession session)
{
if(hostname.equals("127.0.0.1"))
return true;
}
});
答案 5 :(得分:2)
答案 6 :(得分:1)
您的问题可能是通过“https”解决了您的网址问题。你必须将所有字符串网址转换为“http”,它才能正常工作。
修改强>
SchemeRegistry schemeRegistry = new SchemeRegistry ();
schemeRegistry.register (new Scheme ("http",
PlainSocketFactory.getSocketFactory (), 80));
schemeRegistry.register (new Scheme ("https",
new CustomSSLSocketFactory (), 443));
ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager (
params, schemeRegistry);
return new DefaultHttpClient (cm, params);
CustomSSLSocketFactory:
public class CustomSSLSocketFactory extends org.apache.http.conn.ssl.SSLSocketFactory
{
private SSLSocketFactory FACTORY = HttpsURLConnection.getDefaultSSLSocketFactory ();
public CustomSSLSocketFactory ()
{
super(null);
try
{
SSLContext context = SSLContext.getInstance ("TLS");
TrustManager[] tm = new TrustManager[] { new FullX509TrustManager () };
context.init (null, tm, new SecureRandom ());
FACTORY = context.getSocketFactory ();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public Socket createSocket() throws IOException
{
return FACTORY.createSocket();
}
// TODO: add other methods like createSocket() and getDefaultCipherSuites().
// Hint: they all just make a call to member FACTORY
}
FullX509TrustManager是一个实现javax.net.ssl.X509TrustManager的类,但没有一个方法实际执行任何工作,得到一个样本[here] [1]。
祝你好运!
答案 7 :(得分:1)
这对我来说更好->更改StrictHostnameVerifier()
https://developer.android.com/reference/org/apache/http/conn/ssl/StrictHostnameVerifier
示例
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
HostnameVerifier hv = new StrictHostnameVerifier();
return hv.verify("example.com", session);
}
};
使用示例https://developer.android.com/training/articles/security-ssl#java
// Tell the URLConnection to use our HostnameVerifier
URL url = new URL("https://example.org/");
HttpsURLConnection urlConnection =
(HttpsURLConnection)url.openConnection();
urlConnection.setHostnameVerifier(hostnameVerifier);
答案 8 :(得分:0)
来自亚马逊的文档: Bucket Restrictions
&#34;使用带有SSL的虚拟托管样式存储桶时,SSL通配符证书仅匹配不包含句点的存储桶。要解决此问题,请使用HTTP或编写自己的证书验证逻辑。&#34;
最简单的方法似乎是创建一个没有句点的唯一存储桶名称:
而不是&#34; bucketname.mycompany.com&#34;,类似&#34; bucketnamemycompany&#34;或任何其他符合DNS的存储桶名称。
答案 9 :(得分:0)
在科特林:
fun HttpsURLConnection.trustCert() {
try {
//Accepts every hostname
this.hostnameVerifier = HostnameVerifier { hostname, _ ->
println(hostname) //To be hardcoded/as needed
true
}
val trustMgr:Array<TrustManager> = arrayOf(object : X509TrustManager {
override fun checkClientTrusted(certs: Array<out X509Certificate>?, authType: String?) {}
override fun checkServerTrusted(certs: Array<out X509Certificate>?, authType: String?) {}
override fun getAcceptedIssuers(): Array<X509Certificate>? = null
})
this.sslSocketFactory = SSLContext.getInstance("TLS").also {
it.init(null, trustMgr, SecureRandom())
}.socketFactory
} catch (e: Exception) {
prinntln("SSL self-signed certificate processing error due to ${e.message}")
}
}
用法:
val conn = URL(Uri.Builder().also {
it.scheme("https")
it.encodedAuthority("$serverIp:$serverPort")
}.build().toString()).openConnection() as HttpsURLConnection
conn.trustCert()
val respCode = conn.responseCode
if(respCode == 200) {
//do something (eg: read inputStream)
}