AES 在 iOS (Obj-C) 和 Android (Java) 中得到不同的结果

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17535918/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-01 02:14:52  来源:igfitidea点击:

AES gets different results in iOS (Obj-C) and Android (Java)

javaandroidiosaes

提问by alternatiph

I'm a complete newbie to this kind of encryption things but I have a Java app and an iOS, and I want them to both be able to ecrypt a text to a same result. I use AES. I found these codes, with a little modification of course, but they return different result

我是这种加密事物的完全新手,但我有一个 Java 应用程序和一个 iOS,我希望它们都能够将文本加密为相同的结果。我使用AES。我找到了这些代码,当然稍加修改,但它们返回不同的结果

iOS Code:

iOS代码:

- (NSData *)AESEncryptionWithKey:(NSString *)key {    
    unsigned char keyPtr[kCCKeySizeAES128] = { 'T', 'h', 'e', 'B', 'e', 's', 't', 'S', 'e', 'c', 'r','e', 't', 'K', 'e', 'y' };
    size_t bufferSize = 16;
    void *buffer = malloc(bufferSize);
    size_t numBytesEncrypted = 0;
    const char iv2[16] = {  65, 1, 2, 23, 4, 5, 6, 7, 32, 21, 10, 11, 12, 13, 84, 45 };
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
                                          kCCAlgorithmAES128,
                                          kCCOptionECBMode | kCCOptionPKCS7Padding,,
                                          keyPtr,
                                          kCCKeySizeAES128,
                                          iv2,
                                          @"kayvan",
                                          6,
                                          dataInLength,
                                          buffer,
                                          bufferSize,
                                          &numBytesEncrypted);


    if (cryptStatus == kCCSuccess) {
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }

    free(buffer);
    return nil;
}

and the Java code is:

和Java代码是:

public static void main(String[] args) throws Exception {
    String password = "kayvan";
    String key = "TheBestSecretKey";
    String newPasswordEnc = AESencrp.newEncrypt(password, key);
    System.out.println("Encrypted Text : " + newPasswordEnc);
}

and in another java class (AESencrp.class) I have:

在另一个 java 类 ( AESencrp.class) 中,我有:

public static final byte[] IV = { 65, 1, 2, 23, 4, 5, 6, 7, 32, 21, 10, 11, 12, 13, 84, 45 };
public static String newEncrypt(String text, String key) throws Exception {
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    byte[] keyBytes= new byte[16];
    byte[] b= key.getBytes("UTF-8");
    int len = 16; 
    System.arraycopy(b, 0, keyBytes, 0, len);
    SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
    IvParameterSpec ivSpec = new IvParameterSpec(IV);
    System.out.println(ivSpec);
    cipher.init(Cipher.ENCRYPT_MODE,keySpec,ivSpec);
    byte[] results = cipher.doFinal(text.getBytes("UTF-8"));
    String result = DatatypeConverter.printBase64Binary(results);
    return result;
}

The string I wanted to encrypt is kayvanwith the key TheBestSecretKey. and the results after Base64 encoding are:

我想加密的字符串是kayvankey TheBestSecretKey。Base64编码后的结果是:

for iOS: 9wXUiV+ChoLHmF6KraVtDQ==

对于 iOS: 9wXUiV+ChoLHmF6KraVtDQ==

for Java: /s5YyKb3tDlUXt7pqA5OFA==

对于 Java: /s5YyKb3tDlUXt7pqA5OFA==

What should I do now?

我现在应该怎么办?

回答by Michal Zaborowski

I made a gist with iOS/Android/Node.js AES256 same result encoding, https://gist.github.com/m1entus/f70d4d1465b90d9ee024

我用 iOS/Android/Node.js AES256 相同的结果编码做了一个要点,https://gist.github.com/m1entus/f70d4d1465b90d9ee024

回答by A.S.

And here is the Android Version wich is generating the String for decrypt/encrypt Messages, it uses Cipher and generates the right vector to make the same result as iOS. This is corresponding to the iOS Version of @亚历山大 here in this thread.

这是生成用于解密/加密消息的字符串的 Android 版本,它使用 Cipher 并生成正确的向量以产生与 iOS 相同的结果。这与此线程中@亚历山大的iOS版本相对应。

public class MyCrypter {

private static String TAG = "MyCrypter";

public MyCrypter() {

}

/**
 * Encodes a String in AES-128 with a given key
 * 
 * @param context
 * @param password
 * @param text
 * @return String Base64 and AES encoded String
 * @throws NoPassGivenException
 * @throws NoTextGivenException
 */
public String encode(Context context, String password, String text)
        throws NoPassGivenException, NoTextGivenException {
    if (password.length() == 0 || password == null) {
        throw new NoPassGivenException("Please give Password");
    }

    if (text.length() == 0 || text == null) {
        throw new NoTextGivenException("Please give text");
    }

    try {
        SecretKeySpec skeySpec = getKey(password);
        byte[] clearText = text.getBytes("UTF8");

        //IMPORTANT TO GET SAME RESULTS ON iOS and ANDROID
        final byte[] iv = new byte[16];
        Arrays.fill(iv, (byte) 0x00);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);

        // Cipher is not thread safe
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivParameterSpec);

        String encrypedValue = Base64.encodeToString(
                cipher.doFinal(clearText), Base64.DEFAULT);
        Log.d(TAG, "Encrypted: " + text + " -> " + encrypedValue);
        return encrypedValue;

    } catch (InvalidKeyException e) {
        e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (BadPaddingException e) {
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
        e.printStackTrace();
    } catch (InvalidAlgorithmParameterException e) {
        e.printStackTrace();
    }
    return "";
}

/**
 * Decodes a String using AES-128 and Base64
 * 
 * @param context
 * @param password
 * @param text
 * @return desoded String
 * @throws NoPassGivenException
 * @throws NoTextGivenException
 */
public String decode(Context context, String password, String text)
        throws NoPassGivenException, NoTextGivenException {

    if (password.length() == 0 || password == null) {
        throw new NoPassGivenException("Please give Password");
    }

    if (text.length() == 0 || text == null) {
        throw new NoTextGivenException("Please give text");
    }

    try {
        SecretKey key = getKey(password);

        //IMPORTANT TO GET SAME RESULTS ON iOS and ANDROID
        final byte[] iv = new byte[16];
        Arrays.fill(iv, (byte) 0x00);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);

        byte[] encrypedPwdBytes = Base64.decode(text, Base64.DEFAULT);
        // cipher is not thread safe
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        cipher.init(Cipher.DECRYPT_MODE, key, ivParameterSpec);
        byte[] decrypedValueBytes = (cipher.doFinal(encrypedPwdBytes));

        String decrypedValue = new String(decrypedValueBytes);
        Log.d(TAG, "Decrypted: " + text + " -> " + decrypedValue);
        return decrypedValue;

    } catch (InvalidKeyException e) {
        e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (BadPaddingException e) {
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
        e.printStackTrace();
    } catch (InvalidAlgorithmParameterException e) {
        e.printStackTrace();
    }
    return "";
}

/**
 * Generates a SecretKeySpec for given password
 * @param password
 * @return SecretKeySpec
 * @throws UnsupportedEncodingException
 */
public SecretKeySpec getKey(String password)
        throws UnsupportedEncodingException {


    int keyLength = 128;
    byte[] keyBytes = new byte[keyLength / 8];
    // explicitly fill with zeros
    Arrays.fill(keyBytes, (byte) 0x0);

    // if password is shorter then key length, it will be zero-padded
    // to key length
    byte[] passwordBytes = password.getBytes("UTF-8");
    int length = passwordBytes.length < keyBytes.length ? passwordBytes.length
            : keyBytes.length;
    System.arraycopy(passwordBytes, 0, keyBytes, 0, length);
    SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
    return key;
}

public class NoTextGivenException extends Exception {
    public NoTextGivenException(String message) {
        super(message);
    }
}

public class NoPassGivenException extends Exception {
    public NoPassGivenException(String message) {
        super(message);
    }
}
}

回答by Alex Cio

A friend of mine and me created an iOS and Android app which can crypt messages. To use it, you should create an extension of NSData with following code Snippet from this website:

我和我的一个朋友创建了一个可以加密消息的 iOS 和 Android 应用程序。要使用它,您应该使用以下来自本网站的代码片段创建 NSData 的扩展:

- (NSData *)AES128EncryptWithKey:(NSString *)key {

    // 'key' should be 32 bytes for AES256,
    // 16 bytes for AES256, will be null-padded otherwise
    char keyPtr[kCCKeySizeAES128 + [key length]]; // room for terminator (unused)
    bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)

    // insert key in char array
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

    NSUInteger dataLength = [self length];
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);

    size_t numBytesEncrypted = 0;

    // the encryption method, use always same attributes in android and iPhone (f.e. PKCS7Padding)
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
                                          kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding,
                                          keyPtr,
                                          kCCKeySizeAES128,
                                          NULL                      /* initialization vector (optional) */,
                                          [self bytes], dataLength, /* input */
                                          buffer, bufferSize,       /* output */
                                          &numBytesEncrypted);
    if (cryptStatus == kCCSuccess) {

        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }

    free(buffer);
    return nil;
}

- (NSData *)AES128DecryptWithKey:(NSString *)key {
    // 'key' should be 32 bytes for AES256, will be null-padded otherwise
    char keyPtr[kCCKeySizeAES128 + [key length]]; // room for terminator (unused)
    bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)

    // insert key in char array
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

    NSUInteger dataLength = [self length];
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);

    size_t numBytesDecrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
                                          kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding,
                                          keyPtr,
                                          kCCKeySizeAES128,
                                          NULL                      /* initialization vector (optional) */,
                                          [self bytes], dataLength, /* input */
                                          buffer, bufferSize,       /* output */
                                          &numBytesDecrypted);

    if (cryptStatus == kCCSuccess) {

        return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
    }

    free(buffer);
    return nil;
}

+ (NSData *)base64DataFromString: (NSString *)string
{
    unsigned long ixtext, lentext;
    unsigned char ch, inbuf[4], outbuf[3];
    short i, ixinbuf;
    Boolean flignore, flendtext = false;
    const unsigned char *tempcstring;
    NSMutableData *theData;

    if (string == nil){
        return [NSData data];
    }

    ixtext = 0;
    tempcstring = (const unsigned char *)[string UTF8String];
    lentext = [string length];
    theData = [NSMutableData dataWithCapacity: lentext];
    ixinbuf = 0;

    while (true){
        if (ixtext >= lentext){
            break;
        }

        ch = tempcstring [ixtext++];
        flignore = false;

        if ((ch >= 'A') && (ch <= 'Z')){
            ch = ch - 'A';
        } else if ((ch >= 'a') && (ch <= 'z')){
            ch = ch - 'a' + 26;
        } else if ((ch >= '0') && (ch <= '9')){
            ch = ch - '0' + 52;
        } else if (ch == '+'){
            ch = 62;
        } else if (ch == '=') {
            flendtext = true;
        } else if (ch == '/') {
            ch = 63;
        } else {
            flignore = true;
        }

        if (!flignore){
            short ctcharsinbuf = 3;
            Boolean flbreak = false;

            if (flendtext){
                if (ixinbuf == 0){
                    break;
                }

                if ((ixinbuf == 1) || (ixinbuf == 2)) {
                    ctcharsinbuf = 1;
                } else {
                    ctcharsinbuf = 2;
                }

                ixinbuf = 3;
                flbreak = true;
            }

            inbuf [ixinbuf++] = ch;

            if (ixinbuf == 4){
                ixinbuf = 0;

                outbuf[0] = (inbuf[0] << 2) | ((inbuf[1] & 0x30) >> 4);
                outbuf[1] = ((inbuf[1] & 0x0F) << 4) | ((inbuf[2] & 0x3C) >> 2);
                outbuf[2] = ((inbuf[2] & 0x03) << 6) | (inbuf[3] & 0x3F);

                for (i = 0; i < ctcharsinbuf; i++) {
                    [theData appendBytes: &outbuf[i] length: 1];
                }
            }

            if (flbreak) {
                break;
            }
        }
    }

    return theData;
}

Then inside the class you want to use the crypt methods insert at the top:

然后在类里面你想使用 crypt 方法在顶部插入:

#import "NSData+Crypt.h"

And than encrypt your strings like that:

然后像这样加密你的字符串:

 NSData *value = [aString dataUsingEncoding:NSUTF8StringEncoding];
 NSData *encryptedData = [value AES128EncryptWithKey:myKey];
 NSString *myString = [encryptedData base64Encoding];

And decrypt the data like this:

并像这样解密数据:

NSData *myData = [NSData base64DataFromString:_textView.text];
NSData *decryptedData = [myData AES128DecryptWithKey:_textField.text];
NSString *myString2 = [[NSString alloc] initWithData:decryptedData
                                            encoding:NSUTF8StringEncoding];

I used the method base64DataFromStringfrom the website of Matt Gallagherotherwise if you use

我使用了base64DataFromString来自Matt Gallagher 网站的方法,否则如果你使用

[[NSData alloc] base64EncodedDataWithOptions:NSUTF8StringEncoding];

the method is just available on >= iOS 7.0

该方法仅适用于 >= iOS 7.0

回答by Navneet Kumar

Few important things to note while implementing AES encryption:
1. Never use plain text as encryption key. Always hash the plain text key and then use for encryption.
2. Always use Random IV (initialization vector) for encryption and decryption. True randomization is important. In the examples above, no initialization vector is set. This is a security flaw.
I recently wrote cross platform AES encryption and decryption library for C#, iOS and Android which I have posted on Github. You can see it here - https://github.com/Pakhee/Cross-platform-AES-encryption

实现 AES 加密时需要注意的几个重要事项:
1. 切勿使用纯文本作为加密密钥。始终散列纯文本密钥,然后用于加密。
2. 始终使用Random IV(初始化向量)进行加密和解密。真正的随机化很重要。在上面的例子中,没有设置初始化向量。这是一个安全漏洞。
我最近为 C#、iOS 和 Android 编写了跨平台 AES 加密和解密库,我已将其发布在 Github 上。你可以在这里看到它 - https://github.com/Pakhee/Cross-platform-AES-encryption