java - 客户端无法使用自签名证书连接到 REST API
问题描述
我正在尝试将我的 Android 客户端连接到 TOMCAT REST API。只要使用 HTTP,一切正常。不幸的是,必须实施 HTTPS。
在服务器端 HTTPS 实现服务器看起来很好之后(服务器使用自签名证书。使用 JAVA KEYTOOL 创建)。
不幸的是,由于某种原因,我无法连接 Android 客户端。
起初错误是:“未找到证书路径的信任锚”。我尝试使用 Android 开发人员指南,但在使用“AsyncTask”访问 .crt / cer 文件时遇到问题。所以我决定暂时使用“ALLOW_ALL_HOSTNAME_VERIFIER”。HTTPS CLIENT 类如下所示:
(注释部分为HTTP版本)
import android.util.Base64;
import android.os.Build;
import android.annotation.TargetApi;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.SingleClientConnManager;
import android.os.AsyncTask;
import android.util.Log;
public class UrlUtil extends AsyncTask<String, Void, String> {
@TargetApi(Build.VERSION_CODES.O_MR1)
@Override
protected String doInBackground(String... strings) {
StringBuilder jsonResult = new StringBuilder();
String message = null;
try {
//setup SSL
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);
String cred = "xxxx:yyyy";
String encoding = Base64.encodeToString(cred.getBytes(), Base64.DEFAULT);
// Example send http request
HttpPost httpPost = new HttpPost(strings[0]);
httpPost.setHeader("Authorization", "Basic " + encoding);
HttpResponse response = httpClient.execute(httpPost);
InputStream is = response.getEntity().getContent(); //outputs an inputstream
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
is.close();
Log.d("httpstest",sb.toString());
return sb.toString();
} catch (IOException e) { }
return null;
// try {
// URL url = new URL(strings[0]);
//
// Authenticator.setDefault(new Authenticator() {
// protected PasswordAuthentication getPasswordAuthentication() {
// return new PasswordAuthentication("user", "userPass".toCharArray());
// }
// });
// HttpURLConnection connection = (HttpURLConnection) new URL(url.toString()).openConnection();
// connection.setUseCaches(false);
// connection.connect();
// InputStream inputStream = connection.getInputStream();
// InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
// BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
// message = org.apache.commons.io.IOUtils.toString(bufferedReader);
//
// } catch (MalformedURLException e) {
// e.printStackTrace();
// } catch (IOException e) {
// e.printStackTrace();
// }
//
//
//
// } catch (Exception e) { }
//
// return message;
}
}
错误信息:
07/09 18:27:29: Launching app
$ adb install-multiple -r -t -p org.pilat.eko_raporty20 C:\Users\Pilat\Desktop\AndroidStudioProjects\ekoraporty20\app\build\intermediates\split-apk\debug\slices\slice_9.apk
Split APKs installed in 2 s 675 ms
$ adb shell am start -n "org.pilat.eko_raporty20/org.pilat.eko_raporty20.activity.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Client not ready yet..Waiting for process to come online
Connected to process 944 on device lenovo-lenovo_p2a42-ZY223TJMN2
Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
D/LenovoAppIconTheme: ExtraResources;cleanCachedIcon;clear cache..
W/ActivityThread: Application org.pilat.eko_raporty20 can be debugged on port 8100...
W/System: ClassLoader referenced unknown path: /data/app/org.pilat.eko_raporty20-1/lib/arm64
I/InstantRun: starting instant run server: is main process
I/Typeface: setThemeFont(): sThemeFontPath = ,fontPath =
W/Typeface: setThemeFont(): FontPath Not Changed!
W/art: Before Android 4.1, method android.graphics.PorterDuffColorFilter android.support.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable
V/BoostFramework: mAcquireFunc method = public int com.qualcomm.qti.Performance.perfLockAcquire(int,int[])
V/BoostFramework: mReleaseFunc method = public int com.qualcomm.qti.Performance.perfLockRelease()
mAcquireTouchFunc method = public int com.qualcomm.qti.Performance.perfLockAcquireTouch(android.view.MotionEvent,android.util.DisplayMetrics,int,int[])
mIOPStart method = public int com.qualcomm.qti.Performance.perfIOPrefetchStart(int,java.lang.String)
mIOPStop method = public int com.qualcomm.qti.Performance.perfIOPrefetchStop()
V/BoostFramework: BoostFramework() : mPerf = com.qualcomm.qti.Performance@e5acceb
BoostFramework() : mPerf = com.qualcomm.qti.Performance@fdb0a48
D/NetworkSecurityConfig: No Network Security Config specified, using platform default
I/DpmTcmClient: RegisterTcmMonitor from: org.apache.http.impl.conn.TcmIdleTimerMonitor
I/System.out: [socket][0] connection /"ip":"port";LocalPort=-1(0)
I/System.out: [socket][/100.82.169.168:35907] connected
I/System.out: close [socket][/100.82.169.168:35907]
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
Process: org.pilat.eko_raporty20, PID: 944
java.lang.RuntimeException: Unable to start activity ComponentInfo{org.pilat.eko_raporty20/org.pilat.eko_raporty20.activity.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.String.toString()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2659)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2720)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1466)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6111)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.String.toString()' on a null object reference
at org.pilat.eko_raporty20.activity.MainActivity.onCreate(MainActivity.java:126)
at android.app.Activity.performCreate(Activity.java:6734)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2612)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2720)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1466)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6111)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
Application terminated.
为什么我没有从服务器获取数据?
(我不明白:I/System.out: [socket][0] connection /"ip":"port";LocalPort=-1(0))
如何以正确的方式做到这一点?
如果没有其他选项可以接受所有证书,则首选选项是使用自签名证书使其工作。
提前致谢 :-)
解决方案
两天的互联网搜索和尝试许多选项的结果是使用 Android 开发人员选项: https ://developer.android.com/training/articles/security-ssl 比添加证书文件时遇到问题。Android 不会看到它。您可以通过向 AsyncTask 类添加新的构造函数来添加证书。它应该看起来像这样:
Public class UrlUtil extends AsyncTask<String, String, String> {
private Context context;
public UrlUtil (Context myContext) {
this.context = myContext;
}
public UrlUtil () {};
@TargetApi(Build.VERSION_CODES.O_MR1)
@Override
protected String doInBackground(String... params) {...}
...}
你执行 AsyncTask 像这样:
new UrlUtil(this).execute("https://myserver:myport/restofpath").get()
其中“this”是 ACTIVITY 上下文。
证书文件应放在资产文件夹中。如果您的项目中没有一个,添加一个最简单的方法是右键单击您的项目 NEW / FOLDER / ASSETS FOLDER。要将文件添加到资产文件夹,您不能简单地使用拖放。您必须在操作系统资源管理器中找到文件夹并将其放入文件夹中。
我希望你会发现它很有用:-)
推荐阅读
- javascript - 通过 JavaScript 中的父类访问子元素
- plugins - 为 Eclipse Oxygen 4.7 下载黄瓜插件时面临的问题
- sql - 在postgres中将列数据排列成行
- angular - Angular 开发构建失败,产品构建工作
- vba - Excel vba,无法在列表框中选择行
- html - 在主图像/标题 boostrap 上居中对齐文本
- service-worker - 绕过 Service-Worker 缓存
- python - 如何检查 SymPy 表达式是否具有解析积分
- python - 如何同时执行两个“聚合”函数(如 sum),从同一个迭代器提供它们?
- angular - 在数据加载到组件之前阻止视图部分