【发布时间】:2016-01-06 02:38:20
【问题描述】:
我正在尝试在 iOS 中使用 AES/CBC 加密。解密由 C# 完成。在 Android 中由 Java 完成的加密工作正常。我收到错误“填充无效,无法删除。”在尝试使用 iOS 代码时。请帮忙。
请在下面找到 C#、Java 和 Objective C 代码。
C#代码:
namespace InternetMobileService
{
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
public class EncryptDecrypt
{
//// Replace me with a 16-byte key, share between Java and C#
/// <summary>
/// rawSecretKey values
/// </summary>
private static byte[] rawSecretKey = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
/// <summary>
/// variable field
/// </summary>
private ICryptoTransform rijndaelDecryptor;
/// <summary>
/// Initializes a new instance of the <see cref="EncryptDecrypt"/> class.
/// </summary>
/// <param name="passphrase">pass phrase</param>
public EncryptDecrypt(string passphrase)
{
byte[] passwordKey = EncodeDigest(passphrase);
RijndaelManaged rijndael = new RijndaelManaged();
this.rijndaelDecryptor = rijndael.CreateDecryptor(passwordKey, rawSecretKey);
}
public static byte[] EncodeDigest(string text)
{
SHA256CryptoServiceProvider x = new System.Security.Cryptography.SHA256CryptoServiceProvider();
byte[] data = Encoding.ASCII.GetBytes(text);
return x.ComputeHash(data);
}
public static string Encrypt(string plainText)
{
using (RijndaelManaged myRijndael = new RijndaelManaged())
{
byte[] passwordKey = EncodeDigest(System.Configuration.ConfigurationManager.AppSettings["ConstructorKey"].ToString());
byte[] encrypted = EncryptStringToBytes(plainText, passwordKey, rawSecretKey);
return Convert.ToBase64String(encrypted);
}
}
public static byte[] EncryptStringToBytes(string plainText, byte[] key, byte[] iV)
{
if (plainText == null || plainText.Length <= 0)
{
throw new ArgumentNullException("plainText");
}
if (key == null || key.Length <= 0)
{
throw new ArgumentNullException("key");
}
if (iV == null || iV.Length <= 0)
{
throw new ArgumentNullException("key");
}
byte[] encrypted;
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = key;
rijAlg.IV = iV;
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
using (MemoryStream memorystreamEncrypt = new MemoryStream())
{
using (CryptoStream cryptostreamEncrypt = new CryptoStream(memorystreamEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter streamwriterEncrypt = new StreamWriter(cryptostreamEncrypt))
{
streamwriterEncrypt.Write(plainText);
}
encrypted = memorystreamEncrypt.ToArray();
}
}
}
return encrypted;
}
public string Decrypt(byte[] encryptedData)
{
byte[] newClearData = this.rijndaelDecryptor.TransformFinalBlock(encryptedData, 0, encryptedData.Length);
return Encoding.ASCII.GetString(newClearData);
}
public string DecryptFromBase64(string encryptedBase64)
{
return this.Decrypt(Convert.FromBase64String(encryptedBase64));
}
}
}
Java 代码:
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;
}
}
Obj C 代码:
+ (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;
}
【问题讨论】:
-
您显然没有使用 AES-128 (obj-c),而是使用 AES-256,因为密钥是从带有 SH-A256 的密码中派生的。
rawSecretKey是 IV 的字节数组,因此名称具有误导性。从密码派生的密钥需要比 PBKDF2 更强大,具有一百万次迭代和随机盐。此外,您没有使用 HMAC 验证您的密文。 -
如果是填充问题,只有最后一个块是不正确的。本质上 PKCS5Padding 和 PKCS7Padding 是相同的,只是 PKCS7Padding 的文档允许更大的块大小。
标签: c# android ios encryption aes