RSA:iOS 加密,Java 解密 [英] RSA: encrypt in iOS, decrypt in Java

查看:30
本文介绍了RSA:iOS 加密,Java 解密的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个从 Java 服务器发送的公钥.在我解码并去除 ASN.1 标头之前,base64 编码的字符串匹配.我使用 SecItemAdd 将公钥存储在钥匙串中.

所以我尝试使用公钥加密数据并使用 Java 中的私钥解密.我在 iOS 端使用 SecKeyEncrypt,在 Java 端使用 Cipher.

我正在加密的是对我的实际数据进行加密的对称 AES 密钥,因此密钥长度为 16 字节.当简单地对密钥进行 base64 编码时,一切正常,所以我知道这个 RSA 加密有问题.

以下是我的 iOS 调用示例:

OSStatus sanityCheck = SecKeyEncrypt(publicKey,kSecPaddingPKCS1,(const uint8_t *) [传入数据字节],键缓冲区大小,密码缓冲区,&cipherBufferSize);

这是我的 Java 调用示例:

public static byte[]decryptMessage(byte[]消息,PrivateKeyprivateKey,String算法){if (message == null || privateKey == null) {返回空;}Cipher cipher = createCipher(Cipher.DECRYPT_MODE, privateKey, algorithm, false);如果(密码==空){返回空;}尝试 {返回 cipher.doFinal(message);}捕获(非法块大小异常 e){e.printStackTrace();//要更改catch语句的主体,请使用File |设置 |文件模板.返回空;}抓住(BadPaddingException e){e.printStackTrace();//要更改catch语句的主体,请使用File |设置 |文件模板.返回空;}}私有静态密码 createCipher(整数模式,密钥加密密钥,字符串算法,布尔值 useBouncyCastle){密码密码;尝试 {如果(使用BouncyCastle){Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());cipher = Cipher.getInstance(algorithm, "BC");}别的 {cipher = Cipher.getInstance(算法);}}捕获(NoSuchAlgorithmException e){e.printStackTrace();//要更改catch语句的主体,请使用File |设置 |文件模板.返回空;}抓住(NoSuchPaddingException e){e.printStackTrace();//要更改catch语句的主体,请使用File |设置 |文件模板.返回空;}抓住(NoSuchProviderException e){e.printStackTrace();返回空;}尝试 {cipher.init(模式,加密密钥);}catch (InvalidKeyException e) {e.printStackTrace();//要更改catch语句的主体,请使用File |设置 |文件模板.返回空;}返回密码;}

我尝试了很多组合,但没有任何效果.

  • iOS:PKCS1,Java:RSA/ECB/PKCS1Padding
  • iOS:PKCS1,Java:RSA
  • iOS:PKCS1,Java:RSA/None/PKCS1Padding(抛出 org.bouncycastle.crypto.DataLengthException:输入对于 RSA 密码来说太大.)
  • iOS:OAEP,Java:RSA/ECB/OAEPWithSHA-1AndMGF1Padding
  • iOS:OAEP,Java:RSA/ECB/OAEPWithSHA-256AndMGF1Padding

我还尝试使用内部 Java 提供程序以及 BouncyCastle 提供程序.javax.crypto.BadPaddingException 每次都会被抛出,但每种组合的消息都不同.有的显示数据必须从零开始,有的则显示Message is大于模数.

iOS: PKCS1, Java: RSA 不会抛出异常,但是解密后的 byte[] 数组的长度应该是 16,但它的长度是 256,这意味着填充没有被正确剥离.

有人可以帮忙吗?

*** 编辑 ***

当我进行更多测试时,我遇到了这个页面(http://javadoc.iaik.tugraz.at/iaik_jce/current/iaik/pkcs/pkcs1/RSACipher.html),它本质上告诉我 RSA == RSA/None/PKCS1Padding.解密在没有例外的意义上有效,但我仍然得到一个解密的密钥,其字节 [] 的长度为 256 而不是长度 16.

另一个兴趣点.似乎如果 Java 服务器具有从 iOS 设备生成并使用 Cipher.getInstance("RSA") 加密的公钥,则手机能够使用 RSA/PKCS1 正确解码消息.

*** 编辑 2 ***

我已经查看了这些教程,并在 iOS 端再次查看了我的代码:

据我所知,我的代码一切正常.一个显着的区别在于我保存密钥的方式,所以我尝试以另一种方式保存它:

 OSStatus error = noErr;CFTypeRef persistPeer = NULL;NSMutableDictionary * keyAttr = [[NSMutableDictionary alloc] init];keyAttr[(__bridge id) kSecClass] = (__bridge id) kSecClassKey;keyAttr[(__bridge id) kSecAttrKeyType] = (__bridge id) kSecAttrKeyTypeRSA;keyAttr[(__bridge id) kSecAttrApplicationTag] = [secKeyWrapper getKeyTag:serverPublicKeyTag];keyAttr[(__bridge id) kSecValueData] = strippedServerPublicKey;keyAttr[(__bridge id) kSecReturnPersistentRef] = @YES;error = SecItemAdd((__bridge CFDictionaryRef) keyAttr, (CFTypeRef *)&persistPeer);if (persistPeer == nil || ( error != noErr && error != errSecDuplicateItem)) {NSLog(@"向钥匙串添加公钥的问题");返回;}CFRelease(persistPeer);

保存成功,但最终结果是一样的:解密后的 AES 密钥仍然是 256 字节长,而不是 16 字节.

解决方案

我遇到了同样的问题.与 kSecPaddingNone 一起工作,但 kSecPaddingPKCS1 和 Java 代码中的任何 PKCS1 组合一起工作.>

但是,在没有填充的情况下使用它并不是一个好主意.

因此,在 iOS 上,将 kSecPaddingNone 替换为 kSecPaddingOAEP 并在您的 Java 代码中使用 RSA/NONE/OAEPWithSHA1AndMGF1Padding.这对我有用.

I have a public key that's sent from a Java server. The base64 encoded strings match before I decode and strip out the ASN.1 headers. I store the public key in the keychain with SecItemAdd.

So I'm trying to encrypt the data using the public key and decrypt it with the private key in Java. I'm using SecKeyEncrypt on the iOS side and Cipher on the Java side.

What I'm encrypting is the symmetric AES key that encrypts my actual data, so the key length is 16 bytes. When simply base64 encoding the key, everything works, so I know something is wrong with this RSA encryption.

Here's an example of my iOS call:

OSStatus sanityCheck = SecKeyEncrypt(publicKey,
        kSecPaddingPKCS1,
        (const uint8_t *) [incomingData bytes],
        keyBufferSize,
        cipherBuffer,
        &cipherBufferSize
);

Here's an example of my Java call:

public static byte[] decryptMessage (byte[] message, PrivateKey privateKey, String algorithm) {
    if (message == null || privateKey == null) {
        return null;
    }
    Cipher cipher = createCipher(Cipher.DECRYPT_MODE, privateKey, algorithm, false);
    if (cipher == null) {
        return null;
    }

    try {
        return cipher.doFinal(message);
    }
    catch (IllegalBlockSizeException e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        return null;
    }
    catch (BadPaddingException e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        return null;
    }
}

private static Cipher createCipher (int mode, Key encryptionKey, String algorithm, boolean useBouncyCastle) {
    Cipher cipher;

    try {
        if (useBouncyCastle) {
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            cipher = Cipher.getInstance(algorithm, "BC");
        }
        else {
            cipher = Cipher.getInstance(algorithm);
        }
    }
    catch (NoSuchAlgorithmException e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        return null;
    }
    catch (NoSuchPaddingException e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        return null;
    }
    catch (NoSuchProviderException e) {
        e.printStackTrace();
        return null;
    }

    try {
        cipher.init(mode, encryptionKey);
    }
    catch (InvalidKeyException e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        return null;
    }

    return cipher;
}

I've tried so many combinations and nothing has worked.

  • iOS: PKCS1, Java: RSA/ECB/PKCS1Padding
  • iOS: PKCS1, Java: RSA
  • iOS: PKCS1, Java: RSA/None/PKCS1Padding (throws org.bouncycastle.crypto.DataLengthException: input too large for RSA cipher.)
  • iOS: OAEP, Java: RSA/ECB/OAEPWithSHA-1AndMGF1Padding
  • iOS: OAEP, Java: RSA/ECB/OAEPWithSHA-256AndMGF1Padding

I've also tried using the internal Java provider as well as the BouncyCastle provider. The javax.crypto.BadPaddingException gets thrown every time, but the message is different for each combination. Some show Data must start with zero, while others are Message is larger than modulus.

The iOS: PKCS1, Java: RSA doesn't throw an exception, but the resulting decrypted byte[] array should be length 16, but it's length 256, which means the padding isn't correctly stripped out.

Can someone help?

*** EDIT ***

As I'm doing more testing, I came across this page (http://javadoc.iaik.tugraz.at/iaik_jce/current/iaik/pkcs/pkcs1/RSACipher.html), which essentially tells me that RSA == RSA/None/PKCS1Padding. The decryption works in the sense that there are no exceptions, but I'm still getting a decrypted key whose byte[] is length 256 instead of length 16.

Another point of interest. It seems that if the Java server has the public key generated from the iOS device and encrypted using Cipher.getInstance("RSA"), the phone is able to decode the message correctly with RSA/PKCS1.

*** EDIT 2 ***

I have looked at these tutorials and looked through my code again on the iOS side:

As far as I can tell, my code is doing everything correctly. One significant difference was in how I was saving the key, so I tried saving it the other way:

    OSStatus error = noErr;
    CFTypeRef persistPeer = NULL;

    NSMutableDictionary * keyAttr = [[NSMutableDictionary alloc] init];

    keyAttr[(__bridge id) kSecClass] = (__bridge id) kSecClassKey;
    keyAttr[(__bridge id) kSecAttrKeyType] = (__bridge id) kSecAttrKeyTypeRSA;
    keyAttr[(__bridge id) kSecAttrApplicationTag] = [secKeyWrapper getKeyTag:serverPublicKeyTag];
    keyAttr[(__bridge id) kSecValueData] = strippedServerPublicKey;
    keyAttr[(__bridge id) kSecReturnPersistentRef] = @YES;

    error = SecItemAdd((__bridge CFDictionaryRef) keyAttr, (CFTypeRef *)&persistPeer);

    if (persistPeer == nil || ( error != noErr && error != errSecDuplicateItem)) {
        NSLog(@"Problem adding public key to keychain");
        return;
    }

    CFRelease(persistPeer);

That save was successful, but the end result was the same: the decrypted AES key was still 256 bytes long instead of 16 bytes.

解决方案

I had same problem. Does work with kSecPaddingNone, but doesn't work with kSecPaddingPKCS1 with any PKCS1 combination in Java code.

But, it's not good idea to use it without padding.

So, on iOS, replace kSecPaddingNone with kSecPaddingOAEP and use RSA/NONE/OAEPWithSHA1AndMGF1Padding in your Java code. This does work for me.

这篇关于RSA:iOS 加密,Java 解密的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆