銀行系統的密鑰有三種,主密鑰/pinkey/Mackey,其中pinkey是用來加密密碼的,而mackey是用來校驗報文是否有錯碼,主密鑰是用來加密pinkey和mackey的.
其中主密鑰加密pinkey和mackey是是用3des來加解密的
代碼如下:
/* param: pKey:密鑰(十六進制) strPlainAKey:需要加解密的字符串(十六進制) ciperAKey:返回值 iflag:1解密 0加密 */ void getCiper(char* pKey, char* strPlainAKey, char* ciperAKey, int iflag) { unsigned char binPlainAKey[64] = {0}; hex2binary(binPlainAKey, strPlainAKey, strlen(strPlainAKey)); ASCIIStr2BinCharStrBy3DES(pKey,(unsigned char*)binPlainAKey,strlen(strPlainAKey)/2, (unsigned char*)ciperAKey, iflag); } /* param: pKey:密鑰(十六進制) inBinary:加解密字符串的字節形式 inLen:加解密字符串的長度 binCharString:返回值 iflag:1解密 0加密 */ void ASCIIStr2BinCharStrBy3DES(char* pKey, unsigned char* inBinary, int inLen, unsigned char* binCharString,int iflag) { unsigned char targetIdBin[DESBINARY_LEN] = {0}; //TargetIdLen=8 // 3DES encription unsigned char key[LEN_OF_KEY]; unsigned char block_key[9]; memset(key, 0, LEN_OF_KEY); hex2binary(key, pKey, strlen(pKey)); DES_key_schedule ks,ks2,ks3; memset(block_key, 0, sizeof(block_key)); memcpy(block_key, key + 0, 8); DES_set_key_unchecked((const_DES_cblock*)block_key, &ks); memcpy(block_key, key + 8, 8); DES_set_key_unchecked((const_DES_cblock*)block_key, &ks2); memcpy(block_key, key + 0, 8); DES_set_key_unchecked((const_DES_cblock*)block_key, &ks3); unsigned char input[8]; memset(input, 0, sizeof(input)); unsigned char encryptedOutBinary[DESBINARY_LEN]; memset(encryptedOutBinary, 0, sizeof(encryptedOutBinary)); for(int i=0; i<inLen/8; i++) { memset(targetIdBin, 0, sizeof(targetIdBin)); memcpy((void*)targetIdBin, (const void*)(inBinary+i*8), 8); DES_ecb3_encrypt((const_DES_cblock*)targetIdBin, (DES_cblock*)encryptedOutBinary, &ks, &ks2, &ks3, iflag); binary2char((char*)binCharString+i*16, encryptedOutBinary, DESBINARY_LEN); } } // // A public function: convert binary string to character string, the character string's length = 2 // binary string. // @param charArray: character array. output. // @param binArray: binary array. input. // @param binLen: length of binary array. // void binary2char(char* charArray, const unsigned char* binArray, int binLen) { int i; for(i = 0; i < binLen; i++) { sprintf(charArray + 2*i, "%02X", binArray[i]); } charArray[2*i] = '\0'; } // // A public function: convert hex string to binary string, the hex string's length = 2 * binary string. // @param binArray: binary array. output. // @param hexCharArray: character array contains hex string. input. // @param hexLen: length of hex string array. input. // void hex2binary(unsigned char* binArray, const char* hexCharArray, int hexLen) { if (hexLen%2 != 0) { printf("hex2binary(): length of input parameter hexCharArray should be even number!\n"); return; } int i, j; //convert two hex chars to one byte char atom[2 + 1] = "\0"; for (i = 0, j = 0; i < hexLen/2; i++, j+=2) { strncpy(atom, hexCharArray + j, sizeof(atom) - 1); atom[sizeof(atom) - 1] = '\0'; binArray[i] = (unsigned char)strtol(atom, NULL, 16); } }
一般來說,ATM拿到pinkey密文會使用主密鑰解密,得到pinkey明文,然後是用帳號+密碼生成pinblock明文,然後使用pinkey使用3des加密,然後傳給後端
生成pinblock明文的算法為:
char uniteBytes(char a,char b) { char c = (int(a-'0')<<4)+b-'0'; return c; } /** * getHPin * 對密碼進行轉換 * PIN格式 * BYTE 1 PIN的長度 * BYTE 2 – BYTE 3/4/5/6/7 4--12個PIN(每個PIN占4個BIT) * BYTE 4/5/6/7/8 – BYTE 8 FILLER “F” (每個“F“占4個BIT) * @param pin String * @return byte[] */ void getHPin(char* pin, char* encode) { encode[0] = 6; encode[1] = uniteBytes(pin[0], pin[1]); encode[2] = uniteBytes(pin[2], pin[3]); encode[3] = uniteBytes(pin[4], pin[5]); encode[4] = 255; encode[5] = 255; encode[6] = 255; encode[7] = 255; } /** * getHAccno * 對帳號進行轉換 * BYTE 1 — BYTE 2 0X0000 * BYTE 3 — BYTE 8 12個主帳號 * 取主帳號的右12位(不包括最右邊的校驗位),不足12位左補“0X00”。 * @param accno String * @return byte[] */ char* getHAccno(char* accno,char* encode) { int len = strlen(accno); int beginPos = len < 13 ? 0 : len - 13; char arrTemp[13] = {0}; memcpy(arrTemp, accno+beginPos, len-beginPos-1); char arrAccno[12]; for(int i=0; i<12; i++) { arrAccno[i] = (i <= strlen(arrTemp) ? arrTemp[i] : 0); } encode[0] = 0; encode[1] = 0; encode[2] = uniteBytes(arrAccno[0], arrAccno[1]); encode[3] = uniteBytes(arrAccno[2], arrAccno[3]); encode[4] = uniteBytes(arrAccno[4], arrAccno[5]); encode[5] = uniteBytes(arrAccno[6], arrAccno[7]); encode[6] = uniteBytes(arrAccno[8], arrAccno[9]); encode[7] = uniteBytes(arrAccno[10], arrAccno[11]); return encode; } /** * getPinBlock * 標准ANSI X9.8 Format(帶主帳號信息)的PIN BLOCK計算 * PIN BLOCK 格式等於 PIN 按位異或 主帳號; * @param pin String * @param accno String * @return byte[] */ void process(char* pin, char* accno,char* pHexRet) { char arrAccno[128]={0}; getHAccno(accno,arrAccno); char arrPin[128]={0}; getHPin(pin, arrPin); unsigned char arrRet[8]={0}; for(int i=0; i<8; i++){ arrRet[i] = (unsigned char)(arrPin[i] ^ arrAccno[i]); } binary2char(pHexRet, arrRet, 8); }
Mac運算:
ATM拿到mackey,通過主密鑰解密得到密鑰明文,使用一串雙方協議好的macdata格式,通過ANSI 9.19得到macblock,隨報文發到後端,後端也會做同樣的操作,然後比對,得到校驗結果
Ansi 9.19的算法如下
void xor(unsigned char *input1,unsigned char *input2,unsigned char *output,int len) { while (len) { *output++=*input1++^*input2++; len--; } } /* *@brief: 根據輸入數據計算MAC,初始IV向量默認為"x00x00x00x00x00x00x00x00" *@param: sMacKey 密鑰 *@param: pInData 輸入數據 *@param: pRetData 計算出來的MAC *@調用自定義xor和des函數 */ void MacArithmetic(char *sMacKey,char *inBinary,char *pRetData) { //MAC算法: //將字符串pInata分為8字節為單位的數據塊,不足補x00,分別標號為D1,D2,D3,...,Dn //設置初始向量E0="x00x00x00x00x00x00x00x00" //將E0^D1 —---->E1(E0,D1異或的後結果經使用密鑰左8位des加密得到E1) //將E1^D2 ----->E2 //如此類推,知道得出En結束, //使用密鑰右8位des解密En得到En0 //使用密鑰左8位加密En0得到En1 //En1即是計算出來的MAC unsigned char sUpData[512]; unsigned char sData[20]; unsigned char sXorData[20]; unsigned char sDesData[20]; int i,n,iNum,iInLen; unsigned char key[16]; unsigned char block_key[9]; iInLen=strlen(inBinary); memset(key, 0, sizeof(key)); hex2binary(key, sMacKey, strlen(sMacKey)); DES_key_schedule ks,ks2,ks3; memset(block_key, 0, sizeof(block_key)); memcpy(block_key, key + 0, 8); DES_set_key_unchecked((const_DES_cblock*)block_key, &ks); memcpy(block_key, key + 8, 8); DES_set_key_unchecked((const_DES_cblock*)block_key, &ks2); memcpy(block_key, key + 0, 8); DES_set_key_unchecked((const_DES_cblock*)block_key, &ks3); memset(sUpData,0,sizeof(sUpData)); memset(sData,0,sizeof(sData)); memset(sXorData,0,sizeof(sXorData)); memset(sDesData,0,sizeof(sDesData)); //補全要加密數據成8倍數到sUpData,不足補x00 memcpy(sUpData,inBinary,iInLen); iNum = iInLen%8; if (iNum == 0) n=iInLen/8; else { n=iInLen/8+1; memcpy(sUpData+iInLen,"\0\0\0\0\0\0\0\0",8-iNum); } printf("n=%dnsUpData=[%s]n",n,sUpData); memset(sDesData,0,sizeof(sDesData)); //初始向量賦給sDesData for (i=0;i<n;i++) { memcpy(sData,sUpData+i*8,8); xor(sDesData,sData,sXorData,8); //異或 DES_ecb_encrypt((const_DES_cblock*)sXorData, (DES_cblock*)sDesData, &ks,1); } DES_ecb_encrypt((const_DES_cblock*)sDesData, (DES_cblock*)sXorData, &ks2,0); DES_ecb_encrypt((const_DES_cblock*)sXorData, (DES_cblock*)sDesData, &ks,1); binary2char(pRetData, sDesData, 8); return ; }