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

时间:2013-07-08 21:01:32

标签: java android ios aes

我是这种加密的完全新手,但我有一个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==

for Java:/s5YyKb3tDlUXt7pqA5OFA==

我现在该怎么办?

5 个答案:

答案 0 :(得分:6)

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

答案 1 :(得分:5)

这是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);
    }
}
}

答案 2 :(得分:1)

我的一位朋友和我创建了一个可以加密邮件的iOS和Android应用程序。要使用它,您应该使用以下代码片段from this website

创建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];

我使用website of Matt Gallagher中的base64DataFromString方法,否则使用

[[NSData alloc] base64EncodedDataWithOptions:NSUTF8StringEncoding];

该方法仅适用于&gt; = iOS 7.0

答案 3 :(得分:1)

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

答案 4 :(得分:0)

检查这个 https://github.com/mataprasad/Cross-platform-AES-encryption-128bit 适用于 iOS、Android、Java 后端,结果一致。

例如:

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