【发布时间】:2018-12-31 03:34:24
【问题描述】:
我能够正确加载证书并可以访问我的服务器(小步骤...)我现在的问题是参数(“username=admin&password=admin”)没有进入服务器(请求是以空值出现)。这是我从here 获得的 Android 代码(其中一些):
String host = "turbo";
int port = 8081;
String command = "get_status";
String user = "admin";
String password = "admin";
HttpsURLConnection connection = null;
try {
Certificate ca;
String keyStoreType;
KeyStore keyStore;
String tmfAlgorithm;
TrustManagerFactory tmf;
Scanner scanner;
boolean hasInput;
/**
* Set up certificate stuff first
*/
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();
tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
URL url = new URL(String.format("https://%s:%d/test/%s", host, port, command));
connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(context.getSocketFactory());
connection.setReadTimeout(1500);
connection.setConnectTimeout(1500);
connection.setRequestMethod("POST");
connection.setDoInput(true);
connection.setDoOutput(true);
DataOutputStream os = new DataOutputStream(connection.getOutputStream());
os.writeBytes("username=admin&password=admin");
String b = getPostDataString(params);
os.flush();
os.close();
我不得不将我的 Flask 运行方法更改为 host='0.0.0.0',现在我正在连接下面的 Android 代码,但我遇到了其他问题:我在获取参数时遇到问题(用户名和密码)。
在服务器端,我收到一个参数(用户名)为空的错误。
我也试过这个:
Map<String,Object> params = new LinkedHashMap<>();
params.put("username", user);
params.put("password", password);
StringBuilder postData = new StringBuilder();
for (Map.Entry<String,Object> param : params.entrySet()) {
if (postData.length() != 0) postData.append('&');
postData.append(URLEncoder.encode(param.getKey(), "UTF-8"));
postData.append('=');
postData.append(URLEncoder.encode(String.valueOf(param.getValue()), "UTF-8"));
}
byte[] postDataBytes = postData.toString().getBytes("UTF-8");
connection.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length));
connection.setDoOutput(true);
connection.getOutputStream().write(postDataBytes);
connection.setReadTimeout(30000);
connection.setConnectTimeout(30000);
int responseCode = connection.getResponseCode();
但是 responseCode 是 400,我在服务器上遇到了同样的错误(用户名未定义) 有什么建议吗?
app.run(debug=server_debug, ssl_context=context, host='0.0.0.0', port=server_port)
我在使用自签名认证的 Android 应用程序通过 HTTPS 连接时遇到问题。
我创建了 create_key.cfg:
[req]
default_bits=2048
prompt=no
default_md=sha256
x509_extensions=v3_req
#req_extensions=req_ext
distinguished_name=dn
[dn]
C=US
ST=Florida
L=Satellite Beach
O=ThompCo, Inc
OU=turbo
emailAddress=Jordan@ThompCo.com
CN=turbo
[v3_req]
subjectAltName=@alt_names
[alt_names]
DNS.1=turbo
DNS.2=turbo.thompco.com
DNS.3=thompco.com
然后我运行了以下命令:
openssl req -x509 -nodes -days 730 -newkey rsa:2048 -keyout server.key -out server.crt -config create_key.cfg -sha256
openssl x509 -in server.crt -out server.pem -outform PEM
创建文件:
server.crt server.key server.pem
当我使用 Chrome 访问该站点时,我得到了正确的证书信息(显然存在证书不安全的警告)
当我运行我的测试 python 脚本(使用 urllib2)时,一切都按预期工作。
我试过直接卷曲:
curl https://turbo:8081
但是我收到了这个错误:
curl: (60) SSL certificate problem: self signed certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.
我尝试按照here 的建议将 crt 文件添加到 /usr/lib/ssl/certs 但没有帮助(也许这是问题的根源?)
我在 Android 中涉足(阅读“我不知道我在做什么”),并(在 Google 和世界各地许多优秀开发人员的帮助下)编写了以下内容(请注意,我将 server.pem 放入res/raw 文件夹):
package com.thompco.test;
import android.content.Context;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.net.URL;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Scanner;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
class DoCurl extends Thread {
Context myParentContext;
public DoCurl(Context context) {
myParentContext = context;
}
public void run() {
String host = "turbo";
int port = 8081;
HttpsURLConnection connection = null;
String command = "get_status";
String http = "https";
String user = "admin";
String password = "admin";
try {
Certificate ca;
String keyStoreType;
KeyStore keyStore;
String tmfAlgorithm;
TrustManagerFactory tmf;
SSLContext context;
URL url;
InputStream in;
Scanner scanner;
boolean hasInput;
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream ins = myParentContext.getResources().openRawResource(
myParentContext.getResources().getIdentifier("server",
"raw", myParentContext.getPackageName()));
BufferedInputStream caInput = new BufferedInputStream(ins);
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
keyStoreType = KeyStore.getDefaultType();
keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
url = new URL(String.format("%s://%s:%d/test/%s", http, host, port, command));
connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(context.getSocketFactory());
connection.setRequestProperty("username", user);
connection.setRequestProperty("password", password);
connection.setReadTimeout(30000);
connection.setConnectTimeout(30000);
connection.setRequestMethod("POST");
connection.setDoInput(true);
connection.connect();
if (connection.getResponseCode() == 200) {
// Success
// Further processing here
} else {
// Error handling code goes here
}
in = connection.getInputStream();
scanner = new Scanner(in);
scanner.useDelimiter("\\A");
hasInput = scanner.hasNext();
if (hasInput) {
String tmp = scanner.next();
}
} catch (Exception e) {
Exception blah = e;
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}
当它运行时,它会抛出一个 java.security.cert.CertPathValidatorException:尝试连接时找不到证书路径的信任锚。
我的目标是能够部署此设备(当前运行 Flask)并能够从本地网络上的 Android 手机访问它。如果我能侥幸成功,我不想安装真正受信任的证书。
服务器恰好在 Windows 机器上以 python 运行。它适用于我的 curl 测试,我知道这不是最好的方法,但我现在只是在测试 Android 应用程序。
我回到我的 Python 测试代码并确认我连接正确(注意我使用的是适当的 .crt 文件)应该验证自签名证书的有效性:
def send_curl(command, host, port, data, use_ssh=True):
logger = get_logger()
logger.debug("host:{}, port:{}".format(host, port))
context = ssl.create_default_context()
context.load_verify_locations("../keys/server.crt")
url = '{}://{}:{}/test/{}'.format("https", host, port, command)
for k, v in data.items():
data[k] = str(v)
data = json.dumps(data)
logger.debug("URL is {}".format(url))
logger.debug("data is {}".format(data))
req = urllib2.Request(url, data, {'Content-Type': 'application/json'})
try:
rtn = urllib2.urlopen(req, context=context)
pp = pprint.PrettyPrinter()
pp.format = my_safe_repr
print "Success:" + str(rtn.code)
pp.pprint(data)
except urllib2.HTTPError as err:
print str(err) + ":"
print err.read()
data = None
return data
【问题讨论】:
标签: android python-2.7 ssl ssl-certificate httpsurlconnection