Android 使用自签名证书接受HTTPS连接
我试图使用Android 使用自签名证书接受HTTPS连接,android,ssl,https,httpclient,ca,Android,Ssl,Https,Httpclient,Ca,我试图使用HttpClientlib建立HTTPS连接,但问题是,由于证书没有由Android可信证书集中列出的、等可识别的证书颁发机构(CA)签名,因此我一直得到javax.net.ssl.SSLException:不受信任的服务器证书 我见过一些简单地接受所有证书的解决方案,但是如果我想问用户怎么办 我想得到一个类似于浏览器的对话框,让用户决定是否继续。我最好使用与浏览器相同的证书存储。有什么想法吗?您需要做的第一件事是设置验证级别。 这样的水平并不重要: 允许所有主机名验证程序 浏览器\
HttpClient
lib建立HTTPS连接,但问题是,由于证书没有由Android可信证书集中列出的、等可识别的证书颁发机构(CA)签名,因此我一直得到javax.net.ssl.SSLException:不受信任的服务器证书
我见过一些简单地接受所有证书的解决方案,但是如果我想问用户怎么办
我想得到一个类似于浏览器的对话框,让用户决定是否继续。我最好使用与浏览器相同的证书存储。有什么想法吗?您需要做的第一件事是设置验证级别。 这样的水平并不重要:
- 允许所有主机名验证程序
- 浏览器\兼容\主机名\验证器
- 严格主机名验证程序
ALLOW\u ALL\u HOSTNAME\u VERIFIER
并在方法factorySSLSocketFactory.setHostnameVerifier()中设置它
接下来,您需要将协议的工厂设置为https。为此,只需调用SchemeRegistry.register()
方法
然后您需要使用SingleClientConnManager
创建一个DefaultHttpClient
。
在下面的代码中,您还可以看到,默认情况下,还将通过方法HttpsURLConnection.setDefaultHostnameVerifier()使用我们的标志(ALLOW\u ALL\u HOSTNAME\u VERIFIER
)
以下代码适用于我:
HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
DefaultHttpClient client = new DefaultHttpClient();
SchemeRegistry registry = new SchemeRegistry();
SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
registry.register(new Scheme("https", socketFactory, 443));
SingleClientConnManager mgr = new SingleClientConnManager(client.getParams(), registry);
DefaultHttpClient httpClient = new DefaultHttpClient(mgr, client.getParams());
// Set verifier
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
// Example send http request
final String url = "https://encrypted.google.com/";
HttpPost httpPost = new HttpPost(url);
HttpResponse response = httpClient.execute(httpPost);
您需要做的第一件事是设置验证级别。
这样的水平并不重要:
- 允许所有主机名验证程序
- 浏览器\兼容\主机名\验证器
- 严格主机名验证程序
虽然setHostnameVerifier()方法对于新库apache已经过时,但是对于Android SDK中的版本来说是正常的。
因此我们采用ALLOW\u ALL\u HOSTNAME\u VERIFIER
并在方法factorySSLSocketFactory.setHostnameVerifier()中设置它
接下来,您需要将协议的工厂设置为https。为此,只需调用SchemeRegistry.register()
方法
然后您需要使用SingleClientConnManager
创建一个DefaultHttpClient
。
在下面的代码中,您还可以看到,默认情况下,还将通过方法HttpsURLConnection.setDefaultHostnameVerifier()使用我们的标志(ALLOW\u ALL\u HOSTNAME\u VERIFIER
)
以下代码适用于我:
HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
DefaultHttpClient client = new DefaultHttpClient();
SchemeRegistry registry = new SchemeRegistry();
SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
registry.register(new Scheme("https", socketFactory, 443));
SingleClientConnManager mgr = new SingleClientConnManager(client.getParams(), registry);
DefaultHttpClient httpClient = new DefaultHttpClient(mgr, client.getParams());
// Set verifier
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
// Example send http request
final String url = "https://encrypted.google.com/";
HttpPost httpPost = new HttpPost(url);
HttpResponse response = httpClient.execute(httpPost);
以下主要步骤是从android平台不信任的认证机构实现安全连接所必需的
根据许多用户的要求,我在此处镜像了我的系统中最重要的部分:
获取所有必需的证书(根证书和任何中间证书)
使用keytool和提供程序创建密钥库并导入证书
在android应用程序中加载密钥库并将其用于安全连接(我建议使用而不是标准的java.net.ssl.HttpsURLConnection
(更容易理解,性能更高)
拿到证书
您必须获取从端点证书一直到根CA的所有构建链的证书。这意味着,任何(如果存在)中间CA证书以及根CA证书。您不需要获取端点证书
创建密钥库
下载并将其存储到已知位置。
还要确保可以调用keytool命令(通常位于JRE安装的bin文件夹下)
现在将获得的证书(不导入端点证书)导入到BouncyCastle格式的密钥库中
我没有测试它,但我认为导入证书的顺序很重要。这意味着,首先导入最低的中间CA证书,然后一直导入根CA证书
使用以下命令,将创建密码为mysecret的新密钥库(如果尚未存在),并导入中间CA证书。我还定义了BouncyCastle提供程序,可以在我的文件系统和密钥库格式中找到它。对链中的每个证书执行此命令
keytool -importcert -v -trustcacerts -file "path_to_cert/interm_ca.cer" -alias IntermediateCA -keystore "res/raw/mykeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret
验证证书是否已正确导入密钥库:
keytool -list -keystore "res/raw/mykeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret
应输出整个链:
RootCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 24:77:D9:A8:91:D1:3B:FA:88:2D:C2:FF:F8:CD:33:93
IntermediateCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 98:0F:C3:F8:39:F7:D8:05:07:02:0D:E3:14:5B:29:43
现在,您可以将密钥库作为原始资源复制到android应用程序的res/raw/
使用应用程序中的密钥库
首先,我们必须创建一个自定义Apache HttpClient,它使用我们的密钥库进行HTTPS连接:
import org.apache.http.*
public class MyHttpClient extends DefaultHttpClient {
final Context context;
public MyHttpClient(Context context) {
this.context = context;
}
@Override
protected ClientConnectionManager createClientConnectionManager() {
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
// Register for port 443 our SSLSocketFactory with our keystore
// to the ConnectionManager
registry.register(new Scheme("https", newSslSocketFactory(), 443));
return new SingleClientConnManager(getParams(), registry);
}
private SSLSocketFactory newSslSocketFactory() {
try {
// Get an instance of the Bouncy Castle KeyStore format
KeyStore trusted = KeyStore.getInstance("BKS");
// Get the raw resource, which contains the keystore with
// your trusted certificates (root and any intermediate certs)
InputStream in = context.getResources().openRawResource(R.raw.mykeystore);
try {
// Initialize the keystore with the provided trusted certificates
// Also provide the password of the keystore
trusted.load(in, "mysecret".toCharArray());
} finally {
in.close();
}
// Pass the keystore to the SSLSocketFactory. The factory is responsible
// for the verification of the server certificate.
SSLSocketFactory sf = new SSLSocketFactory(trusted);
// Hostname verification from certificate
// http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
return sf;
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
我们已经创建了自定义HttpClient,现在可以将其用于安全连接。例如,当我们对REST资源进行GET调用时:
// Instantiate the custom HttpClient
DefaultHttpClient client = new MyHttpClient(getApplicationContext());
HttpGet get = new HttpGet("https://www.mydomain.ch/rest/contacts/23");
// Execute the GET call and obtain the response
HttpResponse getResponse = client.execute(get);
HttpEntity responseEntity = getResponse.getEntity();
就是这样;)要从android平台不信任的认证机构实现安全连接,需要以下主要步骤
根据许多用户的要求,我在此处镜像了我的系统中最重要的部分:
获取所有必需的证书(根证书和任何中间证书)
使用keytool和提供程序创建密钥库并导入证书
在android应用程序中加载密钥库并将其用于安全连接(我建议使用而不是标准的java.net.ssl.HttpsURLConnection
(更容易理解,性能更高)
拿到证书
您必须获取从端点证书一直到根CA的所有构建链的证书。这意味着,任何(如果存在)中间CA证书以及根CA证书。您不需要获取端点证书
创建密钥库
下载并将其存储到已知位置。
还要确保可以调用keytool命令(通常位于bin文件夹下)
private static class MyTrustManager implements X509TrustManager
{
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException
{
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException
{
}
@Override
public X509Certificate[] getAcceptedIssuers()
{
return null;
}
}
...
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
try
{
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
TrustManager[] tmlist = {new MyTrustManager()};
context.init(null, tmlist, null);
conn.setSSLSocketFactory(context.getSocketFactory());
}
catch (NoSuchAlgorithmException e)
{
throw new IOException(e);
} catch (KeyManagementException e)
{
throw new IOException(e);
}
conn.setRequestMethod("GET");
int rcode = conn.getResponseCode();
/**
* Set up a connection to myservice.domain using HTTPS. An entire function
* is needed to do this because myservice.domain has a self-signed certificate.
*
* The caller of the function would do something like:
* HttpsURLConnection urlConnection = setUpHttpsConnection("https://littlesvr.ca");
* InputStream in = urlConnection.getInputStream();
* And read from that "in" as usual in Java
*
* Based on code from:
* https://developer.android.com/training/articles/security-ssl.html#SelfSigned
*/
public static HttpsURLConnection setUpHttpsConnection(String urlString)
{
try
{
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// My CRT file that I put in the assets folder
// I got this file by following these steps:
// * Go to https://littlesvr.ca using Firefox
// * Click the padlock/More/Security/View Certificate/Details/Export
// * Saved the file as littlesvr.crt (type X.509 Certificate (PEM))
// The MainActivity.context is declared as:
// public static Context context;
// And initialized in MainActivity.onCreate() as:
// MainActivity.context = getApplicationContext();
InputStream caInput = new BufferedInputStream(MainActivity.context.getAssets().open("littlesvr.crt"));
Certificate ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
// 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(urlString);
HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
return urlConnection;
}
catch (Exception ex)
{
Log.e(TAG, "Failed to establish SSL connection to server: " + ex.toString());
return null;
}
}
OkHttpClient client = new OkHttpClient();
SSLContext sslContext = SslUtils.getSslContextForCertificateFile(context, "BPClass2RootCA-sha2.cer");
client.setSslSocketFactory(sslContext.getSocketFactory());
<?php
caronte ("https://www.secure-example.com/page.php");
function caronte($url) {
// build curl request
$ch = curl_init();
foreach ($_POST as $a => $b) {
$post[htmlentities($a)]=htmlentities($b);
}
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,http_build_query($post));
// receive server response ...
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$server_output = curl_exec ($ch);
curl_close ($ch);
echo $server_output;
}
?>
private fun disableSSLCertificateChecking() {
val hostnameVerifier = object: HostnameVerifier {
override fun verify(s:String, sslSession: SSLSession):Boolean {
return true
}
}
val trustAllCerts = arrayOf<TrustManager>(object: X509TrustManager {
override fun getAcceptedIssuers(): Array<X509Certificate> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
//val acceptedIssuers:Array<X509Certificate> = null
@Throws(CertificateException::class)
override fun checkClientTrusted(arg0:Array<X509Certificate>, arg1:String) {// Not implemented
}
@Throws(CertificateException::class)
override fun checkServerTrusted(arg0:Array<X509Certificate>, arg1:String) {// Not implemented
}
})
try
{
val sc = SSLContext.getInstance("TLS")
sc.init(null, trustAllCerts, java.security.SecureRandom())
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory())
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier)
}
catch (e: KeyManagementException) {
e.printStackTrace()
}
catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
}
}