【问题标题】:Android: how to do HttpPost with a certificateAndroid:如何使用证书做 HttpPost
【发布时间】: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");可能这会有所帮助,因为在我的情况下它解决了我的问题......

标签: android paypal http-post


【解决方案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 连接。

    我猜thisthis 会有所帮助。

    【讨论】:

    • 请提供一些上下文或解释,而不仅仅是答案链接!如果只有链接,这应该是一个评论。谢谢。
    【解决方案3】:

    好的,那么你要做的就是将服务器的证书链导入到你的应用程序中。

    首先,您必须下载整个证书链。您可以使用任何网络浏览器执行此操作。假设你有 chrome:

    现在您有了证书,您必须将它们放入密钥库中。我建议你使用 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 我没有日志可提供,因为到目前为止我什么都没尝试。
      猜你喜欢
      • 1970-01-01
      • 2012-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-22
      • 1970-01-01
      • 2016-11-07
      相关资源
      最近更新 更多