PIN加密有3個要素,PIN-KEY,PIN,PAN。
PIN-KEY也就是加密的密鑰,這裏採用的3DES加密,所以PIN-KEY長度要麼是16個字節要麼是24個字節,也即是要麼採用128位長密鑰要麼採用192長字節;PIN也就是要加密的明文,這裏可能是賬戶密碼,也可能是登錄密碼的明文;使用PIN加密,在加密之前,首先使用一串數字和PIN異或,然後將異或後的數據拿來進行3DES加密,這樣加密出來的數據就是PIN加密結果。通常每次加密時採用的PAN不一樣。當時解密的時候也需要取得該PAN進行解密。使用策略模式以方便以後擴展其他加密算法。對接口不滿足的可適當通過Adapter調整
實現示例:
/**
*@(#)EncryRule.java1.02007-10-23
*/
package com.security;
/**
* 數據加密Strategy
*
* @author tsimgsong
* @version 1.0,2007-10-15
*
*/
public abstract class EncryRule {
/**
* 明文
*/
protected String plainText = "";
/**
* 密文
*/
protected String encryedText = "";
/**
* 取密文
* @return String 密文
*/
public String getEncryedText(){
return encryedText;
}
/**
* 設置密文
* @param encryedText 密文
*/
public void setEncryedText(String encryedText) {
this.encryedText = encryedText;
}
/**
* 取明文
* @return String 明文
*/
public String getPlainText(){
return plainText;
}
/**
* 設置明文
* @param plainText 明文
*/
public void setPlainText(String plainText){
this.plainText = plainText;
}
/**
* 加密操作
*
*/
public abstract void encry();
/**
* 解密操作
*
*/
public abstract void deEncry();
}
/**
*@(#)PinEncry.java1.02007-10-23
*/
package com.security;
import java.lang.reflect.Array;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
/**
* PIN碼加密,加密報文通信中需要傳輸的客戶密碼
* <br>
*
* @author tsimgsong
* @version 1.0,2007-10-15
*
*/
public class PinEncry extends EncryRule {
private static boolean initialized = false;
// 密鑰24個字節,192位
private static String key_pin ;
// pin和pan的長度,值域長度爲30,前面2位長度表示值域的長度
private static final int LENGTH_PIN = 32;
private static final int LENGTH_PAN = 32;
private static final int LENGTH_PWD = 0;
// 加密3DES變量
private static Cipher cipher = null;
// 解密3DES變量
private static Cipher deCipher = null;
// 加密算法
private static final String ALGORITHM = "DESede";
private static SecretKey key = null;
/**
* "00"+網銀流水號+交易日期+客戶號+操作員編號
* <br> 前面兩位爲數據長度,設爲定值00,數據30位長,不足左補0,超過30位保留後30個字節
*/
private String pan = "";
static {
if (!initialized) {
pinEncryInit();
initialized = true;
}
}
/**
*初始化加密要素,從數據庫中提取PIN密碼
*
*/
private static void pinEncryInit() {
System.out.println("Pin Encry Parameters Initialize complete:success");
}
/**
* 重置加密密鑰,從下次使用該功能時生效
*
*/
private static void reset() {
initialized = false;
System.out.println("Reset Pin Encry Parameters");
pinEncryInit();
}
/**
* PIN碼加密
* <br>對明文進行3DES加密,沒8個字節處理一次,對第一個8字節進行3DES加密,後面每8個字節都和前面的加密結果進行異或後3DES加密</br>
*
*/
public void encry() {
encryedText = plainText;
// PIN nor PAN
byte[] toBeEncry = null;
toBeEncry = norProcess(plainText, pan);
// System.out.println("Before 3DES:"+encryedText +"Base64:"+new sun.misc.BASE64Encoder().encode(encryedText.getBytes()));
try {
// 明文
// 密文 存放計算後的數據
byte[] encryedData = new byte[toBeEncry.length];
// 本次需要進行處理的數據 一次最多處理8個字節
byte[] encrying = new byte[8];
// 3DeS需要以字節數組的方式進行加密
} catch (Exception e) {
e.printStackTrace();
}
}
public void deEncry(){
byte[] deEncryed = null;
try {
byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(encryedText);
byte[] mDec = new byte[8];
deEncryed = new byte[dec.length];
System.arraycopy(dec,0,deEncryed,0,dec.length);
}
encryedText = new sun.misc.BASE64Encoder().encode(deEncryed);
// System.out.println("After De3DES :"+encryedText);
} catch (Exception e) {
e.printStackTrace();
}
plainText = denorProcess(deEncryed,pan);
}
/**
* 數據異或處理<br>
*
* @param pin 密碼長度(2Byte)+客戶密碼<br> 密鑰最長不超過30位,不足部分右補0
* @param pan "00"+網銀流水號+交易日期+客戶號+操作員編號
* <br> 前面兩位爲數據長度,設爲定值00,數據30位長,不足左補0,超過30位保留後30個字節
* @return PIN String Block
*/
private byte[] norProcess(String pin,String pan){
// 異或結果存放
int[] norNum=new int[LENGTH_PIN];
byte[] norRes = new byte[LENGTH_PIN];
byte bb;
String temp = "";
String result="";
// System.out.println("PIN:"+pin);
// System.out.println("PAN:"+pan);
// 密碼不足30位右補0
for(int i = pin.length() ;i <LENGTH_PIN ;i++){
pin = pin+"0";
}
// pan不足30位左補0
for(int i = pan.length() ;i<LENGTH_PAN ;i++){
pan = "0"+pan;
}
// pan超過30位去後30位
if(pan.length() > LENGTH_PAN){
pan = pan.substring(pan.length()-LENGTH_PAN,pan.length());
}
for(int i=0;i<LENGTH_PIN;i++){
norRes[i] =(byte) (pin.charAt(i)^pan.charAt(i));
}
// System.out.print("Nor Result:");
// for(int i = 0 ;i<norRes.length ;i++){
// System.out.print(Integer.toHexString(norRes[i])+" ");
// }
// System.out.println();
return norRes;//new String(norRes);
}
/**
* 異或結果還原處理
*
* @param norStr 異或值
* @param pan "00"+交易日期+交易時間+客戶號+操作員編號
* <br> 前面兩位爲數據長度,設爲定值00,數據30位長,不足左補0,超過30位保留後30個字節
* @return String 異或原串
*/
private String denorProcess(byte[] norStr,String pan){
byte[] snNum=norStr;
String result="";
// pan不足30位左補0
for(int i = pan.length() ;i<LENGTH_PAN ;i++){
pan = "0"+pan;
}
// pan超過30位去後30位
if (pan.length() > LENGTH_PAN) {
pan = pan.substring(pan.length() - LENGTH_PAN, pan.length());
}
int n = 0;
for(int i=0;i<norStr.length;i++){
snNum[i]=(byte)(norStr[i]^pan.charAt(i));
}
for(int k=0;k< norStr.length;k++){
result+=(char)snNum[k];
}
// System.out.println("DeNcry result:"+result);
return result;
}
/**
* 取Pan值
* @return String
*/
public String getPan() {
return pan;
}
/**
* 設置Pan值
* @param pan
*/
public void setPan(String pan) {
this.pan = pan;
}
}
/**
*@(#)PinEncry.java1.02007-10-23
*/
Package com.security;
/**
* 數據加密策略
*
* @author tsimgsong
* @version 1.0,2007-10-15
*
*/
public class EncrySolve {
// 加密策略
static EncryRule rule ;
/**
* 加密運算
* @param r 加密策略
* @param plainText 明文
* @return String 加密後數據
*/
public static String encry(EncryRule r,String plainText){
rule = r;
rule.setPlainText(plainText);
rule.encry();
return rule.getEncryedText();
}
/**
* 解密運算
* @param r 解密策略
* @param encryedText 密文
* @return String 明文
*/
public static String deEncry(EncryRule r,String encryedText){
rule = r;
rule.setEncryedText(encryedText);
rule.deEncry();
return rule.getPlainText();
}
/**
* 取密文
* @return
*/
public String getEncryedText(){
return rule.getEncryedText();
}
/**
* 取明文
* @return
*/
public String getPlainText(){
return rule.getPlainText();
}
}
Junit測試:
PinEncry pe = new PinEncry();
String passwd = "000001";
int length_passwd = passwd.length() ;
if(length_passwd<10){
passwd = "0"+length_passwd+passwd;
}else{
passwd = ""+length_passwd+passwd;
}
PinEncry pe = new PinEncry();
// 組織Pan值 交易日期YYYYMMDD+企業客戶號+操作員編號
String pan = "00";
pan = "383090020071022799070001";
pe.setPan(pan);
加密:
passwd = (EncrySolve.encry(pe,passwd));
assertEquals(‘X065VL+nZV6/jP8XjJq6FmMH18egeDW852gA2RzTF0Q=’,passwd);
解密:
passwd=EncrySolve.deEncry(pe,"X065VL+nZV6/jP8XjJq6FmMH18egeDW852gA2RzTF0Q=");
loger.debug(passwd);
密碼進行加密之前必須計算出密碼原文的長度,並以兩個字節的長度放置在明文的前面,後面根據密碼的長度進行截取。加密後的數據進行了Base64編碼。
3DES算法就是進行了3次DES計算,用第一個密鑰進行一次加密,用第二個密鑰進行一次解密,之後再用第3個密鑰進行一次加密,DES加密的密鑰長度是64位,如果採用192位密碼加密,那個三個密鑰分別取依次的64位,如果採用128位密鑰,第一個和第三個的密鑰相同