我正在开发一个Java程序,该程序会将POST请求发送到网站供我的公司使用。我们不拥有该网站,它们与我们分开。我一直在尝试各种方法来实际传递它想要的非常挑剔的参数,以便我可以从程序中进行处理(而不是手动进行处理)。
我发现Apache HttpClient 4.3似乎是我尝试尝试访问它的最佳途径,任何导致网站生气的响应都会告诉我我的用户名和密码,并且无效/未授权。
但是随后我得到了一个错误,因为站点证书不匹配,我联系了他们的支持人员,据说他们与另一个站点共享一个基础结构,因此可能会出现证书不匹配的情况。
因此,我进入命令行并生成了一个密钥库,将该密钥库传递给程序,然后得到错误“ java.security.cert.CertificateException:没有主题备用DNS名称匹配”。
一些狩猎使我使用了验证程序,从而消除了错误。
然后我意识到我无法使URLConnection / HttpsURLConnection和HttpClient / HttpPost一起工作。那就是我被困住的地方。我不确定如何使处理我的密钥库,TrustManager,SSLSocketFactory等的代码连接到我实际必须连接和POST的部分。
处理证书和验证的代码:
InputStream in = new FileInputStream(new File("C:\\Program Files (x86)\\Java\\jre7\\bin\\my.keystore"));
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(in, "blahblah".toCharArray());
in.close(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
X509TrustManager defaultTrustManager = (X509TrustManager)tmf.getTrustManagers()[0];
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new TrustManager[] {defaultTrustManager}, null);
javax.net.ssl.SSLSocketFactory sslSocketFactory = context.getSocketFactory();
URL url = new URL("https://emailer.driveclick.com/dbadmin/xml_post.pl");
URLConnection con = url.openConnection();
((HttpsURLConnection) con).setSSLSocketFactory(sslSocketFactory);
((HttpsURLConnection) con).setHostnameVerifier(new Verifier());
con.connect();
in = con.getInputStream();
应该将我连接到该网站的代码:
try {
//log into the website
String url2 = "https://emailer.driveclick.com/dbadmin/xml_post.pl";
HttpClient client = HttpClientBuilder.create().build();
HttpPost post = new HttpPost(url2);
post.setHeader("User-Agent", USER_AGENT);
List<BasicNameValuePair> urlParameters = new ArrayList<>();
urlParameters.add(new BasicNameValuePair("username", "namefoo"));
urlParameters.add(new BasicNameValuePair("api_password", "passfoo"));
post.setEntity(new UrlEncodedFormEntity(urlParameters));
org.apache.http.HttpResponse response = client.execute(post);
System.out.println("\nSending 'POST' request to URL : " + url2);
System.out.println("Post parameters : " + post.getEntity());
System.out.println("Response Code : " + response.getStatusLine().getStatusCode());
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuffer result = new StringBuffer();
String line = "";
while ((line = rd.readLine()) != null)
{
result.append(line);
}
System.out.println(result.toString());
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(LastFileMove.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(LastFileMove.class.getName()).log(Level.SEVERE, null, ex);
}
编辑:我忘了包括我为我参考的验证程序制作的小类。
public class Verifier implements HostnameVerifier
{
public boolean verify(String arg0, SSLSession arg1) {
return true; // mark everything as verified
}
}
更新5/8/2014
现在,SSLConext和Verifier的设置如下:
SSLContext sslContext = SSLContexts.custom()
.useTLS()
.loadTrustMaterial(ks)
.build();
X509HostnameVerifier verifier = new AbstractVerifier()
{
@Override
public void verify(final String host, final String[]
cns, final String[] subjectAlts) throws SSLException
{
verify(host, cns, subjectAlts, true);
}
};
我继续将HttpClient更改为此处的可关闭对象:
CloseableHttpClient client = HttpClients.custom()
sslSocketFactory)
.setHostnameVerifier(verifier)
.setSslcontext(sslContext)
.build();
我又回到了“ javax.net.ssl.SSLException:证书中的主机名不匹配”的错误。有什么建议吗?
最佳答案
我不知道Verifier
的实现方式,但是此代码段演示了如何创建自定义主机名验证程序,而HttpClient附带的这些验证程序都不符合他们的需求
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream in = new FileInputStream(new File("C:\\Program Files (x86)\\Java\\jre7\\bin\\my.keystore"));
try {
ks.load(in, "blahblah".toCharArray());
} finally {
in.close();
}
SSLContext sslContext = SSLContexts.custom()
.useTLS()
.loadTrustMaterial(ks)
.build();
X509HostnameVerifier verifier = new AbstractVerifier() {
@Override
public void verify(final String host, final String[] cns, final String[] subjectAlts) throws SSLException {
verify(host, cns, subjectAlts, true);
}
};
CloseableHttpClient hc = HttpClients.custom()
.setSslcontext(sslContext)
.setHostnameVerifier(verifier)
.build();