由于前段时间开发需要,要实现企业通过微信支付到个人银行卡的功能,现在整理一下,里面的极大部分代码是一位大神写好的,然后自己在基础上列出了123,如果有什么不好的地方望指出,以备后期改进,谢谢!!!
开发步骤:
一,准备工作
1.商户(企业)入驻微信,即在微信的商户平台进行注册
2.获取:mch_id(商户号),key(就是你商户平台设置的密钥key)
3.商户在产品中心,开通企业付款到个人银行卡功能
4.获取证书,微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->证书下载
注:下载下来后,需要安装,商户证书调用或安装都需要使用到密码,该密码的值为微信商户号(mch_id), 其中的apiclient_cert.p12是商户证书文件,java开发仅使用此证书文件。
5.商户充值
二,开发
1.需要的jar包
commons-codec-1.9.jar,commons-logging-1.1.3.jar,fluent-hc-4.3.4.jar,httpclient-4.5.jar,httpclient-cache-4.3.4.jar,
httpcore-4.4.1.jar,httpmime-4.3.4.jar,javabase64-1.3.1.jar,jdom-1.0.jar,servlet-api.jar,sun.misc.BASE64Decoder.jar,
xml-apis-1.0.b2.jar
2.需要的配置参数
public class WChatInfo {
//微信商户的id
public static final String MCH_ID = "1480911042";
//微信商户的key 签名使用
public static final String KEY="1bKk1wpnRbT2seZKOM2GEWoSJSoL1SRF";
public static final String CHARSET = "UTF-8";//编码格式
//证书url
public static final String CA_LICENSE = "E:" + File.separator + "pay"
+ File.separator + "cert" + File.separator + "apiclient_cert.p12";
}
3.获取商户号的公钥
调用的接口:https://fraud.mch.weixin.qq.com/risk/getpublickey
请求参数:
mch_id,nonce_str(长度小于32位的随机字符串),sign(签名),sign_type(签名类型:MD5)
请求方式:post
public class GetPublicKey {
public String getPublicKey() throws Exception {
SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
String nonce_str = StringUtils1.getStrRandom(28);//获取随机字符串
parameters.put("mch_id", WChatInfo.MCH_ID);
parameters.put("nonce_str", nonce_str);
parameters.put("sign_type", "MD5");
// 创建签名
String sign = SignUtils.creatSign(WChatInfo.CHARSET, parameters);
System.out.println(sign);
TreeMap<String, String> tmap = new TreeMap<String, String>();
tmap.put("mch_id", WChatInfo.MCH_ID);
tmap.put("nonce_str", nonce_str);
tmap.put("sign_type", "MD5");
tmap.put("sign", sign);
String xml = XMLUtils.getRequestXml(tmap);//将请求参数转换为请求报文
//带证书请求
String xml1 = HttpClientCustomSSL.httpClientResultGetPublicKey(xml);//发送http的post请求获取公钥报文
String publicKey = XMLUtils.Progress_resultParseXml(xml1);//解析腾迅返回的公钥xml并获取公钥元素
return publicKey;
}
}
/**
*获取随机字符串
**/
public class StringUtils1 {
/**
* 获取随机字符串
* @param length 生成的随机字符串长度
* */
public static String getStrRandom(int length){
if(length<1){
return "";
}else{
String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz";
StringBuilder sb = new StringBuilder();
Random ra = new Random();
int index = 0;
for (int i = 0; i < length ;i++ ) {
index = ra.nextInt(str.length());
sb.append(str.charAt(index));
}
return sb.toString();
}
}
}
/**
*创建签名
**/
public class SignUtils {
/**
* @param characterEncoding 编码格式 utf-8
* */
public static String creatSign(String characterEncoding,
SortedMap<Object, Object> parameters) {
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();
Iterator it = es.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String k = (String)entry.getKey();
Object v = entry.getValue();
if(null != v && !"".equals(v)
&& !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + WChatInfo.KEY);
String sign = MD5Utils.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
System.out.println(sign);
return sign;
}
}
/**
*MD5加密
**/
public class MD5Utils {
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
resultSb.append(byteToHexString(b[i]));
return resultSb.toString();
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
n += 256;
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname))
resultString = byteArrayToHexString(md.digest(resultString
.getBytes()));
else
resultString = byteArrayToHexString(md.digest(resultString
.getBytes(charsetname)));
} catch (Exception exception) {
}
return resultString;
}
private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
}
/**
*将请求参数转换为请求报文和解析腾迅返回的公钥xml并获取公钥元素
**/
public class XMLUtils {
/**
* 将请求参数转换为请求报文
*/
@SuppressWarnings("rawtypes")
public static String getRequestXml(TreeMap<String, String> parameters)
throws Exception {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = parameters.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if ("mch_id".equalsIgnoreCase(k) || "nonce_str".equalsIgnoreCase(k)
|| "sign".equalsIgnoreCase(k)) {
sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
} else {
sb.append("<" + k + ">" + v + "</" + k + ">");
}
}
sb.append("</xml>");
return sb.toString();
}
/**
* 解析腾迅返回的公钥xml并获取公钥元素
* @param xml
* @return
*/
@SuppressWarnings("unchecked")
public static String Progress_resultParseXml(String xml) {
String publicKey = null;
try {
StringReader read = new StringReader(xml);
InputSource source = new InputSource(read);
SAXBuilder sb = new SAXBuilder();
org.jdom.Document doc;
doc = (org.jdom.Document) sb.build(source);
org.jdom.Element root = doc.getRootElement();
List<org.jdom.Element> list = root.getChildren();
if (list != null && list.size() > 0) {
for (org.jdom.Element element : list) {
if("pub_key".equals(element.getName())){
publicKey=element.getText();
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return publicKey;
}
}
/**
* 发送公钥的http请求以及企业支付到银行卡的http请求
*
* */
public class HttpClientCustomSSL {
/**
* httpClient 请求获取公钥
* @param parms
* @return
* @throws Exception
*/
public static String httpClientResultGetPublicKey(String xml) throws Exception{
StringBuffer reultBuffer = new StringBuffer();
SSLConnectionSocketFactory sslsf = ReadSSl.getInstance().readCustomSSL();
HttpPost httpPost = new HttpPost("https://fraud.mch.weixin.qq.com/risk/getpublickey");
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
StringEntity myEntity = new org.apache.http.entity.StringEntity(xml,WChatInfo.CHARSET);
myEntity.setContentType("text/xml;charset=UTF-8");
myEntity.setContentEncoding(WChatInfo.CHARSET);
httpPost.setHeader("Content-Type", "text/xml; charset=UTF-8");
httpPost.setEntity(myEntity);
CloseableHttpResponse response = null;
InputStream inputStream
= null;
InputStreamReader inputStreamReader = null;
BufferedReader bufferedReader = null;
try {
response = httpclient.execute(httpPost);
HttpEntity entity = response.getEntity();
if (entity!=null){
inputStream = entity.getContent();
inputStreamReader = new InputStreamReader(inputStream,WChatInfo.CHARSET);
bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
while ((str = bufferedReader.readLine()) != null) {
reultBuffer.append(str);
}
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
httpclient.close();
response.close();
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
}
return reultBuffer.toString();
}
/**
* httpClient 请求企业支付到银行卡
* @param parms
* @return
* @throws Exception
*/
public static String httpClientResultPANK(String xml) throws Exception{
StringBuffer reultBuffer = new StringBuffer();
SSLConnectionSocketFactory sslsf = ReadSSl.getInstance().readCustomSSL();
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/mmpaysptrans/pay_bank");
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
StringEntity myEntity = new org.apache.http.entity.StringEntity(xml, WChatInfo.CHARSET);
myEntity.setContentType("text/xml;charset=UTF-8");
myEntity.setContentEncoding(WChatInfo.CHARSET);
httpPost.setHeader("Content-Type", "text/xml; charset=UTF-8");
httpPost.setEntity(myEntity);
CloseableHttpResponse response = null;
InputStream inputStream
= null;
InputStreamReader inputStreamReader = null;
BufferedReader bufferedReader = null;
try {
response = httpclient.execute(httpPost);
HttpEntity entity = response.getEntity();
if (entity!=null){
inputStream = entity.getContent();
inputStreamReader = new InputStreamReader(inputStream, WChatInfo.CHARSET);
bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
while ((str = bufferedReader.readLine()) != null) {
reultBuffer.append(str);
}
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
httpclient.close();
response.close();
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
}
return reultBuffer.toString();
}
}
/**
* 读取证书
*
*
*/
@SuppressWarnings("deprecation")
public class ReadSSl {
private static ReadSSl readSSL = null;
private ReadSSl(){
}
public static ReadSSl getInstance(){
if(readSSL == null){
readSSL = new ReadSSl();
}
return readSSL;
}
/**
* 读取 apiclient_cert.p12 证书
* @return
* @throws Exception
*/
public SSLConnectionSocketFactory readCustomSSL() throws Exception{
KeyStore keyStore = KeyStore.getInstance("PKCS12");
FileInputStream instream = new FileInputStream(new File(WChatInfo.CA_LICENSE));
try {
keyStore.load(instream, WChatInfo.MCH_ID.toCharArray());
} finally {
instream.close();
}
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, WChatInfo.MCH_ID.toCharArray()).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslcontext, new String[] { "TLSv1" }, null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
return sslsf;
}
}
public class RSAEncrypt {
private static final char[] HEX_CHAR = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
public static RSAPublicKey loadPublicKeyByStr(String publicKeyStr)
throws Exception {
try {
byte[] buffer = Base64.decode(publicKeyStr);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException e) {
throw new Exception("出错了");
} catch (InvalidKeySpecException e) {
throw new Exception("出错了");
} catch (NullPointerException e) {
throw new Exception("出错了");
}
}
/**
*
*
* @param publicKey
*
* @param plainTextData
*
* @return
* @throws Exception
*
*/
public static byte[] encrypt(RSAPublicKey publicKey, byte[] plainTextData)
throws Exception {
if (publicKey == null) {
throw new Exception("");
}
Cipher cipher = null;
try {
cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] output = cipher.doFinal(plainTextData);
return output;
} catch (NoSuchAlgorithmException e) {
throw new Exception("出错了");
} catch (NoSuchPaddingException e) {
e.printStackTrace();
return null;
} catch (InvalidKeyException e) {
throw new Exception("出错了");
} catch (IllegalBlockSizeException e) {
throw new Exception("出错了");
} catch (BadPaddingException e) {
throw new Exception("出错了");
}
}
}
4.获取的公钥需要转化后才能使用
注明:因为上述获取的公钥是PKCS#1格式的,java可以直接使用的是PKCS#8格式的公钥,所以需要将PKCS#1格式的公钥转换为PKCS#8格式的公钥
PKCS#1 转 PKCS#8
linux下的转换命令:
openssl rsa -RSAPublicKey_in -in <filename> -pubout
注意:将转换后的公钥保存起来,因为后面需要用来加密收款方银行卡号和收款方用户名
如:在.properties的文件中配置:publicKey="bhfuruynvoiudnfxnjwpqfnnfnfnjiy46488578937583892u4bbuffbf"
5.企业支付到银行卡的http请求
请求接口:https://api.mch.weixin.qq.com/mmpaysptrans/pay_bank
请求方式:post
请求参数:mch_id,partner_trade_no(商户订单号,需保持唯一(只允许数字[0~9]或字母[A~Z]和[a~z],最短8位,最长32位),如:时间戳拼上几位随机数,保证唯一即可),
nonce_str(随机字符串,不长于32位),sign(签名),enc_bank_no(收款方银行卡号,是配合公钥加密后的卡号),enc_true_name(收款方用户名,是配合公钥加密后的用户名)
bank_code(收款方开户行,就是具体银行对应的编号,具体在:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=24_4),
amount(付款金额(不含手续费的金额),单位为分),desc(付款说明,这个可传可不传)
public class TestWChatToBank {
/**
*测试企业支付到银行卡功能
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
String source ="李四";//银行卡开户名
String pank = "6220002340018125678";//银行卡号
//注意 这里的 publicKeyPKCS8 是上一步获取微信支付公钥后经openssl 转化成PKCS8格式的公钥
String publicKeyPKCS8 = "";//转换为PKCS8格式后的公钥
//获得RAS加密后的收款方用户名
String enc_true_name =GetRSA.getRSA(source,publicKeyPKCS8);//将收款方用户名配合公钥加密
//获得RAS加密后的收款方银行卡号
String enc_bank_no = GetRSA.getRSA(pank,publicKeyPKCS8);//将收款方银行卡号配合公钥加密
String bank_code = "1003";//开户行对应的编号
String amount = "100";//付款金额,这里是100分
String desc ="测试";//付款说明
String partner_trade_no = "2017112433233331";//商户订单号
String nonce_str1 = StringUtils1.getStrRandom(28);//获得随机字符串
//获取签名
SortedMap<Object,Object> parameters1 = new TreeMap<Object,Object>();
parameters1.put("mch_id", WChatInfo.MCH_ID);
parameters1.put("partner_trade_no", partner_trade_no);
parameters1.put("nonce_str", nonce_str1);
parameters1.put("enc_bank_no", enc_bank_no);
parameters1.put("enc_true_name", enc_true_name);
parameters1.put("bank_code", bank_code);
parameters1.put("amount", amount);
parameters1.put("desc", desc);
String sign1 = SignUtils.creatSign(WChatInfo.CHARSET, parameters1);
//请求企业付款
TreeMap<String, String> tmap1 = new TreeMap<String, String>();
tmap1.put("mch_id", WChatInfo.MCH_ID);
tmap1.put("partner_trade_no", partner_trade_no);
tmap1.put("nonce_str", nonce_str1);
tmap1.put("enc_bank_no", enc_bank_no);
tmap1.put("enc_true_name", enc_true_name);
tmap1.put("bank_code", bank_code);
tmap1.put("amount", amount);
tmap1.put("desc", desc);
tmap1.put("sign", sign1);
String xml2 = XMLUtils.getRequestXml(tmap1);//将请求参数转换为请求报文
String xml3= HttpClientCustomSSL.httpClientResultPANK(xml2);//发送请求
System.out.println(xml3);
}
}
/**
* rsa加密
*
* */
public class GetRSA {
/**
* @param publicKeyPKCS8 为pkcs8格式的公钥
* */
public static String getRSA(String str,String publicKeyPKCS8) throws Exception {
byte[] cipherData=RSAEncrypt.encrypt(RSAEncrypt.loadPublicKeyByStr(publicKeyPKCS8),str.getBytes());
String cipher=Base64.encode(cipherData);
return cipher;
}
}
public class RSAEncrypt {
private static final char[] HEX_CHAR = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
public static RSAPublicKey loadPublicKeyByStr(String publicKeyStr)
throws Exception {
try {
byte[] buffer = Base64.decode(publicKeyStr);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException e) {
throw new Exception("出错了");
} catch (InvalidKeySpecException e) {
throw new Exception("出错了");
} catch (NullPointerException e) {
throw new Exception("出错了");
}
}
/**
*
*
* @param publicKey
*
* @param plainTextData
*
* @return
* @throws Exception
*
*/
public static byte[] encrypt(RSAPublicKey publicKey, byte[] plainTextData)
throws Exception {
if (publicKey == null) {
throw new Exception("");
}
Cipher cipher = null;
try {
cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] output = cipher.doFinal(plainTextData);
return output;
} catch (NoSuchAlgorithmException e) {
throw new Exception("出错了");
} catch (NoSuchPaddingException e) {
e.printStackTrace();
return null;
} catch (InvalidKeyException e) {
throw new Exception("出错了");
} catch (IllegalBlockSizeException e) {
throw new Exception("出错了");
} catch (BadPaddingException e) {
throw new Exception("出错了");
}
}
}
public final class Base64 {
static private final int BASELENGTH = 128;
static private final int LOOKUPLENGTH = 64;
static private final int TWENTYFOURBITGROUP = 24;
static private final int EIGHTBIT = 8;
static private final int SIXTEENBIT = 16;
static private final int FOURBYTE = 4;
static private final int SIGN = -128;
static private final char PAD = '=';
static private final boolean fDebug = false;
static final private byte[] base64Alphabet = new byte[BASELENGTH];
static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH];
static {
for (int i = 0; i < BASELENGTH; ++i) {
base64Alphabet[i] = -1;
}
for (int i = 'Z'; i >= 'A'; i--) {
base64Alphabet[i] = (byte) (i - 'A');
}
for (int i = 'z'; i >= 'a'; i--) {
base64Alphabet[i] = (byte) (i - 'a' + 26);
}
for (int i = '9'; i >= '0'; i--) {
base64Alphabet[i] = (byte) (i - '0' + 52);
}
base64Alphabet['+'] = 62;
base64Alphabet['/'] = 63;
for (int i = 0; i <= 25; i++) {
lookUpBase64Alphabet[i] = (char) ('A' + i);
}
for (int i = 26, j = 0; i <= 51; i++, j++) {
lookUpBase64Alphabet[i] = (char) ('a' + j);
}
for (int i = 52, j = 0; i <= 61; i++, j++) {
lookUpBase64Alphabet[i] = (char) ('0' + j);
}
lookUpBase64Alphabet[62] = (char) '+';
lookUpBase64Alphabet[63] = (char) '/';
}
private static boolean isWhiteSpace(char octect) {
return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
}
private static boolean isPad(char octect) {
return (octect == PAD);
}
private static boolean isData(char octect) {
return (octect < BASELENGTH && base64Alphabet[octect] != -1);
}
/**
* Encodes hex octects into Base64
*
* @param binaryData Array containing binaryData
* @return Encoded Base64 array
*/
public static String encode(byte[] binaryData) {
if (binaryData == null) {
return null;
}
int lengthDataBits = binaryData.length * EIGHTBIT;
if (lengthDataBits == 0) {
return "";
}
int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets;
char encodedData[] = null;
encodedData = new char[numberQuartet * 4];
byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
int encodedIndex = 0;
int dataIndex = 0;
if (fDebug) {
System.out.println("number of triplets = " + numberTriplets);
}
for (int i = 0; i < numberTriplets; i++) {
b1 = binaryData[dataIndex++];
b2 = binaryData[dataIndex++];
b3 = binaryData[dataIndex++];
if (fDebug) {
System.out.println("b1= " + b1 + ", b2= " + b2 + ", b3= " + b3);
}
l = (byte) (b2 & 0x0f);
k = (byte) (b1 & 0x03);
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
if (fDebug) {
System.out.println("val2 = " + val2);
System.out.println("k4 = " + (k << 4));
System.out.println("vak = " + (val2 | (k << 4)));
}
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];
encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
}
// form integral number of 6-bit groups
if (fewerThan24bits == EIGHTBIT) {
b1 = binaryData[dataIndex];
k = (byte) (b1 & 0x03);
if (fDebug) {
System.out.println("b1=" + b1);
System.out.println("b1<<2 = " + (b1 >> 2));
}
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];
encodedData[encodedIndex++] = PAD;
encodedData[encodedIndex++] = PAD;
} else if (fewerThan24bits == SIXTEENBIT) {
b1 = binaryData[dataIndex];
b2 = binaryData[dataIndex + 1];
l = (byte) (b2 & 0x0f);
k = (byte) (b1 & 0x03);
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];
encodedData[encodedIndex++] = PAD;
}
return new String(encodedData);
}
/**
* Decodes Base64 data into octects
*
* @param encoded string containing Base64 data
* @return Array containind decoded data.
*/
public static byte[] decode(String encoded) {
if (encoded == null) {
return null;
}
char[] base64Data = encoded.toCharArray();
// remove white spaces
int len = removeWhiteSpace(base64Data);
if (len % FOURBYTE != 0) {
return null;//should be divisible by four
}
int numberQuadruple = (len / FOURBYTE);
if (numberQuadruple == 0) {
return new byte[0];
}
byte decodedData[] = null;
byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
int i = 0;
int encodedIndex = 0;
int dataIndex = 0;
decodedData = new byte[(numberQuadruple) * 3];
for (; i < numberQuadruple - 1; i++) {
if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))
|| !isData((d3 = base64Data[dataIndex++]))
|| !isData((d4 = base64Data[dataIndex++]))) {
return null;
}//if found "no data" just return null
b1 = base64Alphabet[d1];
b2 = base64Alphabet[d2];
b3 = base64Alphabet[d3];
b4 = base64Alphabet[d4];
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
}
if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) {
return null;//if found "no data" just return null
}
b1 = base64Alphabet[d1];
b2 = base64Alphabet[d2];
d3 = base64Data[dataIndex++];
d4 = base64Data[dataIndex++];
if (!isData((d3)) || !isData((d4))) {//Check if they are PAD characters
if (isPad(d3) && isPad(d4)) {
if ((b2 & 0xf) != 0)//last 4 bits should be zero
{
return null;
}
byte[] tmp = new byte[i * 3 + 1];
System.arraycopy(decodedData, 0, tmp, 0, i * 3);
tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
return tmp;
} else if (!isPad(d3) && isPad(d4)) {
b3 = base64Alphabet[d3];
if ((b3 & 0x3) != 0)//last 2 bits should be zero
{
return null;
}
byte[] tmp = new byte[i * 3 + 2];
System.arraycopy(decodedData, 0, tmp, 0, i * 3);
tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
return tmp;
} else {
return null;
}
} else { //No PAD e.g 3cQl
b3 = base64Alphabet[d3];
b4 = base64Alphabet[d4];
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
}
return decodedData;
}
/**
* remove WhiteSpace from MIME containing encoded Base64 data.
*
* @param data the byte array of base64 data (with WS)
* @return the new length
*/
private static int removeWhiteSpace(char[] data) {
if (data == null) {
return 0;
}
// count characters that's not whitespace
int newSize = 0;
int len = data.length;
for (int i = 0; i < len; i++) {
if (!isWhiteSpace(data[i])) {
data[newSize++] = data[i];
}
}
return newSize;
}
}