如何使用 Retrofit 2 + OkHttp 3 加密/隐藏 HTTPS 调用的主体?
How do you encrypt / hide the body of an HTTPS call using Retrofit 2 + OkHttp 3?
我目前正在做一个项目,通过 https 调用向我们的服务器发送数据 api。该项目的基础 URL 支持 ssl(我们的 url api 端点以 https://api.... 开头)。我正在使用 Retrofit 2 和 OkHttp3 并像这样设置客户端:
public static void buildClient(){
//Misc code here.... not showing for security reasons.
OkHttpClient client = RetrofitClient.configureClient(new OkHttpClient());
//I make calls here to update interceptors, timeouts, etc.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(new Gson()))
.client(client)
.build();
}
//Setup the ssl stuff here
public static OkHttpClient configureClient(final OkHttpClient client) {
final TrustManager[] certs = new TrustManager[]{new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkServerTrusted(final X509Certificate[] chain,
final String authType)
throws CertificateException {
}
@Override
public void checkClientTrusted(final X509Certificate[] chain,
final String authType)
throws CertificateException {
}
}};
SSLContext ssl = null;
try {
ssl = SSLContext.getInstance("TLS");
ssl.init(null, certs, new SecureRandom());
} catch (final java.security.GeneralSecurityException ex) {
}
try {
final HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(final String hostname,
final SSLSession session) {
return true;
}
};
client.setHostnameVerifier(hostnameVerifier);
client.setSslSocketFactory(ssl.getSocketFactory());
} catch (final Exception e) {
}
return client;
}
所以在这之后,我们都准备好了。
现在,这是我所知道的:
1) 我通过 HTTPS 发送,因为如果我不这样做,服务器会抛出错误,但事实并非如此。
2) 我的代码工作正常,因为它正在与服务器通信并且应用程序可以运行。
这里的问题是实际的正文数据没有被加密。这里有 2 个照片示例来说明我的意思。
1)
2)
第一张图片显示了对实际正文数据的正确混淆,因为数据正在转换为加密 'stuff',而第二张图片显示的是纯文本。第二个是我用一个对象向服务器发送 POST 调用。
我的问题是,我该如何复制它,以便我的正文像其他文本一样被隐藏/加密?
备注:
1) 我正在通过混淆器使用混淆
2) 我将 minifyEnabled 设置为 true
3) 我是如何通过数据包嗅探器发现这一点的
有人知道如何完成这个吗?或者任何人都可以为我指出正确的方向,具体叫什么?
谢谢。
编辑:
所以,看来我没有理解这里的关键组件。
简短的回答是,呼叫已经加密并且正在发送 Https。
长话短说,我一直在将我的数据调用与这些调用进行比较:
1)
2)
我假设 这些 是加密的,而我的不是。事实证明,我发送的呼叫加密得很好,就像这些一样,但是这些数据是压缩的/压缩的,这使得它无法用眼睛读取,这就是让我认为这是加密数据从数据包嗅探器。
您可能会看到,仍然使用 "http" 发出请求。使用不同的 Retrofit.Builder
方法 baseUrl(HttpUrl):
HttpUrl httpUrl = new HttpUrl.Builder()
.host(BASE_URL)
.scheme("https").build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(httpUrl)
.build();
您的问题是:为什么我使用 HTTPS 但 Packet Capture 或 Charles 可以查看客户端和互联网?
因为 Packet Capture(VPN 代理)或 Charles 作为中介欺骗了您的客户:
你的客户端<-->
数据包Capture/Charles<-->
你的目标服务器。
这样代理工具就可以查看到你所有的HTTPS内容(实际上它们确实是加密过的)
解决方法:
您可以参考OkHttp wiki:https://github.com/square/okhttp/wiki/HTTPS
并为您的 HTTPS 检查设置一个 证书 固定。例如:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView textView = (TextView) findViewById(R.id.text);
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.cipherSuites(
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256)
.build();
OkHttpClient client = new OkHttpClient.Builder()
.connectionSpecs(Collections.singletonList(spec))
.certificatePinner(new CertificatePinner.Builder()
.add("drakeet.me", "sha256/gGOcYKAwzEaUfun6YdxZvFSQq/x2lF/R8UizDFofveY=")
.build())
.build();
Request request = new Request.Builder()
.url("https://drakeet.me?s=type")
.post(RequestBody.create(MediaType.parse("text"), "xxx...xxx"))
.addHeader("token", "xxx")
.build();
final Handler handler = new Handler();
client.newCall(request).enqueue(new Callback() {
@Override public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override public void onResponse(Call call, final Response response)
throws IOException {
final String t = response.body().string();
handler.post(new Runnable() {
@Override public void run() {
textView.setText(t);
}
});
}
});
}
}
作为我上面的代码,我预设了一个certificatePinner
与我的目标服务器的真实certificatePinner相关,所以如果我现在使用Packet Capture/Charles,他们会自己创建一个假的certificatePinner,而OkHttp会比较两个pinning,如果不相等,抛出一个javax.net.ssl.SSLPeerUnverifiedException: Certificate pinning failure!
并且如果您关闭数据包 Capture/Charles,异常会解除并成功发送 HTTPS 内容。
我目前正在做一个项目,通过 https 调用向我们的服务器发送数据 api。该项目的基础 URL 支持 ssl(我们的 url api 端点以 https://api.... 开头)。我正在使用 Retrofit 2 和 OkHttp3 并像这样设置客户端:
public static void buildClient(){
//Misc code here.... not showing for security reasons.
OkHttpClient client = RetrofitClient.configureClient(new OkHttpClient());
//I make calls here to update interceptors, timeouts, etc.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(new Gson()))
.client(client)
.build();
}
//Setup the ssl stuff here
public static OkHttpClient configureClient(final OkHttpClient client) {
final TrustManager[] certs = new TrustManager[]{new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkServerTrusted(final X509Certificate[] chain,
final String authType)
throws CertificateException {
}
@Override
public void checkClientTrusted(final X509Certificate[] chain,
final String authType)
throws CertificateException {
}
}};
SSLContext ssl = null;
try {
ssl = SSLContext.getInstance("TLS");
ssl.init(null, certs, new SecureRandom());
} catch (final java.security.GeneralSecurityException ex) {
}
try {
final HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(final String hostname,
final SSLSession session) {
return true;
}
};
client.setHostnameVerifier(hostnameVerifier);
client.setSslSocketFactory(ssl.getSocketFactory());
} catch (final Exception e) {
}
return client;
}
所以在这之后,我们都准备好了。
现在,这是我所知道的:
1) 我通过 HTTPS 发送,因为如果我不这样做,服务器会抛出错误,但事实并非如此。
2) 我的代码工作正常,因为它正在与服务器通信并且应用程序可以运行。
这里的问题是实际的正文数据没有被加密。这里有 2 个照片示例来说明我的意思。
1)
2)
第一张图片显示了对实际正文数据的正确混淆,因为数据正在转换为加密 'stuff',而第二张图片显示的是纯文本。第二个是我用一个对象向服务器发送 POST 调用。
我的问题是,我该如何复制它,以便我的正文像其他文本一样被隐藏/加密?
备注:
1) 我正在通过混淆器使用混淆
2) 我将 minifyEnabled 设置为 true
3) 我是如何通过数据包嗅探器发现这一点的
有人知道如何完成这个吗?或者任何人都可以为我指出正确的方向,具体叫什么?
谢谢。
编辑:
所以,看来我没有理解这里的关键组件。
简短的回答是,呼叫已经加密并且正在发送 Https。
长话短说,我一直在将我的数据调用与这些调用进行比较:
1)
2)
我假设 这些 是加密的,而我的不是。事实证明,我发送的呼叫加密得很好,就像这些一样,但是这些数据是压缩的/压缩的,这使得它无法用眼睛读取,这就是让我认为这是加密数据从数据包嗅探器。
您可能会看到,仍然使用 "http" 发出请求。使用不同的 Retrofit.Builder
方法 baseUrl(HttpUrl):
HttpUrl httpUrl = new HttpUrl.Builder()
.host(BASE_URL)
.scheme("https").build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(httpUrl)
.build();
您的问题是:为什么我使用 HTTPS 但 Packet Capture 或 Charles 可以查看客户端和互联网?
因为 Packet Capture(VPN 代理)或 Charles 作为中介欺骗了您的客户:
你的客户端<-->
数据包Capture/Charles<-->
你的目标服务器。
这样代理工具就可以查看到你所有的HTTPS内容(实际上它们确实是加密过的)
解决方法:
您可以参考OkHttp wiki:https://github.com/square/okhttp/wiki/HTTPS
并为您的 HTTPS 检查设置一个 证书 固定。例如:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView textView = (TextView) findViewById(R.id.text);
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.cipherSuites(
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256)
.build();
OkHttpClient client = new OkHttpClient.Builder()
.connectionSpecs(Collections.singletonList(spec))
.certificatePinner(new CertificatePinner.Builder()
.add("drakeet.me", "sha256/gGOcYKAwzEaUfun6YdxZvFSQq/x2lF/R8UizDFofveY=")
.build())
.build();
Request request = new Request.Builder()
.url("https://drakeet.me?s=type")
.post(RequestBody.create(MediaType.parse("text"), "xxx...xxx"))
.addHeader("token", "xxx")
.build();
final Handler handler = new Handler();
client.newCall(request).enqueue(new Callback() {
@Override public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override public void onResponse(Call call, final Response response)
throws IOException {
final String t = response.body().string();
handler.post(new Runnable() {
@Override public void run() {
textView.setText(t);
}
});
}
});
}
}
作为我上面的代码,我预设了一个certificatePinner
与我的目标服务器的真实certificatePinner相关,所以如果我现在使用Packet Capture/Charles,他们会自己创建一个假的certificatePinner,而OkHttp会比较两个pinning,如果不相等,抛出一个javax.net.ssl.SSLPeerUnverifiedException: Certificate pinning failure!
并且如果您关闭数据包 Capture/Charles,异常会解除并成功发送 HTTPS 内容。