iOS加密与Android和.NET的3DES不同

4

我尝试在iOS上使用3DES加密一些内容,必须与Java和.NET的结果匹配。

Java代码:

public class EncryptionHelper {

// Encrypts string and encode in Base64
public static String encryptText(String plainText,String key, String IV) throws Exception {
    // ---- Use specified 3DES key and IV from other source --------------
    byte[] plaintext = plainText.getBytes();//input
    byte[] tdesKeyData = key.getBytes();// your encryption key

    byte[] myIV = IV.getBytes();// initialization vector

    Cipher c3des = Cipher.getInstance("DESede/CBC/PKCS5Padding");
    SecretKeySpec myKey = new SecretKeySpec(tdesKeyData, "DESede");
    IvParameterSpec ivspec = new IvParameterSpec(myIV);

    c3des.init(Cipher.ENCRYPT_MODE, myKey, ivspec);
    byte[] cipherText = c3des.doFinal(plaintext);
    String encryptedString = Base64.encodeToString(cipherText,
            Base64.DEFAULT);
    // return Base64Coder.encodeString(new String(cipherText));
    return encryptedString;
}

需要翻译的内容:

}

对应iOS代码如下:

-(NSString*)new3DESwithoperand:(NSString*)plaintext encryptOrDecrypt:(CCOperation)encryptorDecrypt key:(NSString*)key initVec:(NSString*)initVec
{

NSData* data = [plaintext dataUsingEncoding:NSUTF8StringEncoding];
const void *vplainText = [data bytes];;
size_t plainTextBufferSize = [data length];
NSLog(@"%@, Length: %u",[data description],[data length]);

size_t bufferPtrSize = (plainTextBufferSize + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);
NSLog(@"%zu, sizof of uint8_t: %zu",bufferPtrSize, sizeof(uint8_t));
size_t movedBytes = 0;
uint8_t *bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));
NSLog(@"%zu",sizeof(bufferPtr));
memset((void*)bufferPtr, 0x0, bufferPtrSize);
NSLog(@"%zu",sizeof(bufferPtr));

const void * vkey = [[NSData base64DataFromString:key] bytes];
const void *vinitVec = [[NSData base64DataFromString:initVec] bytes];
NSLog(@"vinitvec: %@",[[NSData base64DataFromString:initVec] description]);

CCCryptorStatus ccStatus;
ccStatus = CCCrypt(encryptorDecrypt,
                   kCCAlgorithm3DES,
                   kCCOptionPKCS7Padding & kCCModeCBC,
                   vkey,
                   kCCKeySize3DES,
                   vinitVec,
                   vplainText,
                   plainTextBufferSize,
                   (void*)bufferPtr,
                   bufferPtrSize,
                   &movedBytes);

NSData* result = [NSData dataWithBytes:(const void*)bufferPtr length:(NSUInteger)movedBytes];
NSString* str = [NSString base64StringFromData:result length:result.length];
NSLog(@"%@",str);
return str;

这段代码成功地加密和解密了一个字符串。但是,它的结果与.NET和Java不匹配。

谢谢。


这被称为2键3DES,已经过时,不安全,如果可能的话,在新工作中不应使用。 - zaph
3个回答

8

已经找到了解决方案,解决了iOS和.NET或Java生成的加密值不同的问题。

解决方案:
1. 在Android和.NET中,您必须使用16字节大小的密钥(例如:key="1234567890123456")

在iOS中,您需要使用24字节大小的密钥,但密钥的生成会有一点不同。 使用与Android或.NET相同的密钥(16字节),并将其附加到相同密钥的前8个字节。

key16Byte = "1234567890123456" //Android和.NET密钥 key24Byte = key16Byte + "12345678" //ios和Java密钥,复制16Byte密钥的前8个字节 //new24ByteKey = "123456789012345612345678"

  1. 从CCypher Mode中删除"& kCCModeCBC"。

  2. 某些值需要在CCCrypt函数中使用字节,我已在下面的代码中更改了这些值,如keyData、encryptData。

不同加密生成的原因: Android和.NET - 它采用16字节密钥并内部复制,生成24字节密钥。

Java - 如果提供16字节密钥值,则会抛出异常“无效密钥长度”。

iOS - 它生成16字节和24字节的加密值,而不会抛出任何异常,因此我们在使用16字节密钥时会得到不同的加密值。

Java代码

public class EncryptionHelper {

// Encrypts string and encode in Base64
public static String encryptText(String plainText,String key, String IV) throws Exception {
   // ---- Use specified 3DES key and IV from other source --------------
    byte[] plaintext = plainText.getBytes();//input
    byte[] tdesKeyData = key.getBytes();// your encryption key

    byte[] myIV = IV.getBytes();// initialization vector

    Cipher c3des = Cipher.getInstance("DESede/CBC/PKCS5Padding");
    SecretKeySpec myKey = new SecretKeySpec(tdesKeyData, "DESede");
    IvParameterSpec ivspec = new IvParameterSpec(myIV);

    c3des.init(Cipher.ENCRYPT_MODE, myKey, ivspec);
    byte[] cipherText = c3des.doFinal(plaintext);
    String encryptedString = Base64.encodeToString(cipherText,
            Base64.DEFAULT);
    // return Base64Coder.encodeString(new String(cipherText));
    return encryptedString;
}

iOS 代码:

- (NSString *)encrypt:(NSString *)encryptValue key:(NSString *)key24Byte IV:(NSString *)IV{
    // first of all we need to prepare key
    if([key length] != 24)
        return @"Require 24 byte key, call function generate24ByteKeySameAsAndroidDotNet with a 16Byte key same as used in Android and .NET"; //temporary error message


    NSData *keyData = [key24Byte dataUsingEncoding:NSUTF8StringEncoding];

    // our key is ready, let's prepare other buffers and moved bytes length
    NSData *encryptData = [encryptValue dataUsingEncoding:NSUTF8StringEncoding];
    size_t resultBufferSize = [encryptData length] + kCCBlockSize3DES;
    unsigned char resultBuffer[resultBufferSize];
    size_t moved = 0;

    // DES-CBC requires an explicit Initialization Vector (IV)
    // IV - second half of md5 key
    NSMutableData *ivData = [[IV dataUsingEncoding:NSUTF8StringEncoding]mutableCopy];
    NSMutableData *iv = [NSMutableData dataWithData:ivData];

    CCCryptorStatus cryptorStatus = CCCrypt(kCCEncrypt, kCCAlgorithm3DES,
                                            kCCOptionPKCS7Padding , [keyData bytes],
                                            kCCKeySize3DES, [iv bytes],
                                            [encryptData bytes], [encryptData length],
                                            resultBuffer, resultBufferSize, &moved);

    if (cryptorStatus == kCCSuccess) {
        return [[NSData dataWithBytes:resultBuffer length:moved] base64EncodedStringWithOptions:0];
    } else {
        return nil;
    }
}

iOS

-(NSString *)generate24ByteKeySameAsAndroidDotNet:(NSString *)key{
    NSString *new24ByteKey = key;

    ;
    new24ByteKey = [new24ByteKey stringByAppendingString:[key substringWithRange:NSMakeRange(0, 8)]];

    return new24ByteKey;
}

我有同样的问题,并询问了这个问题 http://stackoverflow.com/questions/40887490/using-tripledes-and-md5-in-swift?noredirect=1#comment68991174_40887490。如果您能帮忙,我将不胜感激。 - ava

1
正如@Jugal Desai所提到的,iOS和Android/.Net之间的主要区别在于后者会自动从密钥开头再填充另外8个字节以达到16个字节的长度!你救了我 :) 这里提供Swift 3的简单解决方法:

....

YOUR_KEY_SIZE_16 = YOUR_KEY_SIZE_16 + YOUR_KEY_SIZE_16[0...7]

....

以下是完整的示例代码(使用MD5作为密钥哈希值 + ECB模式 + PKCS7填充),输出结果为base64编码:

func tripleDesEncrypt(keyString: String, pass: String) -> String{
    let keyData = keyString.data(using: .utf8)!

    var digestData = Data(count: Int(CC_MD5_DIGEST_LENGTH))

    _ = digestData.withUnsafeMutableBytes {digestBytes in

        keyData.withUnsafeBytes {messageBytes in
            CC_MD5(messageBytes, CC_LONG(keyData.count), digestBytes)
        }
    }

    digestData = digestData + digestData[0...7]
    let data = pass.data(using: .utf8)!
    let dataNS = data as NSData

    let cryptData    = NSMutableData(length: Int(dataNS.length) + kCCBlockSize3DES)!

    let keyLength              = size_t(kCCKeySize3DES)
    let operation: CCOperation = UInt32(kCCEncrypt)
    let algoritm:  CCAlgorithm = UInt32(kCCAlgorithm3DES)
    let options:   CCOptions   = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)

    var numBytesEncrypted :size_t = 0

    let cryptStatus = CCCrypt(operation,
        algoritm,
        options,
        (digestData as NSData).bytes,
        keyLength,
        nil,
        dataNS.bytes,
        dataNS.length,
        cryptData.mutableBytes,
        cryptData.length,
        &numBytesEncrypted)

    if UInt32(cryptStatus) == UInt32(kCCSuccess) {
        cryptData.length = Int(numBytesEncrypted)

        // Not all data is a UTF-8 string so Base64 is used
        let base64cryptString = cryptData.base64EncodedString(options: NSData.Base64EncodingOptions.lineLength76Characters)
        return base64cryptString
    } else {
        print("Error: \(cryptStatus)")
    }
    return ""
}

0

解密有多严重?

- (NSString *)encrypt:(NSString *)encryptValue key:(NSString *)key24Byte IV:(NSString *)IV{
    // first of all we need to prepare key
    if([key length] != 24)
        return @"Require 24 byte key, call function generate24ByteKeySameAsAndroidDotNet with a 16Byte key same as used in Android and .NET"; //temporary error message


    NSData *keyData = [key24Byte dataUsingEncoding:NSUTF8StringEncoding];

    // our key is ready, let's prepare other buffers and moved bytes length
    NSData *encryptData = [encryptValue dataUsingEncoding:NSUTF8StringEncoding];
    size_t resultBufferSize = [encryptData length] + kCCBlockSize3DES;
    unsigned char resultBuffer[resultBufferSize];
    size_t moved = 0;

    // DES-CBC requires an explicit Initialization Vector (IV)
    // IV - second half of md5 key
    NSMutableData *ivData = [[IV dataUsingEncoding:NSUTF8StringEncoding]mutableCopy];
    NSMutableData *iv = [NSMutableData dataWithData:ivData];

    CCCryptorStatus cryptorStatus = CCCrypt(kCCEncrypt, kCCAlgorithm3DES,
                                            kCCOptionPKCS7Padding , [keyData bytes],
                                            kCCKeySize3DES, [iv bytes],
                                            [encryptData bytes], [encryptData length],
                                            resultBuffer, resultBufferSize, &moved);

    if (cryptorStatus == kCCSuccess) {
        return [[NSData dataWithBytes:resultBuffer length:moved] base64EncodedStringWithOptions:0];
    } else {
        return nil;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接