1
我想通過引用IOS實現在Android平臺上實現客戶端加密/解密。我正在與Android和IOS平臺上的加密和解密不同的問題搏鬥,即使他們使用了相同的算法。比方說,當Android設備加密並上傳文件到服務器時,IOS設備無法正確下載和解密。在Android和iPhone中使用AES 256加密(不同的結果)
我使用
- 加密與用戶提供的密碼文件密鑰的算法。我們首先使用PBKDF2算法(SHA256的1000次迭代)從密碼導出密鑰/ iv對,然後使用AES 256/CBC加密文件密鑰。結果被稱爲「加密文件密鑰」。此加密文件密鑰將被髮送並存儲在服務器上。當您需要訪問數據時,您可以從加密的文件密鑰中解密文件密鑰。
- 所有文件數據均使用AES 256/CBC文件密鑰進行加密。我們使用PBKDF2算法(SHA256的1000次迭代)從文件關鍵字派生key/iv對。
- 在本地存儲派生密鑰/ iv對並使用它們來加密文件。加密後,數據被上傳到服務器。下載文件時與解密文件相同。
的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];
}
這是任何完整的項目誰發現這個有用。
「不能正確解密」。超級模糊。提供更多信息,例外情況等 –
@LukePark Android設備的例外只能對自己以前處理過的文件進行加密/解密,而不是IOS設備處理的文件。我還沒有找到任何線索,因爲我不熟悉Objective-C(或C)語言加密。 –
你不可能得到有用的答案,因爲你的問題包含太多的代碼。請做更多分析。你的代碼由幾個步驟組成。使用相同的輸入數據並比較每個步驟的輸出。這樣你就可以找出iOS和Android真正發生的區別。一旦你更好地分離了問題,發佈一個更具體的問題。 – Codo