PHP、JAVA 中的 3DES 加密结果和 .NET 产生与 3DES iOS 不同的结果
3DES encrypt result in PHP, JAVA and .NET produces different result from 3DES iOS
我正在尝试在iOS 8 中升级我当前的应用程序,以适应Redsys / Sermpa 的新加密SHA256。
但是我在数据加密方面遇到了问题。在 PHP、Java 和 .NET 中,我得到的结果与 iOS.
完全不同
我想问题一定出在3DES IOS CCCrypt.
PHP、JAVA 和 .NET 代码是一个库,我无法更改该库。
我必须使 iOS 中的加密结果与 PHP、JAVA 和 .NET 中正确的加密结果相同。
图书馆Java代码:
String secretCodeString = "Mk9m98IfEblmPfrpsawt7BmxObt98Jev";
String Ds_Merchant_Order = "1442772645";
String Ds_MerchantParameters = "eyJEU19NRVJDSEFOVF9BTU9VTlQiOiIxNDUiLCJEU19NRVJDSEFOVF9PUkRFUiI6IjE0NDI3NzI2NDUiLCJEU19NRVJDSEFOVF9NRVJDSEFOVENPREUiOiI5OTkwMDg4ODEiLCJEU19NRVJDSEFOVF9DVVJSRU5DWSI6Ijk3OCIsIkRTX01FUkNIQU5UX1RSQU5TQUNUSU9OVFlQRSI6IjAiLCJEU19NRVJDSEFOVF9URVJNSU5BTCI6Ijg3MSIsIkRTX01FUkNIQU5UX01FUkNIQU5UVVJMIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX05vdGlmLnBocCIsIkRTX01FUkNIQU5UX1VSTE9LIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX09LX0tPLnBocCIsIkRTX01FUkNIQU5UX1VSTEtPIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX09LX0tPLnBocCJ9";
byte [] secretCode = decodeB64(secretCodeString.getBytes("UTF-8"));
String secretKc = toHexadecimal(secretCode, secretCode.length);
byte [] Ds_Merchant_Order_encrypt3DES = encrypt_3DES(secretKc, Ds_Merchant_Order);
byte [] hash = mac256(Ds_MerchantParameters, Ds_Merchant_Order_encrypt3DES);
byte [] res = encodeB64UrlSafe(hash);
String Ds_Signature = new String(res, "UTF-8");
//Ds_Signature: hueCwD/cbvrCi+9IDY86WteMpXulIl0IDNXNlYgcZHM=
public byte [] encrypt_3DES(final String claveHex, final String datos) {
byte [] ciphertext = null;
try {
DESedeKeySpec desKeySpec = new DESedeKeySpec(toByteArray(claveHex));
SecretKey desKey = new SecretKeySpec(desKeySpec.getKey(), "DESede");
Cipher desCipher = Cipher.getInstance("DESede/CBC/NoPadding");
byte [] IV = {0, 0, 0, 0, 0, 0, 0, 0};
desCipher.init(Cipher.ENCRYPT_MODE, desKey, new IvParameterSpec(IV));
int numeroCerosNecesarios = 8 - (datos.length() % 8);
if (numeroCerosNecesarios == 8) {
numeroCerosNecesarios = 0;
}
ByteArrayOutputStream array = new ByteArrayOutputStream();
array.write(datos.getBytes("UTF-8"), 0, datos.length());
for (int i = 0; i < numeroCerosNecesarios; i++) {
array.write(0);
}
byte [] cleartext = array.toByteArray();
ciphertext = desCipher.doFinal(cleartext);
} catch (Exception e) {
e.printStackTrace(System.err);
}
return ciphertext;
}
图书馆PHP代码:
$Ds_Merchant_Order = "1442772645";
$Ds_MerchantParameters = "eyJEU19NRVJDSEFOVF9BTU9VTlQiOiIxNDUiLCJEU19NRVJDSEFOVF9PUkRFUiI6IjE0NDI3NzI2NDUiLCJEU19NRVJDSEFOVF9NRVJDSEFOVENPREUiOiI5OTkwMDg4ODEiLCJEU19NRVJDSEFOVF9DVVJSRU5DWSI6Ijk3OCIsIkRTX01FUkNIQU5UX1RSQU5TQUNUSU9OVFlQRSI6IjAiLCJEU19NRVJDSEFOVF9URVJNSU5BTCI6Ijg3MSIsIkRTX01FUkNIQU5UX01FUkNIQU5UVVJMIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX05vdGlmLnBocCIsIkRTX01FUkNIQU5UX1VSTE9LIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX09LX0tPLnBocCIsIkRTX01FUkNIQU5UX1VSTEtPIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX09LX0tPLnBocCJ9";
$secretCode = "Mk9m98IfEblmPfrpsawt7BmxObt98Jev";
$secretCode = base64_decode($secretCode);
$bytes = array(0,0,0,0,0,0,0,0);
$iv = implode(array_map("chr", $bytes)); //PHP 4 >= 4.0.2
$Ds_Merchant_Order_encrypt3DES = mcrypt_encrypt(MCRYPT_3DES, $secretCode, $Ds_Merchant_Order, MCRYPT_MODE_CBC, $iv);
$hash = hash_hmac('sha256', $Ds_MerchantParameters, $Ds_Merchant_Order_encrypt3DES, true);
$Ds_Signature = $this->encodeBase64($hash);
//Ds_Signature: hueCwD/cbvrCi+9IDY86WteMpXulIl0IDNXNlYgcZHM=
库 .NET 代码:
byte[] secretCode = Base64Decode("Mk9m98IfEblmPfrpsawt7BmxObt98Jev");
string Ds_MerchantParameters = "eyJEU19NRVJDSEFOVF9BTU9VTlQiOiIxNDUiLCJEU19NRVJDSEFOVF9PUkRFUiI6IjE0NDI3NzI2NDUiLCJEU19NRVJDSEFOVF9NRVJDSEFOVENPREUiOiI5OTkwMDg4ODEiLCJEU19NRVJDSEFOVF9DVVJSRU5DWSI6Ijk3OCIsIkRTX01FUkNIQU5UX1RSQU5TQUNUSU9OVFlQRSI6IjAiLCJEU19NRVJDSEFOVF9URVJNSU5BTCI6Ijg3MSIsIkRTX01FUkNIQU5UX01FUkNIQU5UVVJMIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX05vdGlmLnBocCIsIkRTX01FUkNIQU5UX1VSTE9LIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX09LX0tPLnBocCIsIkRTX01FUkNIQU5UX1VSTEtPIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX09LX0tPLnBocCJ9"
string Ds_Merchant_Order = "1442772645";
// Calculate derivated key by encrypting with 3DES the "DS_MERCHANT_ORDER" with decoded key
byte[] Ds_Merchant_Order_encrypt3DES = cryp.Encrypt3DES(Ds_Merchant_Order, secretCode);
// Calculate HMAC SHA256 with Encoded base64 JSON string using derivated key calculated previously
byte[] hash = cryp.GetHMACSHA256(Ds_MerchantParameters, Ds_Merchant_Order_encrypt3DES);
// Encode byte[] res to Base64 String
string Ds_Signature = Base64Encode2(hash);
//Ds_Signature: hueCwD/cbvrCi+9IDY86WteMpXulIl0IDNXNlYgcZHM=
public byte[] Encrypt3DES(string plainText, byte[] key) {
byte[] toEncryptArray = Encoding.UTF8.GetBytes(plainText);
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
try {
/// SALT used in 3DES encryptation process.
byte[] SALT = new byte[8] {0,0,0,0,0,0,0,0};
// Block size 64 bit (8 bytes)
tdes.BlockSize = 64;
// Key Size 192 bit (24 bytes)
tdes.KeySize = 192;
tdes.Mode = CipherMode.CBC;
tdes.Padding = PaddingMode.Zeros;
tdes.IV = SALT;
tdes.Key = key;
var cTransform = tdes.CreateEncryptor();
//transform the specified region of bytes array to resultArray
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
//Release resources held by TripleDes Encryptor
tdes.Clear();
return resultArray;
} // Error in Cryptographic method
catch (CryptographicException ex) {
throw new CryptographicException(ex.Message);
}
}
************************------------*********** ***********------------************************
我的Objective-C代码:
NSString *Ds_Merchant_Order = @"1442772645";
NSString *Ds_MerchantParameters = @"eyJEU19NRVJDSEFOVF9BTU9VTlQiOiIxNDUiLCJEU19NRVJDSEFOVF9PUkRFUiI6IjE0NDI3NzI2NDUiLCJEU19NRVJDSEFOVF9NRVJDSEFOVENPREUiOiI5OTkwMDg4ODEiLCJEU19NRVJDSEFOVF9DVVJSRU5DWSI6Ijk3OCIsIkRTX01FUkNIQU5UX1RSQU5TQUNUSU9OVFlQRSI6IjAiLCJEU19NRVJDSEFOVF9URVJNSU5BTCI6Ijg3MSIsIkRTX01FUkNIQU5UX01FUkNIQU5UVVJMIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX05vdGlmLnBocCIsIkRTX01FUkNIQU5UX1VSTE9LIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX09LX0tPLnBocCIsIkRTX01FUkNIQU5UX1VSTEtPIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX09LX0tPLnBocCJ9";
NSString *clave = @"Mk9m98IfEblmPfrpsawt7BmxObt98Jev";
NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:clave options:0];
NSString *secretCode = [self hexadecimalString:decodedData];
NSData *Ds_Merchant_Order_encrypt3DES = [self encrypt3DES:Ds_Merchant_Order key:secretCode];
NSData *hash = [self hmac256ForKeyAndData:Ds_MerchantParameters withKey:Ds_Merchant_Order_encrypt3DES];
NSString *Ds_Signature = [hash base64EncodedStringWithOptions:0];
//Ds_Signature: kUVwanKNIlrvw3t56HUAYXSBmE/u6ruTj1r/FGOIiUg=
我的函数:
- (NSString *)hexadecimalString:(NSData*)data{
const unsigned char *dataBuffer = (const unsigned char *)[data bytes];
if (!dataBuffer){
return [NSString string];
}
NSUInteger dataLength = [data length];
NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)];
for (int i = 0; i < dataLength; ++i){
[hexString appendFormat:@"%02x", (unsigned int)dataBuffer[i]];
}
return [NSString stringWithString:hexString];
}
- (NSData*)encrypt3DES:(NSString*)data key:(NSString*)key{
NSData *plainData = [data dataUsingEncoding:NSUTF8StringEncoding];
const void *vplainText = (const void *)[plainData bytes];
size_t plainTextBufferSize = [plainData length];
size_t movedBytes = 0;
size_t bufferPtrSize = (plainTextBufferSize + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);
uint8_t * bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));
memset((void *)bufferPtr, 0x0, bufferPtrSize);
NSString *initVec = @"[=17=][=17=][=17=][=17=][=17=][=17=][=17=][=17=]";
const void *vkey = (const void *) [key UTF8String];
const void *vinitVec = (const void *) [initVec UTF8String];
CCCryptorStatus ccStatus = CCCrypt(kCCEncrypt,
kCCAlgorithm3DES,
kCCOptionPKCS7Padding | kCCOptionECBMode,
vkey,
kCCKeySize3DES,
vinitVec,
vplainText,
plainTextBufferSize,
(void *)bufferPtr,
bufferPtrSize,
&movedBytes);
if (ccStatus == kCCSuccess) NSLog(@"SUCCESS");
else if (ccStatus == kCCParamError) NSLog( @"PARAM ERROR");
else if (ccStatus == kCCBufferTooSmall) NSLog( @"BUFFER TOO SMALL");
else if (ccStatus == kCCMemoryFailure) NSLog( @"MEMORY FAILURE");
else if (ccStatus == kCCAlignmentError) NSLog( @"ALIGNMENT");
else if (ccStatus == kCCDecodeError) NSLog( @"DECODE ERROR");
else if (ccStatus == kCCUnimplemented) NSLog( @"UNIMPLEMENTED");
return [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes];
}
-(NSData *)hmac256ForKeyAndData:(NSString *)data withKey:(NSData *)keyData{
NSData *dataData=[data dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData* hash = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, keyData.bytes, keyData.length, dataData.bytes, dataData.length, hash.mutableBytes);
return hash;
}
P.S.: 密钥和密码是假的,只是为了测试。 ;)
有几个错误:
第一:PHP版本使用CBC模式,iOS版本使用ECB模式。 CCCrypt
默认为CBC模式,去掉kCCOptionECBMode
即可。使用空 iv 会使第一个块不安全,通常使用随机 iv 并将其添加到加密数据之前。
其次:mcrypt不支持PKCS#7 padding,它只支持非标准的不安全的null padding。因此有必要在加密之前将填充添加到数据中。
从这个:
添加 PKCS#7 填充 (php):
其中 $block
是以字节为单位的块大小,$str
是要加密的数据
$pad = $block - (strlen($str) % $block);
$str .= str_repeat(chr($pad), $pad);
删除 PKCS#7 填充 (php):
其中 $str
是解密后的数据
$len = strlen($str);
$pad = ord($str[$len-1]);
$str = $strsubstr($str, 0, $len - $pad);
注意:如果数据恰好是块大小的倍数,则将添加整个填充块,这是必要的。
有关填充的详细信息,请参阅 PKCS#7。
为了进一步调试,请提供所有参数和加密数据的十六进制转储:secretCode、Ds_Merchant_Order、iv 和加密输出。
最后:为了更好的安全性,请考虑使用可用于多种平台和语言的 RNCryptor。它经过严格审查,支持当前的最佳实践,目前已得到支持。
我正在尝试在iOS 8 中升级我当前的应用程序,以适应Redsys / Sermpa 的新加密SHA256。
但是我在数据加密方面遇到了问题。在 PHP、Java 和 .NET 中,我得到的结果与 iOS.
完全不同我想问题一定出在3DES IOS CCCrypt.
PHP、JAVA 和 .NET 代码是一个库,我无法更改该库。
我必须使 iOS 中的加密结果与 PHP、JAVA 和 .NET 中正确的加密结果相同。
图书馆Java代码:
String secretCodeString = "Mk9m98IfEblmPfrpsawt7BmxObt98Jev";
String Ds_Merchant_Order = "1442772645";
String Ds_MerchantParameters = "eyJEU19NRVJDSEFOVF9BTU9VTlQiOiIxNDUiLCJEU19NRVJDSEFOVF9PUkRFUiI6IjE0NDI3NzI2NDUiLCJEU19NRVJDSEFOVF9NRVJDSEFOVENPREUiOiI5OTkwMDg4ODEiLCJEU19NRVJDSEFOVF9DVVJSRU5DWSI6Ijk3OCIsIkRTX01FUkNIQU5UX1RSQU5TQUNUSU9OVFlQRSI6IjAiLCJEU19NRVJDSEFOVF9URVJNSU5BTCI6Ijg3MSIsIkRTX01FUkNIQU5UX01FUkNIQU5UVVJMIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX05vdGlmLnBocCIsIkRTX01FUkNIQU5UX1VSTE9LIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX09LX0tPLnBocCIsIkRTX01FUkNIQU5UX1VSTEtPIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX09LX0tPLnBocCJ9";
byte [] secretCode = decodeB64(secretCodeString.getBytes("UTF-8"));
String secretKc = toHexadecimal(secretCode, secretCode.length);
byte [] Ds_Merchant_Order_encrypt3DES = encrypt_3DES(secretKc, Ds_Merchant_Order);
byte [] hash = mac256(Ds_MerchantParameters, Ds_Merchant_Order_encrypt3DES);
byte [] res = encodeB64UrlSafe(hash);
String Ds_Signature = new String(res, "UTF-8");
//Ds_Signature: hueCwD/cbvrCi+9IDY86WteMpXulIl0IDNXNlYgcZHM=
public byte [] encrypt_3DES(final String claveHex, final String datos) {
byte [] ciphertext = null;
try {
DESedeKeySpec desKeySpec = new DESedeKeySpec(toByteArray(claveHex));
SecretKey desKey = new SecretKeySpec(desKeySpec.getKey(), "DESede");
Cipher desCipher = Cipher.getInstance("DESede/CBC/NoPadding");
byte [] IV = {0, 0, 0, 0, 0, 0, 0, 0};
desCipher.init(Cipher.ENCRYPT_MODE, desKey, new IvParameterSpec(IV));
int numeroCerosNecesarios = 8 - (datos.length() % 8);
if (numeroCerosNecesarios == 8) {
numeroCerosNecesarios = 0;
}
ByteArrayOutputStream array = new ByteArrayOutputStream();
array.write(datos.getBytes("UTF-8"), 0, datos.length());
for (int i = 0; i < numeroCerosNecesarios; i++) {
array.write(0);
}
byte [] cleartext = array.toByteArray();
ciphertext = desCipher.doFinal(cleartext);
} catch (Exception e) {
e.printStackTrace(System.err);
}
return ciphertext;
}
图书馆PHP代码:
$Ds_Merchant_Order = "1442772645";
$Ds_MerchantParameters = "eyJEU19NRVJDSEFOVF9BTU9VTlQiOiIxNDUiLCJEU19NRVJDSEFOVF9PUkRFUiI6IjE0NDI3NzI2NDUiLCJEU19NRVJDSEFOVF9NRVJDSEFOVENPREUiOiI5OTkwMDg4ODEiLCJEU19NRVJDSEFOVF9DVVJSRU5DWSI6Ijk3OCIsIkRTX01FUkNIQU5UX1RSQU5TQUNUSU9OVFlQRSI6IjAiLCJEU19NRVJDSEFOVF9URVJNSU5BTCI6Ijg3MSIsIkRTX01FUkNIQU5UX01FUkNIQU5UVVJMIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX05vdGlmLnBocCIsIkRTX01FUkNIQU5UX1VSTE9LIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX09LX0tPLnBocCIsIkRTX01FUkNIQU5UX1VSTEtPIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX09LX0tPLnBocCJ9";
$secretCode = "Mk9m98IfEblmPfrpsawt7BmxObt98Jev";
$secretCode = base64_decode($secretCode);
$bytes = array(0,0,0,0,0,0,0,0);
$iv = implode(array_map("chr", $bytes)); //PHP 4 >= 4.0.2
$Ds_Merchant_Order_encrypt3DES = mcrypt_encrypt(MCRYPT_3DES, $secretCode, $Ds_Merchant_Order, MCRYPT_MODE_CBC, $iv);
$hash = hash_hmac('sha256', $Ds_MerchantParameters, $Ds_Merchant_Order_encrypt3DES, true);
$Ds_Signature = $this->encodeBase64($hash);
//Ds_Signature: hueCwD/cbvrCi+9IDY86WteMpXulIl0IDNXNlYgcZHM=
库 .NET 代码:
byte[] secretCode = Base64Decode("Mk9m98IfEblmPfrpsawt7BmxObt98Jev");
string Ds_MerchantParameters = "eyJEU19NRVJDSEFOVF9BTU9VTlQiOiIxNDUiLCJEU19NRVJDSEFOVF9PUkRFUiI6IjE0NDI3NzI2NDUiLCJEU19NRVJDSEFOVF9NRVJDSEFOVENPREUiOiI5OTkwMDg4ODEiLCJEU19NRVJDSEFOVF9DVVJSRU5DWSI6Ijk3OCIsIkRTX01FUkNIQU5UX1RSQU5TQUNUSU9OVFlQRSI6IjAiLCJEU19NRVJDSEFOVF9URVJNSU5BTCI6Ijg3MSIsIkRTX01FUkNIQU5UX01FUkNIQU5UVVJMIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX05vdGlmLnBocCIsIkRTX01FUkNIQU5UX1VSTE9LIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX09LX0tPLnBocCIsIkRTX01FUkNIQU5UX1VSTEtPIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX09LX0tPLnBocCJ9"
string Ds_Merchant_Order = "1442772645";
// Calculate derivated key by encrypting with 3DES the "DS_MERCHANT_ORDER" with decoded key
byte[] Ds_Merchant_Order_encrypt3DES = cryp.Encrypt3DES(Ds_Merchant_Order, secretCode);
// Calculate HMAC SHA256 with Encoded base64 JSON string using derivated key calculated previously
byte[] hash = cryp.GetHMACSHA256(Ds_MerchantParameters, Ds_Merchant_Order_encrypt3DES);
// Encode byte[] res to Base64 String
string Ds_Signature = Base64Encode2(hash);
//Ds_Signature: hueCwD/cbvrCi+9IDY86WteMpXulIl0IDNXNlYgcZHM=
public byte[] Encrypt3DES(string plainText, byte[] key) {
byte[] toEncryptArray = Encoding.UTF8.GetBytes(plainText);
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
try {
/// SALT used in 3DES encryptation process.
byte[] SALT = new byte[8] {0,0,0,0,0,0,0,0};
// Block size 64 bit (8 bytes)
tdes.BlockSize = 64;
// Key Size 192 bit (24 bytes)
tdes.KeySize = 192;
tdes.Mode = CipherMode.CBC;
tdes.Padding = PaddingMode.Zeros;
tdes.IV = SALT;
tdes.Key = key;
var cTransform = tdes.CreateEncryptor();
//transform the specified region of bytes array to resultArray
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
//Release resources held by TripleDes Encryptor
tdes.Clear();
return resultArray;
} // Error in Cryptographic method
catch (CryptographicException ex) {
throw new CryptographicException(ex.Message);
}
}
************************------------*********** ***********------------************************
我的Objective-C代码:
NSString *Ds_Merchant_Order = @"1442772645";
NSString *Ds_MerchantParameters = @"eyJEU19NRVJDSEFOVF9BTU9VTlQiOiIxNDUiLCJEU19NRVJDSEFOVF9PUkRFUiI6IjE0NDI3NzI2NDUiLCJEU19NRVJDSEFOVF9NRVJDSEFOVENPREUiOiI5OTkwMDg4ODEiLCJEU19NRVJDSEFOVF9DVVJSRU5DWSI6Ijk3OCIsIkRTX01FUkNIQU5UX1RSQU5TQUNUSU9OVFlQRSI6IjAiLCJEU19NRVJDSEFOVF9URVJNSU5BTCI6Ijg3MSIsIkRTX01FUkNIQU5UX01FUkNIQU5UVVJMIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX05vdGlmLnBocCIsIkRTX01FUkNIQU5UX1VSTE9LIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX09LX0tPLnBocCIsIkRTX01FUkNIQU5UX1VSTEtPIjoiaHR0cHM6XC9cL2VqZW1wbG9cL2VqZW1wbG9fVVJMX09LX0tPLnBocCJ9";
NSString *clave = @"Mk9m98IfEblmPfrpsawt7BmxObt98Jev";
NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:clave options:0];
NSString *secretCode = [self hexadecimalString:decodedData];
NSData *Ds_Merchant_Order_encrypt3DES = [self encrypt3DES:Ds_Merchant_Order key:secretCode];
NSData *hash = [self hmac256ForKeyAndData:Ds_MerchantParameters withKey:Ds_Merchant_Order_encrypt3DES];
NSString *Ds_Signature = [hash base64EncodedStringWithOptions:0];
//Ds_Signature: kUVwanKNIlrvw3t56HUAYXSBmE/u6ruTj1r/FGOIiUg=
我的函数:
- (NSString *)hexadecimalString:(NSData*)data{
const unsigned char *dataBuffer = (const unsigned char *)[data bytes];
if (!dataBuffer){
return [NSString string];
}
NSUInteger dataLength = [data length];
NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)];
for (int i = 0; i < dataLength; ++i){
[hexString appendFormat:@"%02x", (unsigned int)dataBuffer[i]];
}
return [NSString stringWithString:hexString];
}
- (NSData*)encrypt3DES:(NSString*)data key:(NSString*)key{
NSData *plainData = [data dataUsingEncoding:NSUTF8StringEncoding];
const void *vplainText = (const void *)[plainData bytes];
size_t plainTextBufferSize = [plainData length];
size_t movedBytes = 0;
size_t bufferPtrSize = (plainTextBufferSize + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);
uint8_t * bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));
memset((void *)bufferPtr, 0x0, bufferPtrSize);
NSString *initVec = @"[=17=][=17=][=17=][=17=][=17=][=17=][=17=][=17=]";
const void *vkey = (const void *) [key UTF8String];
const void *vinitVec = (const void *) [initVec UTF8String];
CCCryptorStatus ccStatus = CCCrypt(kCCEncrypt,
kCCAlgorithm3DES,
kCCOptionPKCS7Padding | kCCOptionECBMode,
vkey,
kCCKeySize3DES,
vinitVec,
vplainText,
plainTextBufferSize,
(void *)bufferPtr,
bufferPtrSize,
&movedBytes);
if (ccStatus == kCCSuccess) NSLog(@"SUCCESS");
else if (ccStatus == kCCParamError) NSLog( @"PARAM ERROR");
else if (ccStatus == kCCBufferTooSmall) NSLog( @"BUFFER TOO SMALL");
else if (ccStatus == kCCMemoryFailure) NSLog( @"MEMORY FAILURE");
else if (ccStatus == kCCAlignmentError) NSLog( @"ALIGNMENT");
else if (ccStatus == kCCDecodeError) NSLog( @"DECODE ERROR");
else if (ccStatus == kCCUnimplemented) NSLog( @"UNIMPLEMENTED");
return [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes];
}
-(NSData *)hmac256ForKeyAndData:(NSString *)data withKey:(NSData *)keyData{
NSData *dataData=[data dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData* hash = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, keyData.bytes, keyData.length, dataData.bytes, dataData.length, hash.mutableBytes);
return hash;
}
P.S.: 密钥和密码是假的,只是为了测试。 ;)
有几个错误:
第一:PHP版本使用CBC模式,iOS版本使用ECB模式。 CCCrypt
默认为CBC模式,去掉kCCOptionECBMode
即可。使用空 iv 会使第一个块不安全,通常使用随机 iv 并将其添加到加密数据之前。
其次:mcrypt不支持PKCS#7 padding,它只支持非标准的不安全的null padding。因此有必要在加密之前将填充添加到数据中。
从这个
添加 PKCS#7 填充 (php):
其中 $block
是以字节为单位的块大小,$str
是要加密的数据
$pad = $block - (strlen($str) % $block);
$str .= str_repeat(chr($pad), $pad);
删除 PKCS#7 填充 (php):
其中 $str
是解密后的数据
$len = strlen($str);
$pad = ord($str[$len-1]);
$str = $strsubstr($str, 0, $len - $pad);
注意:如果数据恰好是块大小的倍数,则将添加整个填充块,这是必要的。
有关填充的详细信息,请参阅 PKCS#7。
为了进一步调试,请提供所有参数和加密数据的十六进制转储:secretCode、Ds_Merchant_Order、iv 和加密输出。
最后:为了更好的安全性,请考虑使用可用于多种平台和语言的 RNCryptor。它经过严格审查,支持当前的最佳实践,目前已得到支持。