使用 BouncyCastle 不推荐使用的方法进行贝宝按钮加密的 Java 代码 - 如何修复?
Posted
技术标签:
【中文标题】使用 BouncyCastle 不推荐使用的方法进行贝宝按钮加密的 Java 代码 - 如何修复?【英文标题】:Java code for paypal button encryption using BouncyCastle deprecated methods - how to fix? 【发布时间】:2011-09-15 14:12:15 【问题描述】:我真的一直在努力获得工作代码、好的示例,最重要的是,获得关于如何使用 Paypal 的 Java SDK 加密网站支付的良好文档。我已经向 Paypal 寻求帮助(发布在他们的论坛上,联系了支持人员),但到目前为止还没有得到任何帮助。
我去了https://www.paypal.com/us/cgi-bin/?cmd=p/xcl/rec/ewp-code并下载了Paypal Java SDK。在 zip 中,有一个 ReadMe.txt 文件,其中包含设置说明。说明很简单。
我去了 Bouncy Castle 的网站 - http://www.bouncycastle.org/latest_releases.html - 下载以下 jar 的最新版本: bcmail-jdk16-146.jar bcpg-jdk16-146.jar bcprov-jdk16-146.jar bctest-jdk16-146.jar
然后我去http://www.oracle.com/technetwork/java/javase/downloads/index.html 下载Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files。
我将所有的 JARS 放在适当的文件夹中,更新了类路径,然后尝试编译 Paypal Java SDK 附带的 ClientSide.java 类。
编译器告诉我有已弃用的类,在使用 -Xlint 重新编译后显示以下错误。
.\palmb\servlets\paypal\ClientSide.java:98: warning: [deprecation] addSigner(jav
a.security.PrivateKey,java.security.cert.X509Certificate,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.CertStore) in org.bouncycastle.cms.CMSSignedGenerator has been deprecated
signedGenerator.addCertificatesAndCRLs(certStore);
^
.\palmb\servlets\paypal\ClientSide.java:110: warning: [deprecation] generate(org.bouncycastle.cms.CMSProcessable,boolean,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.X509Certificate) 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
我的机器上运行着 Java 1.6。我对 Paypal 感到失望,因为他们没有提供几乎足够的、易于理解的文档,此外,对于需要开箱即用设置的人来说,他们的代码不起作用。
我去了 Bouncy Castle 的网站 (www.bouncycastle.org) 并简要查看了 1.6 版的文档 (http://www.bouncycastle.org/documentation.html) - 但老实说,我不知道如何使用替换已弃用的方法。
有人对这个 Java Paypal 代码有经验吗?或者有使用 BouncyCastle 和他们的代码的经验?如果是这样,我非常需要一些帮助。
客户端类
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 类
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\nbusiness=buyer@hotmail.com\nitem_name=vase\nitemprice=25.00"; //cmd=_xclick,business=sample@paypal.com,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();
已编辑 - 运行 ButtonEncryption 类时出现异常
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)
【问题讨论】:
它们不是错误,而是警告。是不是真的在编译? --- 是的,你是对的,我检查了,代码确实编译了。哦,它们只是警告,而不是错误。我的错。我不应该担心他们吗?它们可能会以某种方式产生影响吗? 艰难的决定。你真的不应该使用折旧的方法,但看起来这是他们给你的一些示例代码。如果是这样的话,你有点不走运。也许让他们的 api/javadocs 看一下,看看他们是否有方法取代了那里的折旧方法。然后你可以使用它们。 感谢您的意见,非常感谢。我一直与 Paypal 保持联系,他们的帮助对我没有任何帮助。我想既然它编译了,我就必须坚持下去。 这些编译警告是否与我尝试运行时遇到的 IOException: 异常解密数据 - java.security.InvalidKeyException: Illegal key size 错误有关该程序?如果是这样,我需要解决这个问题,有什么建议可以做吗? 【参考方案1】:您收到了非法KeySize 错误,因为您没有将 JCE 文件安装在正确的位置。您的系统上可能有多个 JRE。
至于回答您关于已弃用功能的问题...我想出了以下 PayPal 示例代码的替换功能,效果很好(基于bouncycastle javadoc):
private final static String getButtonEncryptionValue(String commandData, String keystorePath,
String keystorePassword, boolean sandbox) throws IOException, CertificateException, KeyStoreException,
UnrecoverableKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
NoSuchProviderException, CertStoreException, CMSException, OperatorCreationException
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
commandData = commandData.replace(',', '\n');
CertificateFactory cf = CertificateFactory.getInstance("X509", "BC");
// Read the Private Key
KeyStore ks = KeyStore.getInstance("PKCS12", "BC");
ks.load(new FileInputStream(keystorePath), keystorePassword.toCharArray());
String keyAlias = null;
Enumeration<String> aliases = ks.aliases();
while (aliases.hasMoreElements())
keyAlias = (String) aliases.nextElement();
PrivateKey privateKey = (PrivateKey) ks.getKey(keyAlias, keystorePassword.toCharArray());
// Read the Certificate
X509Certificate certificate = (X509Certificate) cf.generateCertificate(ApplicationProxyService.class
.getResourceAsStream("/myCompanyPublicCert.pem.cer"));
// Read the PayPal Cert
X509Certificate payPalCert = (X509Certificate) cf.generateCertificate(ApplicationProxyService.class
.getResourceAsStream("/paypalPublicCert" + (sandbox ? "-sandbox" : "") + ".pem.cer"));
// Create the Data
// System.out.println(commandData);
byte[] data = commandData.getBytes();
// Sign the Data with my signing only key pair
CMSSignedDataGenerator signedGenerator = new CMSSignedDataGenerator();
List<X509Certificate> certList = new ArrayList<X509Certificate>();
certList.add(certificate);
//deprecated: Store certStore = CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList));
Store certStore = new JcaCertStore(certList);
// deprecated: signedGenerator.addCertificatesAndCRLs(certStore);
signedGenerator.addCertificates(certStore);
// deprecated: signedGenerator.addSigner(privateKey, certificate, CMSSignedDataGenerator.DIGEST_SHA1);
ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(privateKey);
signedGenerator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(sha1Signer, certificate));
CMSProcessableByteArray cmsByteArray = new CMSProcessableByteArray(data);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
cmsByteArray.write(baos);
LOGGER.debug("CMSProcessableByteArray contains [" + baos.toString() + "]");
// deprecated: CMSSignedData signedData = signedGenerator.generate(cmsByteArray, true, "BC");
CMSSignedData signedData = signedGenerator.generate(cmsByteArray, true);
byte[] signed = signedData.getEncoded();
CMSEnvelopedDataGenerator envGenerator = new CMSEnvelopedDataGenerator();
// deprecated: envGenerator.addKeyTransRecipient(payPalCert);
envGenerator.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(payPalCert).setProvider("BC"));
// deprecated: CMSEnvelopedData envData = envGenerator.generate(new CMSProcessableByteArray(signed),
// CMSEnvelopedDataGenerator.DES_EDE3_CBC, "BC");
CMSEnvelopedData envData = envGenerator.generate(new CMSProcessableByteArray(signed),
new JceCMSContentEncryptorBuilder(CMSAlgorithm.DES_EDE3_CBC).setProvider("BC").build());
byte[] pkcs7Bytes = envData.getEncoded();
return new String(DERtoPEM(pkcs7Bytes, "PKCS7"));
我还想指出,示例 DERtoPEM() 函数有一个缺陷,如果加密值的最后一行恰好是 64 个字符长(0 % 64 == 0 AND 64 % 64 == 0)。以下是修复:
private static final byte[] DERtoPEM(byte[] bytes, String headfoot)
byte[] stringBytes = Base64.encode(bytes);
// System.out.println("Converting " + stringBytes.length + " bytes");
StringBuilder sb = new StringBuilder();
sb.append("-----BEGIN " + headfoot + "-----\n");
String encoded = new String(stringBytes);
// write 64 chars per line till done
int i = 0;
while ((i + 1) * 64 < encoded.length())
sb.append(encoded.substring(i * 64, (i + 1) * 64));
sb.append("\n");
i++;
// if (encoded.length() % 64 != 0) //FIXME (fixed via next line): this is a BUG that drops remaining data if data.length==64!
String remainder = encoded.substring(i * 64);
if (StringUtils.isNotEmpty(remainder))
sb.append(remainder); // write remainder
sb.append("\n");
sb.append("-----END " + headfoot + "-----\n");
return sb.toString().getBytes();
【讨论】:
【参考方案2】:无法从 Paypal 获取课程,因此决定尝试使用 Paypal Button API。事实证明这是最好的方法。我仍然可以使用 Java,让 Paypal 负责对按钮进行加密。一旦我正确编码,结果证明是一个简单的过程。
要查看有关 Paypal 按钮 API 的信息,请单击 here。
【讨论】:
您的意思是 this 按钮管理器 API 现在已弃用吗?以上是关于使用 BouncyCastle 不推荐使用的方法进行贝宝按钮加密的 Java 代码 - 如何修复?的主要内容,如果未能解决你的问题,请参考以下文章
无法验证签名 (cmssigneddata) bouncycastle
C# 与JAVA 的RSA 加密解密交互,互通,C#使用BouncyCastle来实现私钥加密,公钥解密的方法