Android:如何使用证书做 HttpPost
Posted
技术标签:
【中文标题】Android:如何使用证书做 HttpPost【英文标题】:Android: how to do HttpPost with a certificate 【发布时间】:2014-05-09 18:50:45 【问题描述】:我有一个执行 HttpPost 的应用程序。
现在我需要在帖子中添加一个证书,以便接收 HttpPost 的服务器接受。
请问我该怎么做?
非常感谢任何建议!!!
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("https://svcs.sandbox.paypal.com/AdaptivePayments/Preapproval");
try
httppost.addHeader("X-PAYPAL-SECURITY-USERID", "maurizio.pietrantuono_api1.db.com");
httppost.addHeader("X-PAYPAL-SECURITY-PASSWORD", "1395657583");
httppost.addHeader("X-PAYPAL-SECURITY-SIGNATURE", "A0GgTivJ6ivBB8QDTl.cZfiYK5d9AZwsFixwIUdUhJc4JXTriwpfU2zw");
httppost.addHeader("X-PAYPAL-REQUEST-DATA-FORMAT", "NV");
httppost.addHeader("X-PAYPAL-RESPONSE-DATA-FORMAT", "NV");
httppost.addHeader("X-PAYPAL-APPLICATION-ID", "APP-80W284485P519543T");
StringEntity se=new StringEntity("cancelUrl=http://your_cancel_url"+
"¤cyCode=USD"+
"&endingDate=2015-03-29T08%3A00%3A00.000Z"+
"&maxAmountPerPayment=200.00"+
"&maxNumberOfPayments=30"+
"&maxTotalAmountOfAllPayments=1500.00"+
"&pinType=NOT_REQUIRED"+
"&requestEnvelope.errorLanguage=en_US"+
"&returnUrl=http://www.google.com"+
"&startingDate=2014-04-29T07%3A00%3A00.000Z"+
"&senderEmail=mauriziop-facilitator@hotmail.it");
httppost.setEntity(se);
HttpResponse response = httpclient.execute(httppost);
【问题讨论】:
什么证书? @kupsef 好问题,我不知道 :-(((( 接收HttpPost的服务器需要android应用程序的证书才能进行身份验证... @kupsef 接收服务器使用 HTTPS 看我的回答,它有你需要的一切。 httppost.addHeader("Accept", "Application/json");可能这会有所帮助,因为在我的情况下它解决了我的问题...... 【参考方案1】:这是由于 android 应用程序不接受服务器提供的安全证书造成的。有时您可能已经看到浏览器请求许可以继续说“该站点的安全证书不受信任!”。同样的事情在android中产生一个异常。所以你必须告诉应用程序接受服务器提供的任何安全证书。这是如何做到的。 http://madurangasblogs.blogspot.com/2013/08/avoiding-javaxnetsslsslpeerunverifiedex.html
但我必须告诉你,这对于生产应用程序来说不是一个好的做法。这将违反拥有安全证书的目的。
此外尝试这样的事情(你需要让你的套接字工厂使用这个默认的信任管理器):
X509TrustManager manager = null;
FileInputStream fs = null;
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
try
fs = new FileInputStream(System.getProperty("javax.net.ssl.trustStore"));
keyStore.load(fs, null);
finally
if (fs != null) fs.close();
trustManagerFactory.init(keyStore);
TrustManager[] managers = trustManagerFactory.getTrustManagers();
for (TrustManager tm : managers)
if (tm instanceof X509TrustManager)
manager = (X509TrustManager) tm;
break;
来源: http://android.bigresource.com/Android-Issues-with-httppost-Authentication-challenge-is-empty-Gg4BTT7Dr.html Get the authentication from the moodle for a android application
【讨论】:
【参考方案2】:您需要告诉 SSLSocketFactory(org.apache.http,不是 javax)您的密钥库,并配置您的 DefaultHTTPClient 以将其用于 https 连接。
我猜this 和 this 会有所帮助。
【讨论】:
请提供一些上下文或解释,而不仅仅是答案链接!如果只有链接,这应该是一个评论。谢谢。【参考方案3】:好的,那么你要做的就是将服务器的证书链导入到你的应用程序中。
首先,您必须下载整个证书链。您可以使用任何网络浏览器执行此操作。假设你有 chrome:
打开服务器 URL "https://svcs.sandbox.paypal.com/AdaptivePayments/Preapproval" 点击 URL 左侧的锁形图标 连接 --> 证书信息 --> 证书路径 您必须下载所有三个元素!现在您有了证书,您必须将它们放入密钥库中。我建议你使用 BKS 密钥库,因为它是 android 内部支持的。
下载 bouncycastle。请务必使用 1.46 版本,因为这是 Android 使用的版本。 I used this one.使用此命令导入您的密钥:(这里密码无关紧要,因为我们使用密钥库只是为了存储密钥)
keytool -list -keystore "my_keystore_path/mykeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "provider_path/bcprov-jdkxx-xxx.jar" -storetype BKS -storepass "my_password"
将密钥库添加到您的应用程序。你可以把它放到资产中,甚至作为原始资源。 在此之后,您必须创建一个供您的 HttpClient 使用的 ClientConnectionmanager。
public ClientConnectionManager createClientConnectionManager()
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
// Register for port 443 our SSLSocketFactory with our keystore
// to the ConnectionManager
registry.register(new Scheme("https", newSslSocketFactory(), 443));
return new SingleClientConnManager(getParams(), registry);
public SSLSocketFactory newSslSocketFactory()
try
// Get an instance of the Bouncy Castle KeyStore format
KeyStore trusted = KeyStore.getInstance("BKS");
// Get the raw resource, which contains the keystore with
// your trusted certificates (root and any intermediate certs)
InputStream in = context.getResources().openRawResource(R.raw.mykeystore);
try
// Initialize the keystore with the provided trusted certificates
// Also provide the password of the keystore
trusted.load(in, "my_password".toCharArray());
finally
in.close();
// Pass the keystore to the SSLSocketFactory. The factory is responsible
// for the verification of the server certificate.
SSLSocketFactory sf = new SSLSocketFactory(trusted);
// Hostname verification from certificate
// http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
return sf;
catch (Exception e)
throw new AssertionError(e);
并以这种方式使用它:
HttpClient c = new DefaultHttpClient(createClientConnectionManager(), new DefaultHttpClient().getParams());
请注意,如果您使用此 ClientConnectionManager,那么只有那些站点将被接受,其整个证书路径都在密钥库中。如果您想通过此 ClientConnectionmanager 与其他站点进行安全连接,则必须导入其他站点的证书
这里有一个详细的教程,介绍如何通过扩展 DefaultHttpClient 来做到这一点,因此您不必使用那个复杂的构造函数。
http://transoceanic.blogspot.hu/2011/11/android-import-ssl-certificate-and-use.html
【讨论】:
【参考方案4】:在 Java 和 Android 等平台上,您可能面临最合乎逻辑但最复杂的事情之一。事实证明,没有一种直接的方法可以实现这一点,因为证书种类繁多,并且没有一种方法可以对所有证书进行 HTTP 调用,因为其中一些可能由未知 CA 签名,而另一些可能由未知 CA 签名需要中间包才能使证书有效等。
可能会帮助您的一种方法是将证书存储到用户的密钥库中,这样您就可以发出 HTTPS 请求,因为他们已经信任目标 SSL 证书。在这种情况下,您将创建一个新的KeyStore
,导入证书,然后发出 HTTPS 请求:
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
// Some of these exist in more than one package, be careful to include these
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
// This would reference to your KeyStore to store the certificate
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null); // This will make a new store
// In the next line, specify the file path
// You can also export the destination certificate (in your case, Paypal's)
// put it as a hardcoded `String` and work with it.
InputStream is = ...;
BufferedInputStream bis = new BufferedInputStream(is);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
while (bis.available() > 0)
Certificate cert = cf.generateCertificate(bis);
trustStore.setCertificateEntry("myAlias" + bis.available(), cert);
之后,向 SSL 服务器发出 HTTP 请求应该可以工作了。
---- 编辑----
您不需要为站点生成证书,因为该站点已经有一个。您必须导入其现有的证书。我正在向您展示如何做到这一点的示例,它是在 Firefox 和西班牙语下完成的,但我想您会用您的语言推断出关键词。
转到您要导出其证书的站点。在此示例中,我正在使用 Paypal
,但您可以为 any 站点执行此操作。还要考虑到一个站点可能有很多证书,这意味着,例如,https://www.paypal.com
有一个,https://sandbox.paypal.com
有另一个完全不同的证书。你需要检查一下。
在地址栏的左侧,单击显示Paypal, Inc (US)
的绿色文本(表明该站点具有 SSL 证书)。
你会看到这样的屏幕:
单击More information
按钮,您将看到如下内容:
点击See certificate
(或类似的)按钮,现在您将看到这个屏幕:
点击Details
选项卡,在列表中逐个选择站点(在这种情况下,首先是VeriSign Class 3 Public Primary Certification Authority - g5
,然后是VeriSign Class 3 Extended Validation SSL CA
,最后是www.paypal.com
),然后,点击Export...
在该屏幕的底部。系统会要求您导出 PEM
证书。
您刚刚所做的是导出整个证书链,但现在您必须将它们放在一起。只需打开一个文本文件,然后按照您下载的顺序依次附加您刚刚下载的三个证书,并特别注意不要包含额外的空格。
这是您必须在代码中导入的证书。在我包含的 sn-p 中,有一个地方需要放置文件的路径,就是这个。由于您可能希望为所有客户包含该代码,因此您可能会做 2 件事:
将证书作为项目的一部分导入。这将使运行您的应用程序的任何客户端都将拥有该证书,这样您就可以使用上面的代码而无需进行任何修改,但是当 Paypal 更改该证书时您需要小心(它们通常会在一段时间后过期并且需要替换为新的有效证书 - 您可以在证书的属性中看到证书的到期时间)。
如上所述导出证书,将其放在公共场所(例如,Web 服务器),每次用户运行您的应用时,下载并验证密钥库中的证书是否与你刚刚读过。如果不存在,只需第一次导入即可。如果存在且不匹配,请更新它。否则,您无需执行任何操作。
【讨论】:
感谢 nKn,请问如何创建 PayPal 接受的证书? 看看我更新的答案,希望对你有帮助。 这个答案是错误的。您必须导入完整的证书链,否则握手将失败。看我的回答。 不知何故,我认为只导出最后一个证书会导出整个链。我更新了我的答案,尤其是最新截图之后的文字。【参考方案5】:首先,你把你的用户名和密码放在上面的代码中,这很危险!! (如果不是假的就换!)
其次请提供一些日志信息以获得更好的帮助!
PayPal 证书是有效的,这意味着您无需在应用程序中嵌入证书。 只是一些建议:
1- 你可以使用android-async-http 库,因为它非常灵活并且有很多选择。
2- 我不熟悉 PayPal,但您绝对应该使用 PayPal REST API。
【讨论】:
感谢 Sirlate,这些是沙盒凭据,窃取的并不多 :-)。无论如何,我必须使用 PayPal Classic Api,因为 REST 不支持我需要的 API(Preapproals) @Sirilate 我没有日志可提供,因为到目前为止我什么都没尝试。以上是关于Android:如何使用证书做 HttpPost的主要内容,如果未能解决你的问题,请参考以下文章
如何为 react.cordova、ionic 和 android 设置 ssl 证书?
通过 HttpPost 从 Android 将图像(jpg)发送到 Servlet(WebServer)
如何为“Visual Studio emulator for android”的模拟器安装证书?
异步任务HttpPost请求DoInbackground调用web api Android Studio Java,如何添加正文字符串数组?