我尝试解密从iOS
发送的Android上的备份,并在方法doFinal上显示异常javax.crypto.BadPaddingException: pad block corrupted
。
public String decrypt(byte[] cipherText, SecretKey key, byte [] initialVector) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector);
cipher.init(Cipher.DECRYPT_MODE, key, ivParameterSpec);
cipherText = cipher.doFinal(cipherText);
return new String(cipherText, "UTF-8");
}
key和initialVector是从iOS以base64字符串发送的。相关代码:
public static byte[] decodeWebSafe(String s) throws Base64DecoderException {
byte[] bytes = s.getBytes();
return decodeWebSafe(bytes, 0, bytes.length);
}
byte[] iv = Base64.decodeWebSafe(enciv);
byte[] salt = Base64.decodeWebSafe(encsalt);
byte[] data = Base64.decodeWebSafe(encdata);
SecretKey key = Security.getExistingKey(password, salt);
String original = aes.decrypt(data, key, iv);
关于Security.getExistingKey:
public static SecretKey getExistingKey(String password, byte[] salt) throws Exception{
SecretKey key= null;
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, 10000, 256);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes=new byte[32];
keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
key= new SecretKeySpec(keyBytes, "AES");
return key;
}
任何解决方案的Thx。
P.S.这是我们在iOS中设置加密的方式:
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
self.encryptionKey.bytes, kCCKeySizeAES128,
self.encryptionIV.bytes, [rawData bytes], dataLength,
/* input */buffer, bufferSize, /* output */&numBytesEncrypted);
密钥和IV推导方法:
(NSData *)keyForPassword:(NSString *)password salt:(NSData *)salt {
NSMutableData *
derivedKey = [NSMutableData dataWithLength:kCCKeySizeAES128];
int result = CCKeyDerivationPBKDF(kCCPBKDF2, // algorithm
password.UTF8String,
password.length,
salt.bytes, // salt
salt.length, // saltLen
kCCPRFHmacAlgSHA1, // PRF
kPBKDFRounds, // rounds
derivedKey.mutableBytes, // derivedKey
derivedKey.length); // derivedKeyLen
}
答案 0 :(得分:2)
这是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);
}
}
}
答案 1 :(得分: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
答案 2 :(得分:0)
我可以看到您生成密钥的方式存在一些差异:
您最好花些时间更好地匹配密钥生成并在解密之前比较密钥。