android - 在 Android 和 iPhone 中使用 AES 256 加密(不同的结果)
<p><p>我正在尝试通过引用 IOS 实现在 Android 平台上实现客户端加密/解密。我正在努力解决Android和IOS平台上的加密和解密不同的问题,即使它们使用了相同的算法。比方说,当Android设备加密上传文件到服务器时,IOS设备无法正确下载和解密。</p>
<p>我正在使用的算法</p>
<ol>
<li>使用用户提供的密码加密文件 key 。我们首先使用 PBKDF2 算法(SHA256 的 1000 次迭代)从密码中导出 key /iv 对,然后使用 AES 256/CBC 加密文件 key 。结果称为“加密文件 key ”。这个加密的文件 key 将被发送到并存储在服务器上。当您需要访问数据时,您可以从加密的文件 key 中解密文件 key 。</li>
<li>所有文件数据均使用 AES 256/CBC 文件 key 加密。我们使用 PBKDF2 算法(SHA256 的 1000 次迭代)从文件 key 中导出 key /iv 对。 </li>
<li>在本地存储派生的 key /iv 对并使用它们来加密文件。加密后,数据上传到服务器。下载文件时解密文件也是如此。</li>
</ol>
<p>安卓代码</p>
<pre><code> 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);
}
</code></pre>
<p>IOS代码</p>
<pre><code>+ (int)deriveKey:(const char *)data_in inlen:(int)in_len version:(int)version key:(unsigned char *)key iv:(unsigned char *)iv
{
unsigned char salt = { 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, iv0;
char passwordPtr = {0}; // room for terminator (unused)
;
if (version < 2) {
;
return;
}
;
char enc_random_key, dec_random_key;
int outlen;
hex_to_rawdata(encKey.UTF8String, enc_random_key, 48);
;
;
}
- (NSData *)encrypt:(NSString *)password encKey:(NSString *)encKey version:(int)version
{
uint8_t key = {0}, iv;
;
char *data_out;
int outlen;
int ret = ;
if (ret < 0) return nil;
return ;
}
</code></pre>
<p>这是一个完整的项目,任何人都觉得这很有用。</p>
<ol>
<li> <a href="https://github.com/haiwen/seadroid/pull/487" rel="noreferrer noopener nofollow">Support client side encryption #487</a> </li>
<li> <a href="http://manual.seafile.com/security/security_features.html" rel="noreferrer noopener nofollow">How does an encrypted library work?</a> </li>
</ol></p>
<br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
<p><p>你好,我也有同样的问题。</p>
<p>我发现 2 件事导致我的代码不匹配:
1. ios 和 android 加密算法不一样(我在 ios 中请求了 PKCS7Padding 算法,我在 android 中尝试了 NoPadding 和 AES/CBC/PKCS5Padding 算法。
2. ios生成的key与Android不同。</p>
<p>请查看我的工作代码在 ios 和 android 中都给出了相同的输出:</p>
<p>IOS:</p>
<pre><code>- (NSString *) encryptString:(NSString*)plaintext withKey:(NSString*)key {
NSData *data = [ AES256EncryptWithKey:key];
return ;
}
- (NSString *) decryptString:(NSString *)ciphertext withKey:(NSString*)key {
if (]) {
NSData *data = [ initWithBase64EncodedString:ciphertext options:kNilOptions];
return [ initWithData: encoding:NSUTF8StringEncoding];
}
return nil;
}
</code></pre>
<p>Android:(我们修改了 aes w.r.t iOS 默认方法)</p>
<pre><code>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;
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;
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;
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;
}
</code></pre>
<p>希望这对你们有帮助
谢谢</p></p>
<p style="font-size: 20px;">关于android - 在 Android 和 iPhone 中使用 AES 256 加密(不同的结果),我们在Stack Overflow上找到一个类似的问题:
<a href="https://stackoverflow.com/questions/36589644/" rel="noreferrer noopener nofollow" style="color: red;">
https://stackoverflow.com/questions/36589644/
</a>
</p>
页:
[1]