Search code examples
javaencryptionpaypalbouncycastle

Java code for paypal button encryption using BouncyCastle deprecated methods - how to fix?


I've really been struggling to get working code, good examples, and most importantly, good documentation on how to use Paypal's Java SDK for Encrypting Website Payments. I've looked to Paypal for help (posted on their forum, contacted support), but haven't gotten any help thus far.

I went to https://www.paypal.com/us/cgi-bin/?cmd=p/xcl/rec/e​wp-code and downloaded the Paypal Java SDK. Within the zip, there is a ReadMe.txt file with instructions for setup. The instructions are simple enough.

I went to Bouncy Castle's site - http://www.bouncycastle.org/latest_releases.html - to download the latest versions of the following jars :
bcmail-jdk16-146.jar
bcpg-jdk16-146.jar
bcprov-jdk16-146.jar
bctest-jdk16-146.jar

I then went to http://www.oracle.com/technetwork/java/javase/down​loads/index.html to download the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files.

I put all the JARS in the appropriate folders, updated the classpath and then tried to compile the ClientSide.java class that came with the Paypal Java SDK.

The compiler tells me that there are deprecated classes, showing me the following errors after recompiling with -Xlint.

.\palmb\servlets\paypal\ClientSide.java:98: warning: [deprecation] addSigner(jav
a.security.PrivateKey,java.security.cert.X509Certi​ficate,java.lang.String) in org.bouncycastle.cms.CMSSignedDataGenerator has been deprecated
                signedGenerator.addSigner( privateKey, certificate, CMSSignedDataGenerator.DIGEST_SHA1 );
                               ^
.\palmb\servlets\paypal\ClientSide.java:101: warning: [unchecked] unchecked call
 to add(E) as a member of the raw type java.util.ArrayList
                certList.add(certificate);
                            ^
.\palmb\servlets\paypal\ClientSide.java:103: warning: [deprecation] addCertificatesAndCRLs(java.security.cert.CertStor​e) in org.bouncycastle.cms.CMSSignedGenerator has been deprecated
                signedGenerator.addCertificatesAndCRLs(certStore);
                               ^
.\palmb\servlets\paypal\ClientSide.java:110: warning: [deprecation] generate(org.bouncycastle.cms.CMSProcessable,boole​an,java.lang.String) in org.bouncycastle.cms.CMSSignedDataGenerator has been deprecated
                CMSSignedData signedData = signedGenerator.generate(cmsByteArray, true, "BC");
                                                  ​        ^
.\palmb\servlets\paypal\ClientSide.java:115: warning: [deprecation] addKeyTransRecipient(java.security.cert.X509Certif​icate) in org.bouncycastle.cms.CMSEnvelopedGenerator has been deprecated  envGenerator.addKeyTransRecipient(payPalCert);
                            ^
.\palmb\servlets\paypal\ClientSide.java:116: warning: [deprecation] generate(org.bouncycastle.cms.CMSProcessable,java.​lang.String,java.lang.String) in org.bouncycastle.cms.CMSEnvelopedDataGenerator has been deprecated
                CMSEnvelopedData envData = envGenerator.generate( new CMSProcessableByteArray(signed),
                                                  ​     ^
6 warnings

I have Java 1.6 running on my machine. I'm disappointed in Paypal, in that they haven't provided nearly adequate, easy to understand documentation, and on to of that, for someone who needs an out of the box setup, their code doesn't work.

I went to Bouncy Castle's site (www.bouncycastle.org) and briefly looked at the documentation (http://www.bouncycastle.org/documentation.html) for version 1.6 - but I honestly have no clue how to use the methods that replace the deprecated ones.

Does anybody have experience with this Java Paypal code? Or experience with BouncyCastle and their code? If so, I'm in great need of some help.


ClientSide class

package palmb.servlets.paypal;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertStore;
import java.security.cert.CertStoreException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Enumeration;

import org.bouncycastle.cms.CMSEnvelopedData;
import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.openssl.PEMReader;
import org.bouncycastle.util.encoders.Base64;

/**
 */
public class ClientSide 
{
    private String  keyPath;
    private String  certPath;
    private String  paypalCertPath;
    private String  keyPass;

    public ClientSide( String keyPath, String certPath, String paypalCertPath, String keyPass )
    {
        this.keyPath = keyPath;
        this.certPath = certPath;
        this.paypalCertPath = paypalCertPath;
        this.keyPass = keyPass;
    }   

    public String getButtonEncryptionValue(String _data, String _privateKeyPath, String _certPath, String _payPalCertPath,
                                            String _keyPass) throws IOException,CertificateException,KeyStoreException,
                                            UnrecoverableKeyException,InvalidAlgorithmParameterException,NoSuchAlgorithmException,
                                            NoSuchProviderException,CertStoreException,CMSException {
        _data = _data.replace(',', '\n');
        CertificateFactory cf = CertificateFactory.getInstance("X509", "BC");

        // Read the Private Key
        KeyStore ks = KeyStore.getInstance("PKCS12", "BC");
        ks.load( new FileInputStream(_privateKeyPath), _keyPass.toCharArray() );

        String keyAlias = null;
        Enumeration aliases = ks.aliases();
        while (aliases.hasMoreElements()) {
            keyAlias = (String) aliases.nextElement();
        }

        PrivateKey privateKey = (PrivateKey) ks.getKey( keyAlias, _keyPass.toCharArray() );

        // Read the Certificate
        X509Certificate certificate = (X509Certificate) cf.generateCertificate( new FileInputStream(_certPath) );

        // Read the PayPal Cert
        X509Certificate payPalCert = (X509Certificate) cf.generateCertificate( new FileInputStream(_payPalCertPath) );

        // Create the Data
        byte[] data = _data.getBytes();

        // Sign the Data with my signing only key pair
        CMSSignedDataGenerator signedGenerator = new CMSSignedDataGenerator();

        signedGenerator.addSigner( privateKey, certificate, CMSSignedDataGenerator.DIGEST_SHA1 );

        ArrayList certList = new ArrayList();
        certList.add(certificate);
        CertStore certStore = CertStore.getInstance( "Collection", new CollectionCertStoreParameters(certList) );
        signedGenerator.addCertificatesAndCRLs(certStore);

        CMSProcessableByteArray cmsByteArray = new CMSProcessableByteArray(data);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        cmsByteArray.write(baos);
        System.out.println( "CMSProcessableByteArray contains [" + baos.toString() + "]" );

        CMSSignedData signedData = signedGenerator.generate(cmsByteArray, true, "BC");

        byte[] signed = signedData.getEncoded();

        CMSEnvelopedDataGenerator envGenerator = new CMSEnvelopedDataGenerator();
        envGenerator.addKeyTransRecipient(payPalCert);
        CMSEnvelopedData envData = envGenerator.generate( new CMSProcessableByteArray(signed),
                CMSEnvelopedDataGenerator.DES_EDE3_CBC, "BC" );

        byte[] pkcs7Bytes = envData.getEncoded();


        return new String( DERtoPEM(pkcs7Bytes, "PKCS7") );

    }

    public static byte[] DERtoPEM(byte[] bytes, String headfoot) 
    {
        ByteArrayOutputStream pemStream = new ByteArrayOutputStream();
        PrintWriter writer = new PrintWriter(pemStream);

        byte[] stringBytes = Base64.encode(bytes);

        System.out.println("Converting " + stringBytes.length + " bytes");

        String encoded = new String(stringBytes);

        if (headfoot != null) {
            writer.print("-----BEGIN " + headfoot + "-----\n");
        }

        // write 64 chars per line till done
        int i = 0;
        while ((i + 1) * 64 < encoded.length()) {
            writer.print(encoded.substring(i * 64, (i + 1) * 64));
            writer.print("\n");
            i++;
        }
        if (encoded.length() % 64 != 0) {
            writer.print(encoded.substring(i * 64)); // write remainder
            writer.print("\n");
        }
        if (headfoot != null) {
            writer.print("-----END " + headfoot + "-----\n");
        }
        writer.flush();
        return pemStream.toByteArray();
    }

}


ButtonEncryption class

package palmb.servlets.paypal;

//import com.paypal.crypto.sample.*;

import palmb.servlets.paypal.ClientSide;

import java.io.*;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertStoreException;
import java.security.cert.CertificateException;
import org.bouncycastle.cms.CMSException;

/**
 */
public class ButtonEncryption {


    //path to public cert
    private static String certPath = "C:/jakarta-tomcat/webapps/PlanB/Certs/public-cert.pem";

    //path to private key in PKCS12 format
    private static String keyPath = "C:/jakarta-tomcat/webapps/PlanB/Certs/my_pkcs12.p12";

    //path to Paypal's public cert
    private static String paypalCertPath = "C:/jakarta-tomcat/webapps/PlanB/Certs/paypal_cert_pem.txt";

    //private key password
    private static String keyPass = "password"; //will be replaced with actual password when compiled and executed

    //the button command, properties/parameters
    private static String cmdText = "cmd=_xclick\[email protected]\nitem_name=vase\nitemprice=25.00";  //cmd=_xclick,[email protected],amount=1.00,currency_code=USD

    //output file for form code
    private static String output = "test.html";


    public static void main(String[] args) 
    {
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); 


        String stage = "sandbox";

        try 
        {
            ClientSide client_side = new ClientSide( keyPath, certPath, paypalCertPath, keyPass );

            String result = client_side.getButtonEncryptionValue( cmdText, keyPath, certPath, paypalCertPath, keyPass );

            File outputFile = new File( output );
            if ( outputFile.exists() )
                outputFile.delete();

            if ( result != null && result != "")
            {
                try {        
                    OutputStream fout= new FileOutputStream( output );
                    OutputStream bout= new BufferedOutputStream(fout);
                    OutputStreamWriter out = new OutputStreamWriter(bout, "US-ASCII");

                    out.write( "<form action=\"https://www." );
                    out.write( stage );
                    out.write( "paypal.com/cgi-bin/webscr\" method=\"post\">" );  
                    out.write( "<input type=\"hidden\" name=\"cmd\" value=\"_s-xclick\">" );  ;
                    out.write( "<input type=\"image\" src=\"https://www." );
                    out.write( stage );
                    out.write( "paypal.com/en_US/i/btn/x-click-but23.gif\" border=\"0\" name=\"submit\" " );
                    out.write( "alt=\"Make payments with PayPal - it's fast, free and secure!\">" );
                    out.write( "<input type=\"hidden\" name=\"encrypted\" value=\"" );
                    out.write( result );
                    out.write( "\">" );
                    out.write( "</form>");

                    out.flush();  // Don't forget to flush!
                    out.close();
                  }
                  catch (UnsupportedEncodingException e) {
                    System.out.println(
                     "This VM does not support the ASCII character set."
                    );
                  }
                  catch (IOException e) {
                    System.out.println(e.getMessage());        
                  }
            }
        } 
        catch (NoSuchAlgorithmException e) 
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        catch (NoSuchProviderException e) 
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        catch (IOException e) 
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        catch (CMSException e) 
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        catch (CertificateException e) 
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        catch (KeyStoreException e) 
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        catch (UnrecoverableKeyException e) 
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        catch (InvalidAlgorithmParameterException e) 
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        catch (CertStoreException e) 
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}


Edited - Exception from running ButtonEncryption class

C:\jakarta-tomcat\webapps\PlanB\WEB-INF\classes>java palmb.servlets.paypal.ButtonEncryption
java.io.IOException: exception decrypting data - java.security.InvalidKeyException: Illegal key size
        at org.bouncycastle.jce.provider.JDKPKCS12KeyStore.cryptData(Unknown Source)
        at org.bouncycastle.jce.provider.JDKPKCS12KeyStore.engineLoad(Unknown Source)
        at java.security.KeyStore.load(Unknown Source)
        at palmb.servlets.paypal.ClientSide.getButtonEncryptionValue(ClientSide.
java:63)
        at palmb.servlets.paypal.ButtonEncryption.main(ButtonEncryption.java:81)

Solution

  • Couldn't get the classes from Paypal to work, so decided to give the Paypal Button API a try. This proved to be the best way to go. I could still use Java, and let Paypal take care of encrypting the buttons. It turned out to be a simple process once I got things coded correctly.

    To view information about the Paypal Button API click here.