繁体   English   中英

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

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

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

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;
}

和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);
}

在另一个 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;
}

我想要加密的字符串是kayvan用钥匙TheBestSecretKey Base64编码后的结果是:

iOS: 9wXUiV+ChoLHmF6KraVtDQ==

对于 Java: /s5YyKb3tDlUXt7pqA5OFA==

我现在该怎么办?

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

这是生成用于解密/加密消息的字符串的 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);
    }
}
}

我和我的一个朋友创建了一个可以加密消息的 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;
}

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

#import "NSData+Crypt.h"

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

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

并像这样解密数据:

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

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

[[NSData alloc] base64EncodedDataWithOptions:NSUTF8StringEncoding];

该方法仅适用于 >= iOS 7.0

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

检查这个https://github.com/mataprasad/Cross-platform-AES-encryption-128bit适用于 iOS、Android、Java 后端对齐相同的结果。

例如:

password: "abc@123"
Key: "12DA321*2X33%@52"
AES Encryption: "3E95waIL9bw07q4ErjJDSw=="

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM