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"+
"&currencyCode=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 证书?

Android设置内容类型HttpPost

通过 HttpPost 从 Android 将图像(jpg)发送到 Servlet(WebServer)

如何为“Visual Studio emulator for android”的模拟器安装证书?

异步任务HttpPost请求DoInbackground调用web api Android Studio Java,如何添加正文字符串数组?

Android - 如何使用 HttpClient 接受任何证书并同时传递证书