C# 在iOS中存在AES填充问题,但在Android中效果良好
我正在尝试在iOS中使用AES/CBC加密。解密由C#完成。由Java在Android中完成的加密工作正常。尝试使用iOS代码时,我收到错误“填充无效且无法删除”。请帮忙 请查找下面的C#、Java和Objective C代码 C#代码: Obj C代码:C# 在iOS中存在AES填充问题,但在Android中效果良好,c#,android,ios,encryption,aes,C#,Android,Ios,Encryption,Aes,我正在尝试在iOS中使用AES/CBC加密。解密由C#完成。由Java在Android中完成的加密工作正常。尝试使用iOS代码时,我收到错误“填充无效且无法删除”。请帮忙 请查找下面的C#、Java和Objective C代码 C#代码: Obj C代码: + (NSString*)encryptBase64String:(NSString*)string keyString:(NSString*)keyString separateLines:(BOOL)separateLines {
+ (NSString*)encryptBase64String:(NSString*)string keyString:(NSString*)keyString separateLines:(BOOL)separateLines
{
const unsigned char rawSectret[] = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00};
NSData* rawScretdata = [NSData dataWithBytes:rawSectret length:kCCBlockSizeAES128];
NSString *shaKeyString = [self sha256HashFor:keyString];
NSData *sourceData = [string dataUsingEncoding:NSUTF8StringEncoding];
NSString *ivString = [[NSString alloc] initWithData:rawScretdata
encoding:NSUTF8StringEncoding];
NSData* Outdata = [sourceData AES128EncryptedDataWithKey:shaKeyString iv:ivString];
NSString *encodedString = [Outdata base64EncodedStringWithSeparateLines:separateLines];
return encodedString;
}
- (NSData *)AES128Operation:(CCOperation)operation key:(NSString *)key iv:(NSString *)iv
{
char keyPtr[kCCKeySizeAES128 + 1];
bzero(keyPtr, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
char ivPtr[kCCBlockSizeAES128 + 1];
bzero(ivPtr, sizeof(ivPtr));
if (iv) {
[iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
}
NSUInteger dataLength = [self length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(operation,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
keyPtr,
kCCBlockSizeAES128,
ivPtr,
[self bytes],
dataLength,
buffer,
bufferSize,
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free(buffer);
return nil;
}
Common Crypto使用显式密钥大小,许多其他库基于提供的密钥使用密钥大小,因此您需要确保为Common Crypto指定正确的密钥大小,
KCCKYSIZEAES128
,KCCKYSIZEAES192
,KCCKYSIZEAES256
,而不是kCCBlockSizeAES128
。在这种情况下,由于密钥是通过SHA-256kCCKeySizeAES256
派生的,因此应指定密钥大小
C#正在使用Rijndael,因此必须确保指定的块大小为128,这是AES支持的唯一块大小
只要你得到相同的输入,输出就会匹配。它们是选项(模式和填充)、键、键大小、数据和iv。用十六进制检查它们
提供测试向量:密钥、输入数据和输出数据均为十六进制,因此我们可以测试代码。通用加密使用显式密钥大小,许多其他库使用基于所提供密钥的密钥大小,因此您需要确保为通用加密指定正确的密钥大小,
kCCKeySizeAES128
,KCCKYSIZEAES192
,KCCKYSIZEAES256
,而不是kCCBlockSizeAES128
。在这种情况下,由于密钥是通过SHA-256kCCKeySizeAES256
派生的,因此应指定密钥大小
C#正在使用Rijndael,因此必须确保指定的块大小为128,这是AES支持的唯一块大小
只要你得到相同的输入,输出就会匹配。它们是选项(模式和填充)、键、键大小、数据和iv。用十六进制检查它们
提供测试向量:所有三个的密钥、输入数据和输出数据均为十六进制,因此我们可以测试代码。显然您使用的不是AES-128(obj-c),而是AES-256,因为密钥是从SH-A256的密码派生的
rawSecretKey
是IV的字节数组,因此名称有误导性。密码的密钥派生需要像PBKDF2那样具有一百万次迭代和一个随机salt。此外,您没有使用HMAC验证密文。如果是填充问题,则只有最后一个块不正确。从本质上讲,PKCS5Padding和PKCS7Padding是相同的,只是PKCS7Padding的文档允许更大的块大小。显然,您使用的不是AES-128(obj-c),而是AES-256,因为密钥来自SH-A256的密码rawSecretKey
是IV的字节数组,因此名称有误导性。密码的密钥派生需要像PBKDF2那样具有一百万次迭代和一个随机salt。此外,您没有使用HMAC验证密文。如果是填充问题,则只有最后一个块不正确。PKCS5Padding和PKCS7Padding本质上是相同的,只是PKCS7Padding的文档允许更大的块大小。我应该在哪里更改?我可以看到kCCBlockSizeAES128的多次出现,您在需要块大小的情况下使用它,kCCBlockSizeAES128
,在需要密钥的情况下使用它。请参阅CCCrypt文档(头文件)。我应该在哪里更改?我可以看到kCCBlockSizeAES128的多次出现,您在需要块大小的情况下使用它,kCCBlockSizeAES128
,在需要密钥的情况下使用它。请参阅CCCrypt文档(头文件)。
public class Crypto {
public static final String TAG = Crypto.class.getSimpleName();
// Replace me with a 16-byte key, share between Java and C#
private static Cipher aesCipher;
private static SecretKey secretKey;
private static IvParameterSpec ivParameterSpec;
private static String CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding";
private static String CIPHER_ALGORITHM = "AES";
private static byte[] rawSecretKey = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
private static String MESSAGEDIGEST_ALGORITHM = "SHA-256";
public Crypto(String passphrase) {
byte[] passwordKey = encodeDigest(passphrase);
try {
aesCipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "No such algorithm " + CIPHER_ALGORITHM, e);
} catch (NoSuchPaddingException e) {
Log.e(TAG, "No such padding PKCS5", e);
}
secretKey = new SecretKeySpec(passwordKey, CIPHER_ALGORITHM);
ivParameterSpec = new IvParameterSpec(rawSecretKey);
}
public byte[] decrypt(byte[] clearData) {
try {
aesCipher.init(Cipher.DECRYPT_MODE, secretKey);
} catch (InvalidKeyException e) {
e.printStackTrace();
}
byte[] decryptedData;
try {
decryptedData = aesCipher.doFinal(clearData);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
return null;
} catch (BadPaddingException e) {
e.printStackTrace();
return null;
}
return decryptedData;
}
public String decryptAsBase64(byte[] clearData) throws IOException {
byte[] decryptedData = decrypt(clearData);
return new String(Base64New.decode(decryptedData));
}
public String encryptAsBase64(byte[] clearData) {
byte[] encryptedData = encrypt(clearData);
return Base64New.encodeBytes(encryptedData);
}
public byte[] encrypt(byte[] clearData) {
try {
aesCipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
} catch (InvalidKeyException e) {
Log.e(TAG, "Invalid key", e);
return null;
} catch (InvalidAlgorithmParameterException e) {
Log.e(TAG, "Invalid algorithm " + CIPHER_ALGORITHM, e);
return null;
}
byte[] encryptedData;
try {
encryptedData = aesCipher.doFinal(clearData);
} catch (IllegalBlockSizeException e) {
Log.e(TAG, "Illegal block size", e);
return null;
} catch (BadPaddingException e) {
Log.e(TAG, "Bad padding", e);
return null;
}
return encryptedData;
}
private byte[] encodeDigest(String text) {
MessageDigest digest;
try {
digest = MessageDigest.getInstance(MESSAGEDIGEST_ALGORITHM);
return digest.digest(text.getBytes());
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "No such algorithm " + MESSAGEDIGEST_ALGORITHM, e);
}
return null;
}
}
+ (NSString*)encryptBase64String:(NSString*)string keyString:(NSString*)keyString separateLines:(BOOL)separateLines
{
const unsigned char rawSectret[] = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00};
NSData* rawScretdata = [NSData dataWithBytes:rawSectret length:kCCBlockSizeAES128];
NSString *shaKeyString = [self sha256HashFor:keyString];
NSData *sourceData = [string dataUsingEncoding:NSUTF8StringEncoding];
NSString *ivString = [[NSString alloc] initWithData:rawScretdata
encoding:NSUTF8StringEncoding];
NSData* Outdata = [sourceData AES128EncryptedDataWithKey:shaKeyString iv:ivString];
NSString *encodedString = [Outdata base64EncodedStringWithSeparateLines:separateLines];
return encodedString;
}
- (NSData *)AES128Operation:(CCOperation)operation key:(NSString *)key iv:(NSString *)iv
{
char keyPtr[kCCKeySizeAES128 + 1];
bzero(keyPtr, sizeof(keyPtr));
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
char ivPtr[kCCBlockSizeAES128 + 1];
bzero(ivPtr, sizeof(ivPtr));
if (iv) {
[iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
}
NSUInteger dataLength = [self length];
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(operation,
kCCAlgorithmAES128,
kCCOptionPKCS7Padding,
keyPtr,
kCCBlockSizeAES128,
ivPtr,
[self bytes],
dataLength,
buffer,
bufferSize,
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free(buffer);
return nil;
}