我想我已经明白了(请告诉我这是否会留下明显的安全漏洞,但是要轻柔;-)
我认为我在这里将自己拥有的钥匙与房东的钥匙进行比较:
return Arrays.equals(bytes, encodedKey);
我认为这对于我想做的事情应该已经足够了。我还想确认来回传递用户名/密码等时,所有流量都将被加密。
private HttpsURLConnection getTrustedConnection()
throws CertificateException, KeyStoreException, NoSuchAlgorithmException,
KeyManagementException, IOException {
Certificate certificate;
String keyStoreType;
final KeyStore keyStore;
String tmfAlgorithm;
TrustManagerFactory trustManagerFactory;
final byte[] encodedKey;
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
InputStream inputStream = myContext.getResources().openRawResource(
myContext.getResources().getIdentifier("server_crt", "raw",
myContext.getPackageName()));
BufferedInputStream caInput = new BufferedInputStream(inputStream);
certificate = certificateFactory.generateCertificate(caInput);
encodedKey = certificate.getEncoded();
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostName, SSLSession sslSession) {
X509Certificate[] peerCertificateChain;
try {
peerCertificateChain = sslSession.getPeerCertificateChain();
String host = sslSession.getPeerHost();
byte[] bytes = peerCertificateChain[0].getEncoded();
return Arrays.equals(bytes, encodedKey);
} catch (SSLPeerUnverifiedException | CertificateEncodingException e) {
e.printStackTrace();
}
return false;
}
});
caInput.close();
keyStoreType = KeyStore.getDefaultType();
keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", certificate);
tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
trustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm);
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
URL url = new URL(String.format(myContext.getString(R.string.command_url), myHost,
myPort, myCommand));
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(sslContext.getSocketFactory());
connection.setReadTimeout(10000);
connection.setConnectTimeout(10000);
connection.setRequestMethod("POST");
connection.setRequestProperty("User-Agent", "Mozilla/5.0");
connection.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
connection.setRequestProperty("Content-Type", "application/json");
connection.setDoInput(true);
connection.setDoOutput(true);
return connection;
}
public void run() {
HttpsURLConnection connection = null;
try {
connection = getTrustedConnection();
OutputStreamWriter wr= new OutputStreamWriter(connection.getOutputStream());
wr.write(myParameters.toString());
wr.flush();
wr.close();
switch (connection.getResponseCode()) {
case HttpURLConnection.HTTP_CREATED:
case HttpURLConnection.HTTP_OK:
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line);
}
bufferedReader.close();
JSONObject jsonObj = new JSONObject(stringBuilder.toString());
myParent.done(jsonObj);
break;
default:
myParent.done(null);
}
} catch (Exception e) {
JSONObject json = new JSONObject();
try {
json.put(INVALID_LOGIN, e.getMessage());
} catch (JSONException e1) { System.out.println(e1.getMessage());
}
myParent.done(json);
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
如果通过主机名连接到服务器(在与Android Studio中的服务器模拟同一台计算机上的Android手机时),以下代码将非常有效:
private HttpsURLConnection getTrustedConnection()
throws CertificateException, KeyStoreException, NoSuchAlgorithmException,
KeyManagementException, IOException {
Certificate ca;
String keyStoreType;
KeyStore keyStore;
String tmfAlgorithm;
TrustManagerFactory trustManagerFactory;
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream ins = myContext.getResources().openRawResource(
myContext.getResources().getIdentifier("server_crt", "raw",
myContext.getPackageName()));
BufferedInputStream caInput = new BufferedInputStream(ins);
ca = cf.generateCertificate(caInput);
caInput.close();
keyStoreType = KeyStore.getDefaultType();
keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
trustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm);
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
URL url = new URL(String.format(myContext.getString(R.string.command_url), myHost,
myPort, myCommand));
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(sslContext.getSocketFactory());
connection.setReadTimeout(10000);
connection.setConnectTimeout(10000);
connection.setRequestMethod("POST");
connection.setRequestProperty("User-Agent", "Mozilla/5.0");
connection.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
connection.setRequestProperty("Content-Type", "application/json");
connection.setDoInput(true);
connection.setDoOutput(true);
return connection;
}
并这样称呼它:
httpsURLConnection connection = null;
connection = getTrustedConnection();
OutputStreamWriter wr= new OutputStreamWriter(connection.getOutputStream());
wr.write(myParameters.toString());
wr.flush();
wr.close();
// do stuff
但是我的问题是我无法通过android物理设备上的主机名进行连接(此外,一旦一切正常,我将把目的地更改为动态的家庭IP地址。我确实想保持类似尽可能(如果可以的话)确保安全,因此我正在尝试执行以下操作:
public HttpsURLConnection getSomewhatSecureConnection()
throws NoSuchAlgorithmException, KeyManagementException, IOException {
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;
}
}
};
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
URL url = new URL(String.format(myContext.getString(R.string.command_url), myHost, myPort,
myCommand));
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
return connection;
}
我用同样的方式称呼它:
connection = getSomewhatSecureConnection();
OutputStreamWriter wr= new OutputStreamWriter(connection.getOutputStream());
wr.write(myParameters.toString());
wr.flush();
wr.close();
//do stuff
我计划在HostnameVerifier或TrustManager中添加一些代码以对证书进行一些检查,但是现在我受困了。我要么得到
java.net.ProtocolException: method does not support a request body: GET
或
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
在
OutputStreamWriter wr= new OutputStreamWriter(connection.getOutputStream());
取决于我是否放置
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
在SSLContext初始化代码之前或之后。