我有一个从Java服务器发送的公钥 . 在解码和删除ASN.1标头之前,base64编码的字符串匹配 . 我使用 SecItemAdd
将公钥存储在钥匙串中 .
所以我在iOS端使用 SecKeyEncrypt
,在Java端使用 Cipher
.
我正在加密的是加密我的实际数据的对称AES密钥,因此密钥长度为16个字节 . 当简单地对base64进行编码时,一切正常,所以我知道这个RSA加密有问题 .
这是我的iOS调用示例:
OSStatus sanityCheck = SecKeyEncrypt(publicKey,
kSecPaddingPKCS1,
(const uint8_t *) [incomingData bytes],
keyBufferSize,
cipherBuffer,
&cipherBufferSize
);
这是我的Java调用的一个例子:
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;
}
我尝试了很多组合,但没有任何效果 .
-
iOS:PKCS1,Java:RSA / ECB / PKCS1Padding
-
iOS:PKCS1,Java:RSA
-
iOS:PKCS1,Java:RSA / None / PKCS1Padding(抛出
org.bouncycastle.crypto.DataLengthException: input too large for RSA cipher.
) -
iOS:OAEP,Java:RSA / ECB / OAEPWithSHA-1AndMGF1Padding
-
iOS:OAEP,Java:RSA / ECB / OAEPWithSHA-256AndMGF1Padding
我也尝试使用内部Java提供程序以及BouncyCastle提供程序 . 每次都会抛出 javax.crypto.BadPaddingException
,但每个组合的消息都不同 . 有些显示 Data must start with zero
,有些显示 Message is larger than modulus
.
iOS: PKCS1, Java: RSA
不会抛出异常,但生成的解密 byte[]
数组应该是长度为16,但它正确地被删除了.1563378_t .
有人可以帮忙吗?
*** EDIT ***
当我进行更多测试时,我遇到了这个页面(http://javadoc.iaik.tugraz.at/iaik_jce/current/iaik/pkcs/pkcs1/RSACipher.html),它基本上告诉我 RSA == RSA/None/PKCS1Padding
. 解密工作的意义是没有异常,但我仍然得到一个解密密钥,其byte []长度为256而不是长度为16 .
另一个兴趣点 . 似乎如果Java服务器具有从iOS设备生成并使用 Cipher.getInstance("RSA")
加密的公钥,则电话能够使用RSA / PKCS1正确解码消息 .
*** EDIT 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(@"Problem adding public key to keychain");
return;
}
CFRelease(persistPeer);
保存成功,但最终结果是相同的:解密的AES密钥仍然是256字节长而不是16字节 .
2 回答
我有同样的问题 . 使用
kSecPaddingNone
,但 doesn't 与kSecPaddingPKCS1
一起使用,Java代码中的任何PKCS1
组合 .但是,没有填充使用它并不是一个好主意 .
因此,在iOS上,将
kSecPaddingNone
替换为kSecPaddingOAEP
并在Java代码中使用RSA/NONE/OAEPWithSHA1AndMGF1Padding
. 这对我有用 .使用RSA / None / NoPadding的解决方案
好的,所以我让它工作但 WITHOUT PADDING . 这部分让我非常沮丧,我将其留给其他人试图帮助 . 也许我到目前为止发现了什么 .
TL;DR :使用最少的属性将密钥保存到钥匙串,以使检索更简单 . 使用
SecKeyEncrypt
进行加密,但使用kSecPaddingNone
. 用BouncyCastle和算法RSA/None/NoPadding
解密Java端 .从Java向iOS发送RSA公钥
使用X.509证书
我想验证是否直接发送公钥,剥离ASN.1标头并保存实际上正在做它应该做的事情 . 所以我考虑将公钥作为证书发送 . 我想赞扬David Benko提供的加密库(https://github.com/DavidBenko/DBTransitEncryption)帮助我进行了证书转换 . 我没有't actually use his library because 1. I'已经使用
RNCryptor
/JNCryptor
进行我的AES加密和2.他没有想要这样做 . 对于那些感兴趣并希望采用这种方法的人,这是我在Java端创建证书然后将该证书转换为iOS上的公钥的代码:*重要说明:请将
e.printStackTrace()
替换为真实的日志记录 . 我只用它来测试和 生产环境 中的 NOT .Java :
Obj-C :
使用RSA公钥
它_1163412_ t需要将公钥作为证书发送 . 实际上,在发现公钥被错误保存后(见下文),我还原了这段代码并将公钥保存到我的设备中 . 您需要删除其中一篇博文中提到的
ASN.1
Headers . 该代码在此处重新发布(为清晰起见而格式化) .所以我会像这样保存密钥:
将RSA公钥保存到钥匙串
这令人抓狂 . 原来,即使我将钥匙保存到钥匙串中,我检索到的不是我放入的钥匙!当我将保存的base64密钥与我用来加密AES密钥的base64密钥进行比较时,我意外地发现了这一点 . 所以我发现最好简化保存密钥时使用的NSDictionary . 这是我最终得到的:
要检索我的密钥,我使用以下方法:
在一天结束时,我只能在没有填充的情况下使其工作 . 我不确定为什么
BouncyCastle
无法删除填充,所以如果有人有任何见解,请告诉我 .这是我的加密代码(由David Benko修改):
这是我在Java方面解密的方式: