在Android和iPhone中使用AES 256encryption(不同的结果)

我试图通过引用IOS实现在Android平台上实现客户端encryption/解密。 我正在琢磨在Android和IOS平台上的encryption和解密是不同的,即使他们使用相同的algorithm。 比方说,Android设备encryption上传文件到服务器时,IOS设备无法正确下载和解密。

我正在使用的algorithm

  1. 使用用户提供的密码encryption文件密钥。 我们首先使用PBKDF2algorithm(SHA256的1000次迭代)从密码中导出密钥/ iv对,然后使用AES 256 / CBC对文件密钥进行encryption。 结果被称为“encryption文件密钥”。 这个encryption的文件密钥将被发送并存储在服务器上。 当您需要访问数据时,您可以从encryption的文件密钥中解密文件密钥。
  2. 所有文件数据都通过AES 256 / CBC文件密钥进行encryption。 我们使用PBKDF2algorithm(SHA256的1000次迭代)从文件密钥派生key / iv对。
  3. 在本地存储派生密钥/ iv对,并使用它们来encryption文件。 encryption后,数据被上传到服务器。 下载文件时与解密文件相同。

Android代码

private static final String TAG = Crypto.class.getSimpleName(); private static final String CIPHER_ALGORITHM = "AES/CBC/NoPadding"; private static int KEY_LENGTH = 32; private static int KEY_LENGTH_SHORT = 16; // minimum values recommended by PKCS#5, increase as necessary private static int ITERATION_COUNT = 1000; // Should generate random salt for each repo private static byte[] salt = {(byte) 0xda, (byte) 0x90, (byte) 0x45, (byte) 0xc3, (byte) 0x06, (byte) 0xc7, (byte) 0xcc, (byte) 0x26}; private Crypto() { } /** * decrypt repo encKey * * @param password * @param randomKey * @param version * @return * @throws UnsupportedEncodingException * @throws NoSuchAlgorithmException */ public static String deriveKeyPbkdf2(String password, String randomKey, int version) throws UnsupportedEncodingException, NoSuchAlgorithmException { if (TextUtils.isEmpty(password) || TextUtils.isEmpty(randomKey)) { return null; } PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(new SHA256Digest()); gen.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password.toCharArray()), salt, ITERATION_COUNT); byte[] keyBytes; if (version == 2) { keyBytes = ((KeyParameter) gen.generateDerivedMacParameters(KEY_LENGTH * 8)).getKey(); } else keyBytes = ((KeyParameter) gen.generateDerivedMacParameters(KEY_LENGTH_SHORT * 8)).getKey(); SecretKey realKey = new SecretKeySpec(keyBytes, "AES"); final byte[] iv = deriveIVPbkdf2(realKey.getEncoded()); return seafileDecrypt(fromHex(randomKey), realKey, iv); } public static byte[] deriveIVPbkdf2(byte[] key) throws UnsupportedEncodingException { PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(new SHA256Digest()); gen.init(key, salt, 10); return ((KeyParameter) gen.generateDerivedMacParameters(KEY_LENGTH_SHORT * 8)).getKey(); } /** * All file data is encrypted by the file key with AES 256/CBC. * * We use PBKDF2 algorithm (1000 iterations of SHA256) to derive key/iv pair from the file key. * After encryption, the data is uploaded to the server. * * @param plaintext * @param key * @return */ private static byte[] seafileEncrypt(byte[] plaintext, SecretKey key, byte[] iv) { try { Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); IvParameterSpec ivParams = new IvParameterSpec(iv); cipher.init(Cipher.ENCRYPT_MODE, key, ivParams); return cipher.doFinal(plaintext); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); Log.e(TAG, "NoSuchAlgorithmException " + e.getMessage()); return null; } catch (InvalidKeyException e) { e.printStackTrace(); Log.e(TAG, "InvalidKeyException " + e.getMessage()); return null; } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); Log.e(TAG, "InvalidAlgorithmParameterException " + e.getMessage()); return null; } catch (NoSuchPaddingException e) { e.printStackTrace(); Log.e(TAG, "NoSuchPaddingException " + e.getMessage()); return null; } catch (IllegalBlockSizeException e) { e.printStackTrace(); Log.e(TAG, "IllegalBlockSizeException " + e.getMessage()); return null; } catch (BadPaddingException e) { e.printStackTrace(); Log.e(TAG, "BadPaddingException " + e.getMessage()); return null; } } /** * All file data is encrypted by the file key with AES 256/CBC. * * We use PBKDF2 algorithm (1000 iterations of SHA256) to derive key/iv pair from the file key. * After encryption, the data is uploaded to the server. * * @param plaintext * @param key * @return */ public static byte[] encrypt(byte[] plaintext, String key, byte[] iv, int version) throws NoSuchAlgorithmException { PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(new SHA256Digest()); gen.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(key.toCharArray()), salt, ITERATION_COUNT); byte[] keyBytes; if (version == 2) { keyBytes = ((KeyParameter) gen.generateDerivedMacParameters(KEY_LENGTH * 8)).getKey(); } else keyBytes = ((KeyParameter) gen.generateDerivedMacParameters(KEY_LENGTH_SHORT * 8)).getKey(); SecretKey realKey = new SecretKeySpec(keyBytes, "AES"); return seafileEncrypt(plaintext, realKey , iv); } 

IOS代码

 + (int)deriveKey:(const char *)data_in inlen:(int)in_len version:(int)version key:(unsigned char *)key iv:(unsigned char *)iv { unsigned char salt[8] = { 0xda, 0x90, 0x45, 0xc3, 0x06, 0xc7, 0xcc, 0x26 }; if (version == 2) { PKCS5_PBKDF2_HMAC (data_in, in_len, salt, sizeof(salt), 1000, EVP_sha256(), 32, key); PKCS5_PBKDF2_HMAC ((char *)key, 32, salt, sizeof(salt), 10, EVP_sha256(), 16, iv); return 0; } else if (version == 1) return EVP_BytesToKey (EVP_aes_128_cbc(), /* cipher mode */ EVP_sha1(), /* message digest */ salt, /* salt */ (unsigned char*)data_in, in_len, 1 << 19, /* iteration times */ key, /* the derived key */ iv); /* IV, initial vector */ else return EVP_BytesToKey (EVP_aes_128_ecb(), /* cipher mode */ EVP_sha1(), /* message digest */ NULL, /* salt */ (unsigned char*)data_in, in_len, 3, /* iteration times */ key, /* the derived key */ iv); /* IV, initial vector */ } +(int)seafileEncrypt:(char **)data_out outlen:(int *)out_len datain:(const char *)data_in inlen:(const int)in_len version:(int)version key:(uint8_t *)key iv:(uint8_t *)iv { int ret, blks; EVP_CIPHER_CTX ctx; EVP_CIPHER_CTX_init (&ctx); if (version == 2) ret = EVP_EncryptInit_ex (&ctx, EVP_aes_256_cbc(), /* cipher mode */ NULL, /* engine, NULL for default */ key, /* derived key */ iv); /* initial vector */ else if (version == 1) ret = EVP_EncryptInit_ex (&ctx, EVP_aes_128_cbc(), /* cipher mode */ NULL, /* engine, NULL for default */ key, /* derived key */ iv); /* initial vector */ else ret = EVP_EncryptInit_ex (&ctx, EVP_aes_128_ecb(), /* cipher mode */ NULL, /* engine, NULL for default */ key, /* derived key */ iv); /* initial vector */ if (ret == DEC_FAILURE) return -1; blks = (in_len / BLK_SIZE) + 1; *data_out = (char *)malloc (blks * BLK_SIZE); if (*data_out == NULL) { Debug ("failed to allocate the output buffer.\n"); goto enc_error; } int update_len, final_len; /* Do the encryption. */ ret = EVP_EncryptUpdate (&ctx, (unsigned char*)*data_out, &update_len, (unsigned char*)data_in, in_len); if (ret == ENC_FAILURE) goto enc_error; /* Finish the possible partial block. */ ret = EVP_EncryptFinal_ex (&ctx, (unsigned char*)*data_out + update_len, &final_len); *out_len = update_len + final_len; /* out_len should be equal to the allocated buffer size. */ if (ret == ENC_FAILURE || *out_len != (blks * BLK_SIZE)) goto enc_error; EVP_CIPHER_CTX_cleanup (&ctx); return 0; enc_error: EVP_CIPHER_CTX_cleanup (&ctx); *out_len = -1; if (*data_out != NULL) free (*data_out); *data_out = NULL; return -1; } + (void)generateKey:(NSString *)password version:(int)version encKey:(NSString *)encKey key:(uint8_t *)key iv:(uint8_t *)iv { unsigned char key0[32], iv0[16]; char passwordPtr[256] = {0}; // room for terminator (unused) [password getCString:passwordPtr maxLength:sizeof(passwordPtr) encoding:NSUTF8StringEncoding]; if (version < 2) { [NSData deriveKey:passwordPtr inlen:(int)password.length version:version key:key iv:iv]; return; } [NSData deriveKey:passwordPtr inlen:(int)password.length version:version key:key0 iv:iv0]; char enc_random_key[48], dec_random_key[48]; int outlen; hex_to_rawdata(encKey.UTF8String, enc_random_key, 48); [NSData seafileDecrypt:dec_random_key outlen:&outlen datain:(char *)enc_random_key inlen:48 version:2 key:key0 iv:iv0]; [NSData deriveKey:dec_random_key inlen:32 version:2 key:key iv:iv]; } - (NSData *)encrypt:(NSString *)password encKey:(NSString *)encKey version:(int)version { uint8_t key[kCCKeySizeAES256+1] = {0}, iv[kCCKeySizeAES128+1]; [NSData generateKey:password version:version encKey:encKey key:key iv:iv]; char *data_out; int outlen; int ret = [NSData seafileEncrypt:&data_out outlen:&outlen datain:self.bytes inlen:(int)self.length version:version key:key iv:iv]; if (ret < 0) return nil; return [NSData dataWithBytesNoCopy:data_out length:outlen]; } 

这是完整的项目,任何人谁发现这个有用的。

  1. 支持客户端encryption#487
  2. encryption库如何工作?

嗨,我也有同样的问题。

我发现2件事情导致我的代码不匹配︰1. IOS和Androidencryptionalgorithm是不一样的(我已经请求在iOS的PKCS7Paddingalgorithm,我尝试在Android的NoPadding和AES / CBC / PKCS5Paddingalgorithm2.在ios中生成的密钥是不同于Android。

请参阅我的工作代码在iOS和Android中给出相同的输出:

IOS:

 - (NSString *) encryptString:(NSString*)plaintext withKey:(NSString*)key { NSData *data = [[plaintext dataUsingEncoding:NSUTF8StringEncoding] AES256EncryptWithKey:key]; return [data base64EncodedStringWithOptions:kNilOptions]; } - (NSString *) decryptString:(NSString *)ciphertext withKey:(NSString*)key { if ([ciphertext isKindOfClass:[NSString class]]) { NSData *data = [[NSData alloc] initWithBase64EncodedString:ciphertext options:kNilOptions]; return [[NSString alloc] initWithData:[data AES256DecryptWithKey:key] encoding:NSUTF8StringEncoding]; } return nil; } 

Android:(我们修改了iOS默认方法)

 private static String Key = "your key"; public static String encryptString(String stringToEncode) throws NullPointerException { try { SecretKeySpec skeySpec = getKey(Key); byte[] clearText = stringToEncode.getBytes("UTF8"); final byte[] iv = new byte[16]; Arrays.fill(iv, (byte) 0x00); IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivParameterSpec); String encrypedValue = Base64.encodeToString(cipher.doFinal(clearText), Base64.DEFAULT); return encrypedValue; } catch (Exception e) { e.printStackTrace(); } return ""; } public static String encryptString(String stringToEncode) throws NullPointerException { try { SecretKeySpec skeySpec = getKey(Key); byte[] clearText = stringToEncode.getBytes("UTF8"); final byte[] iv = new byte[16]; Arrays.fill(iv, (byte) 0x00); IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec); byte[] cipherData = cipher.doFinal(Base64.decode(stringToEncode.getBytes("UTF-8"), Base64.DEFAULT)); String decoded = new String(cipherData, "UTF-8"); return decoded; } catch (Exception e) { e.printStackTrace(); } return ""; } private static SecretKeySpec getKey(String password) throws UnsupportedEncodingException { int keyLength = 256; byte[] keyBytes = new byte[keyLength / 8]; Arrays.fill(keyBytes, (byte) 0x0); 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; } 

希望这个帮助你们谢谢