iOS Objective-C中的AES/CBC/PKCS5Padding与Android结果不同。

5

我正在使用Android应用程序中的AES/CBC/PKCS5Padding。 代码类似于 -

    private static String TRANSFORMATION = "AES/CBC/PKCS5Padding";

    private static String ALGORITHM = "AES";
    private static String DIGEST = "MD5";

    private static Cipher cipher;
    private static SecretKey password;
    private static IvParameterSpec IVParamSpec;
    private final static String pvtkey="GDNBCGDRFSC$%#%=";

    //16-byte private key
    private static byte[] IV = pvtkey.getBytes();

    public PassWordEncryptor() {
        try {

            //Encode digest
            MessageDigest digest;           
            digest = MessageDigest.getInstance(DIGEST);            
            password = new SecretKeySpec(digest.digest(pvtkey.getBytes()), ALGORITHM);

            //Initialize objects
            cipher = Cipher.getInstance(TRANSFORMATION);
            IVParamSpec = new IvParameterSpec(IV);

        } catch (NoSuchAlgorithmException e) {
            Log.i(Lams4gApp.TAG, "No such algorithm " + ALGORITHM);
        } catch (NoSuchPaddingException e) {
            System.out.println( "No such padding PKCS7"+ e);
        }
    }
    /**
    Encryptor.

    @text String to be encrypted
    @return Base64 encrypted text

    */
    public String encrypt(byte[] text) {

        byte[] encryptedData;

        try {

            cipher.init(Cipher.ENCRYPT_MODE, password, IVParamSpec);
            encryptedData = cipher.doFinal(text);

        } catch (InvalidKeyException e) {
            System.out.println( "Invalid key  (invalid encoding, wrong length, uninitialized, etc)."+ e);
            return null;
        } catch (InvalidAlgorithmParameterException e) {
            System.out.println( "Invalid or inappropriate algorithm parameters for " + ALGORITHM+ e);
            return null;
        } catch (IllegalBlockSizeException e) {
            System.out.println( "The length of data provided to a block cipher is incorrect"+ e);
            return null;
        } catch (BadPaddingException e) {
            System.out.println( "The input data but the data is not padded properly."+ e);
            return null;
        }               
        return Base64.encodeToString(encryptedData,Base64.DEFAULT);

    }

我希望你能够提供iOS Objective C的类似代码。加密和解密结果应该与Android相同。请提供Objective C的相同算法。

我正在使用以下iOS代码-

- (void)viewDidLoad {
    [super viewDidLoad];

    NSData *encodingData=[self encrypt:[@"slapkh"
                                          dataUsingEncoding:NSUTF8StringEncoding]];
    NSString *encodingResult = [NSString base64StringFromData:encodingData length:[encodingData length]];


}

- (NSData *) encrypt:(NSData *) plainText {
            return [self transform:kCCEncrypt data:plainText];
}

- (NSData *) decrypt:(NSData *) cipherText {
            return [self transform:kCCDecrypt data:cipherText];
}
- (NSData *) transform:(CCOperation) encryptOrDecrypt data:(NSData *) inputData {
     Cipher* cipher = [[Cipher alloc]initWithKey:@"GDNBCGDRFSC$%#%="];
    NSString* Key = cipher.cipherKey;

    // kCCKeySizeAES128 = 16 bytes
   // CC_MD5_DIGEST_LENGTH = 16 bytes
   NSData* secretKey = [Cipher md5:Key];

   CCCryptorRef cryptor = NULL;
   CCCryptorStatus status = kCCSuccess;

   uint8_t iv[kCCBlockSizeAES128];
   memset((void *) iv, 0x0, (size_t) sizeof(iv));

   status = CCCryptorCreate(encryptOrDecrypt,
                            kCCAlgorithmAES128,kCCOptionPKCS7Padding,
                            [secretKey bytes], kCCKeySizeAES128, iv, &cryptor);

   if (status != kCCSuccess) {
       return nil;
   }

   size_t bufsize = CCCryptorGetOutputLength(cryptor, (size_t)[inputData length],
                                             true);

   void * buf = malloc(bufsize * sizeof(uint8_t));
   memset(buf, 0x0, bufsize);

      size_t bufused = 0;
      size_t bytesTotal = 0;

      status = CCCryptorUpdate(cryptor, [inputData bytes], (size_t)[inputData length],
                               buf, bufsize, &bufused);

      if (status != kCCSuccess) {
          free(buf);
          CCCryptorRelease(cryptor);
          return nil;
      }

      bytesTotal += bufused;

      status = CCCryptorFinal(cryptor, buf + bufused, bufsize - bufused, &bufused);

      if (status != kCCSuccess) {
          free(buf);
          CCCryptorRelease(cryptor);
          return nil;
      }

      bytesTotal += bufused;

      CCCryptorRelease(cryptor);

      return [NSData dataWithBytesNoCopy:buf length:bytesTotal];
  }

但是Android和iOS的结果不同,如下所示:

加密文本: slapkh 密钥: GDNBCGDRFSC$%#%=

Android结果:jN2p1yAdBJLRmoHq+k9KtA==\n

iOS结果:tbaSJFv5mGyZ9t/+kOw+gg==


1
首先,您使用不同的IV:在Android代码中,它是pvtkey.getBytes(),但在iOS代码中,它只是零。 - Roman
可能是 AES/CBC/PKCS5Padding in iOS objective c 的重复问题。 - Roman
@Roman 那个问题被关闭是因为一开始缺少代码,虽然后来添加了代码,但问题并没有重新开放,所以这是一个有效的问题。 - zaph
感谢@Roman的回复。但我需要两者都得到相同的结果,因为web也在运行相同的算法,并使用相同的密钥验证身份。请建议我加密和解密的确切等效方法,以便我可以在Android和iOS上获得相同的结果。 - Manish
@zaph:有没有什么方法可以在Android和iOS上获得类似的结果。我已经在问题中发布了两个代码。请帮忙。 - Manish
我收到了“Cipher”未声明的标识符错误..我需要安装哪个pod?或者需要添加任何框架吗? - Jayprakash Dubey
1个回答

2
最初的回答:

在处理这个问题一段时间后,我使用以下代码在ANDROID(java)和IOS (Objc)上成功使用AES:

ANDROID CODE

    import java.io.IOException;
    import java.security.InvalidAlgorithmParameterException;
    import java.security.InvalidKeyException;
    import java.security.NoSuchAlgorithmException;

    import javax.crypto.BadPaddingException;
    import javax.crypto.Cipher;
    import javax.crypto.IllegalBlockSizeException;
    import javax.crypto.NoSuchPaddingException;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;

    public class SecurityUtils {

        private static final String ALGORITHM = "AES";
        private static final String MODE = "AES";
        private static final String IV = "AEE0715D0778A4E4";
        private static final String KEY= "9336365521W5F092BB5909E8E033BC69";

        public static  String encrypt(String value ) throws NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidKeyException {
            SecretKeySpec secretKeySpec = new SecretKeySpec(KEY.getBytes(), ALGORITHM);
            Cipher cipher = Cipher.getInstance(MODE);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(IV.getBytes()));
            byte[] values = cipher.doFinal(value.getBytes());
            return Base64.encodeBytes(values);
        }

        public static  String decrypt(String value) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidKeyException {
            byte[] values = Base64.decode(value);
            SecretKeySpec secretKeySpec = new SecretKeySpec(KEY.getBytes(), ALGORITHM);
            Cipher cipher = Cipher.getInstance(MODE);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(IV.getBytes()));
            return new String(cipher.doFinal(values));
        }
    }

最初的回答

测试安卓

   try {
        String encrypted = SecurityUtils.encrypt("My Secret Text");
        String decrypted = SecurityUtils.decrypt(encrypted);
        Log.e("encrypted", encrypted);
        Log.e("decrypted", decrypted);
    }catch(Exception ex){
        Log.e("AES", ex.getMessage());
    }

iOS代码

头文件

#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonCryptor.h>

NS_ASSUME_NONNULL_BEGIN

@interface SecurityUtils : NSObject

+ (NSString *)encrypt:(NSString *)plainText error:(NSError **)error;
+ (NSString *)decrypt:(NSString *)plainText error:(NSError **)error;

@end

NS_ASSUME_NONNULL_END

实现文件

NSString *const IV = @"AEE0515D0B08A4E4";
NSString *const KEY =  @"9336565521E5F082BB5929E8E033BC69";


#import "SecurityUtils.h"


@implementation SecurityUtils


+ (NSString *)encrypt:(NSString *)plainText error:(NSError **)error {
    NSMutableData *result =  [SecurityUtils doAES:[plainText dataUsingEncoding:NSUTF8StringEncoding] context: kCCEncrypt error:error];
    return [result base64EncodedStringWithOptions:0];
}


+ (NSString *)decrypt:(NSString *)encryptedBase64String error:(NSError **)error {
    NSData *dataToDecrypt = [[NSData alloc] initWithBase64EncodedString:encryptedBase64String options:0];
    NSMutableData *result = [SecurityUtils doAES:dataToDecrypt context: kCCDecrypt error:error];
    return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
    
}

+ (NSMutableData *)doAES:(NSData *)dataIn context:(CCOperation)kCCEncrypt_or_kCCDecrypt error:(NSError **)error {
        CCCryptorStatus ccStatus   = kCCSuccess;
        size_t          cryptBytes = 0;
        NSMutableData  *dataOut    = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeBlowfish];
        NSData *key =[KEY dataUsingEncoding:NSUTF8StringEncoding];
        NSData *iv = [IV dataUsingEncoding:NSUTF8StringEncoding];
        
        ccStatus = CCCrypt( kCCEncrypt_or_kCCDecrypt,
                           kCCAlgorithmAES,
                           kCCOptionPKCS7Padding,
                           key.bytes,
                           key.length,
                           (iv)?nil:iv.bytes,
                           dataIn.bytes,
                           dataIn.length,
                           dataOut.mutableBytes,
                           dataOut.length,
                           &cryptBytes);
        
        if (ccStatus == kCCSuccess) {
            dataOut.length = cryptBytes;
        }
        else {
            if (error) {
                *error = [NSError errorWithDomain:@"kEncryptionError"
                                             code:ccStatus
                                         userInfo:nil];
            }
            dataOut = nil;
        }
        
        return dataOut;
}


@end

iOS测试

最初的回答
NSError *error;
NSString *encrypted = [SecurityUtils encrypt:@"My Secret Text" error:&error];
NSLog(@"encrypted: %@",encrypted);
NSLog(@"decrypted: %@",[SecurityUtils decrypt:encrypted error:&error]);

最初的回答
最后,测试的输出:
安卓输出:
2019-05-16 21:35:01.215 4920-4920/br.com.my.app E/encrypted: EJ41am5W1k6fA7ygFjTSEw==
2019-05-16 21:35:01.215 4920-4920/br.com.my.app E/decrypted: My Secret Text

IOS OUTPUT

2019-05-16 21:38:02.947043-0300 MyApp[63392:1590665] encrypted: EJ41am5W1k6fA7ygFjTSEw==
2019-05-16 21:38:02.947270-0300 MyApp[63392:1590665] decrypted: My Secret Text

我的GitHub仓库中有这个例子,可以访问此链接。最初的回答。

这种AES的方法与原始的Android代码不匹配,因此这并不是一个真正的答案。您的代码也存在重大安全问题,因此不应将其用作原始代码的替代品(尽管原始代码也存在安全问题,但它们有点不同)。在CBC中重复使用相同的密钥+IV组合是不安全的。Android和iOS加密匹配实际上表明存在安全漏洞。在正确的加密系统中,相同的密码+消息的输出每次都会不同。此外,您的密钥长度比您想要的要短。 - Rob Napier
嗨@RobNapier。我的回答并没有旨在涵盖安全问题。只是解决了一个技术兼容性问题,这也是发布的问题的目标。 - Julian Corrêa
不错!@Maveňツ,Base64类来自:https://sourceforge.net/projects/iharder/files/base64/2.3/然而,Java 1.8已经有了一个Base64类,请参见:https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html我现在已经更新了我的存储库,其中包含Base64类。快去看看吧!谢谢。 - Julian Corrêa
@JulianCorrêa 完成了,现在我的安卓设备可以工作了,但长度超过15个字符的内容无法解密,并抛出错误填充异常。你有什么想法吗? - Maveňツ
这是一个比较特定的情况。我认为最好我们通过电子邮件交流。如果我们找到解决方案,你可以回到这里并发布它。我的电子邮件-> sof@juliancorrea.com.br - Julian Corrêa
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接