Pergunta

I have an Application that performs an HttpPost.

Now I need to add a Certificate to the post to be accepted by the server receiving the HttpPost.

Please how do I go about it?

Any suggestion very much appreciated !!!

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);
Foi útil?

Solução 2

You're probably facing one of the most logical yet most complicate things to do on a plaform like Java and therefore also Android. Turns out that there's not a one-and-direct way of achieving that since there are lots of kinds of certificates and there's not a method for making a HTTP call for all of them, because some of them might be signed by unknown CAs, others have intermediate bundles required in order for the cert be valid, etc.

Probably a way that will help you is storing the certificate into the user's keystore and this way you can make HTTPS requests because they'll already trust the destination SSL certificate. In this case, you'll be creating a new KeyStore, import the certificate and then make the HTTPS request:

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);
}

Afterwards, making the HTTP request to the SSL server should work.

---- EDIT ----

You don't need to generate a certificate for a site, as this site already has one. You have to import its already existing certificate. I'm showing you an example of how to do it, it's done under Firefox and in spanish, but I guess you'll deduce the key words in your language.

Go to the site whose certificate you want to export. In this example, I'm doing Paypal's, but you might do it for any site. Also take in consideration that a site might have many certificates, that means that, for instance, https://www.paypal.com has one and https://sandbox.paypal.com has another totally different. You'll need to check this.

On the left side of the address bar, click on the Green text that says Paypal, Inc (US) (that announces that the site has a SSL certificate).

first

You'll see a screen like this:

second

Click on the More information button and you'll see something like this:

third

Click on the See certificate (or similar) button, and now you'll see this screen:

forth

Click on the Details tab, in the list, select sites one by one (in this case, firstly VeriSign Class 3 Public Primary Certification Authority - g5, then VeriSign Class 3 Extended Validation SSL CA, and lastly www.paypal.com), and afterwards, click on Export... at the bottom of that screen. You'll be asked to export a PEM certificate.

What you have just done is exporting the whole certificate chain, but now you have to put it all together. Simply open a text file and append the three certificates you just downloaded one after other in the order you downloaded and having special care to not include additional spaces.

That's the certificate you'll have to import in your code. In the snippet I included, there's a place you need to put a path to a file, it would be this. Since you probably want to include that code for all your clients, you might do 2 things:

  • Import the certificate as a part of your project. This would make that any client running your app will have that cert, this way you can use the code above without any moddification, but you'd need to be careful when Paypal would change that certificate (they usually expire after a time and need to be replaced by a new valid one - you can see the expiration time of a certificate in the properties of the certificate).

  • Export the certificate as described above, put it in a public place (for instance, a web server) and each time a user runs your app, download it and verify if the certificate in the keystore is the same that you just have read. If it doesn't exist, simply import it for the first time. If it exists and doesn't match, update it. Else, you don't need to do anything.

Outras dicas

This is caused when the android application doesn't accept the security certificate presented by the server. Sometimes you may have seen that the browser ask for the permission to proceed saying "The site's security certificate is not trusted!". The same thing generate an exception in android. So you have to say to the application to accept any security certificate presented by the server. This is how to do it. http://madurangasblogs.blogspot.com/2013/08/avoiding-javaxnetsslsslpeerunverifiedex.html

But I must worn you that this is not a good practice for a production application. This will violate the purpose of having a security certificate.

moreover Try something like this (you'll need to get your socket factory to use this default trust manager):

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;
    }
}

Sources: http://android.bigresource.com/Android-Issues-with-httppost-Authentication-challenge-is-empty-Gg4BTT7Dr.html Get the authentication from the moodle for a android application

Alright, so what you have to do is to import the server's certificate chain to your application.

First you have to download the whole certificate chain. You can do this with any web browser. Assuming you have chrome:

Now as you have the certificates, you have to put them into a keystore. I suggest you to use BKS keystore since it is supported by android internally.

  • Download bouncycastle. Be sure to use the 1.46 version since that is what used by Android. I used this one.
  • Use this command to import your keys: (the password does not matters here, because we use the keystore just to store the keys)

    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"

Add the keystore to your application. You can put it to assets, or even as a raw resource. After this you have to create a ClientConnectionmanager to be used by your HttpClient.

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);
    }
}

And use it this way:

HttpClient c = new DefaultHttpClient(createClientConnectionManager(), new DefaultHttpClient().getParams());

Note that if you use this ClientConnectionManager, then only those sites will be accepted, whose whole certificate path is in the keystore. You have to import other site's certificates if you want to make secure conections to them via this ClientConnectionmanager

Here is a detailed tutorial on how to do this by extending DefaultHttpClient, so you don't have to use that complicated constructor.

http://transoceanic.blogspot.hu/2011/11/android-import-ssl-certificate-and-use.html

You need to tell an SSLSocketFactory (org.apache.http, not javax) about your keystore, and configure your DefaultHTTPClient to use it for https connections.

I guess this and this could help.

Fist of all, you put your userId and password in the above code, which is dangerous!! (Change it if it's not fake!)

Secondly please provide some log information for better help!

PayPal certificate is a valid one which means you don't need to take care of embedding certificate in your application. Just some advice:

1- You can use android-async-http library, because it's very flexible and have lot's of options.

2- I'm not familiar with PayPal, but you should definitely use PayPal REST API.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top