3DESencryption导致PHP,JAVA和.NET从3DES iOS产生不同的结果

我正在尝试在iOS 8中升级当前的应用程序,以适应Redsys / Sermpa的新encryptionSHA256。

但是我在数据encryption方面有问题。 在PHP,Java和.NET中我得到了一个与iOS完全不同的结果。

我认为这个问题一定是在3DES IOS CCCrypt中。

PHP,JAVA和.NET代码是一个库,我不能改变这个库。

我必须在iOS中做出encryption的结果,在PHP,JAVA和.NET中对结果进行encryption。

库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= 

我的function:

 - (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 = @"\0\0\0\0\0\0\0\0"; 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; } 

PS:密钥和密码是假的,只是为了testing。 ;)

有几个错误:

首先:PHP版本使用CBC模式,iOS版本使用ECB模式。 CCCrypt的默认值是CBC模式,只是删除kCCOptionECBMode 。 使用空iv将使第一个块不安全,通常使用一个随机iv,并将其作为前缀encryption数据。

第二:mcrypt不支持PKCS#7填充,它只支持非标准的不安全的null填充。 因此有必要在encryption之前将填充添加到数据中。

从这个SO回答 :

添加PKCS#7填充(php):
其中$block是以字节$str单位的块大小, $str是要encryption的数据

  $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 。

为了进一步debugging,提供所有参数和数据的hex转储encryption和encryption:secretCode,Ds_Merchant_Order,iv和encryption输出。

最后:为了更好的安全性考虑使用可用于多种平台和语言的RNCryptor 。 它经过严格审查,支持目前的最佳做法,目前得到支持。