将 NSData 转换为 SecKeyRef
Posted
技术标签:
【中文标题】将 NSData 转换为 SecKeyRef【英文标题】:Converting NSData to SecKeyRef 【发布时间】:2014-02-14 00:22:04 【问题描述】:我有一个从远程服务器收集的公钥,我想使用该公钥执行 RSA 加密。但问题是我将公钥数据作为缓冲区中的字节数组获取。我可以将其转换为 NSData,但我无法转换为 SecKeyRef,因此我可以继续进行加密。我的加密代码是这样的:
+(NSString *)encryptRSA:(NSString *)plainTextString withKey:(SecKeyRef)publicKey
size_t cipherBufferSize = SecKeyGetBlockSize(publicKey);
uint8_t *cipherBuffer = malloc(cipherBufferSize);
uint8_t *nonce = (uint8_t *)[plainTextString UTF8String];
SecKeyEncrypt(publicKey,
kSecPaddingOAEP,
nonce,
strlen( (char*)nonce ),
&cipherBuffer[0],
&cipherBufferSize);
NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:cipherBufferSize];
return [encryptedData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
如您所见,我需要 SecKeyRef 对象类型来完成我的加密。但我的 RSA 公钥在 NSData 变量中。那么如何 对象类型。提前致谢。
【问题讨论】:
【参考方案1】:使用此功能保存您的公钥。传递您的 RAS 公钥和 peername 的任何名称。
- (void)addPeerPublicKey:(NSString *)peerName keyBits:(NSData *)publicKeyData
OSStatus sanityCheck = noErr;
CFTypeRef persistPeer = NULL;
[self removePeerPublicKey:peerName];
NSData * peerTag = [[NSData alloc] initWithBytes:(const void *)[peerName UTF8String] length:[peerName length]];
NSMutableDictionary * peerPublicKeyAttr = [[NSMutableDictionary alloc] init];
[peerPublicKeyAttr setObject:(id)kSecClassKey forKey:(id)kSecClass];
[peerPublicKeyAttr setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
[peerPublicKeyAttr setObject:peerTag forKey:(id)kSecAttrApplicationTag];
[peerPublicKeyAttr setObject:publicKeyData forKey:(id)kSecValueData];
[peerPublicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnData];
sanityCheck = SecItemAdd((CFDictionaryRef) peerPublicKeyAttr, (CFTypeRef *)&persistPeer);
if(sanityCheck == errSecDuplicateItem)
TRC_DBG(@"Problem adding the peer public key to the keychain, OSStatus == %ld.", sanityCheck );
TRC_DBG(@"SecItemAdd OSStATUS = %ld", sanityCheck);
// TRC_DBG(@"PersistPeer privatekey data after import into keychain %@", persistPeer);
persistPeer = NULL;
[peerPublicKeyAttr removeObjectForKey:(id)kSecValueData];
sanityCheck = SecItemCopyMatching((CFDictionaryRef) peerPublicKeyAttr, (CFTypeRef*)&persistPeer);
TRC_DBG(@"SecItemCopying OSStATUS = %ld", sanityCheck);
// TRC_DBG(@"SecItem copy matching returned this public key data %@", persistPeer);
// The nice thing about persistent references is that you can write their value out to disk and
// then use them later. I don't do that here but it certainly can make sense for other situations
// where you don't want to have to keep building up dictionaries of attributes to get a reference.
//
// Also take a look at SecKeyWrapper's methods (CFTypeRef)getPersistentKeyRefWithKeyRef:(SecKeyRef)key
// & (SecKeyRef)getKeyRefWithPersistentKeyRef:(CFTypeRef)persistentRef.
[peerTag release];
[peerPublicKeyAttr release];
if (persistPeer) CFRelease(persistPeer);
这是检索公钥 ref 的函数。传递相同的名称用于保存。
-(SecKeyRef)getPublicKeyReference:(NSString*)peerName
OSStatus sanityCheck = noErr;
SecKeyRef pubKeyRefData = NULL;
NSData * peerTag = [[NSData alloc] initWithBytes:(const void *)[peerName UTF8String] length:[peerName length]];
NSMutableDictionary * peerPublicKeyAttr = [[NSMutableDictionary alloc] init];
[peerPublicKeyAttr setObject:(id)kSecClassKey forKey:(id)kSecClass];
[peerPublicKeyAttr setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
[peerPublicKeyAttr setObject:peerTag forKey:(id)kSecAttrApplicationTag];
[peerPublicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey: (id)kSecReturnRef];
sanityCheck = SecItemCopyMatching((CFDictionaryRef) peerPublicKeyAttr, (CFTypeRef*)&pubKeyRefData);
[peerTag release];
[peerPublicKeyAttr release];
TRC_DBG(@"SecItemCopying OSStATUS = %ld", sanityCheck);
if(pubKeyRefData)
TRC_DBG(@"SecItem copy matching returned this publickeyref %@", pubKeyRefData);
return pubKeyRefData;
else
TRC_DBG(@"pubKeyRef is NULL");
return nil;
在 addPeerPublicKey 之前将你的公钥数据传递给这个函数
- (NSData *)stripPublicKeyHeader:(NSData *)d_key
// Skip ASN.1 public key header
if (d_key == nil) return(nil);
unsigned int len = [d_key length];
if (!len) return(nil);
unsigned char *c_key = (unsigned char *)[d_key bytes];
unsigned int idx = 0;
if (c_key[idx++] != 0x30) return(nil);
if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;
else idx++;
// PKCS #1 rsaEncryption szOID_RSA_RSA
static unsigned char seqiod[] =
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
0x01, 0x05, 0x00 ;
if (memcmp(&c_key[idx], seqiod, 15)) return(nil);
idx += 15;
if (c_key[idx++] != 0x03) return(nil);
if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;
else idx++;
if (c_key[idx++] != '\0') return(nil);
// Now make a new NSData from this buffer
return([NSData dataWithBytes:&c_key[idx] length:len - idx]);
【讨论】:
感谢您的回答,但此函数返回 nil,我检查代码 peerKeyRef 变量已用 NULL 初始化,并且在对该变量函数不执行任何操作后返回它,因此它为 NULL。 Sorry M. Salih Kocak...第一种方法只是存储公钥密钥(NSData)。第二种方法返回您的公共 seckeyref。请试试这个。 嗨,我是这样写的,但我仍然将公钥设为空:NSData* publicKeyData = [[NSData alloc]initWithBytes:publicKeyBuffer length:publicKeyLen]; [self addPeerPublicKey:@"PeerName" keyBits:publicKeyData]; SecKeyRef publicKey = [self getPublicKeyReference:@"PeerName"];
我用调试模式检查了publicKeyBuffer它有正确的值没有问题,publicKeyData也没有问题。我做错了什么我无法理解。再次感谢。
我怀疑如果您的公钥不是由 ios 生成的,那么您应该在存储公钥之前进行一些转换,然后只有您可以获得 SecKeyRef。我认为这将解决您的问题,请参考我添加的代码【参考方案2】:
希望它会起作用....
-(NSData*)convertIOSKeyToASNFormat:(NSData*)iosKey
static const unsigned char _encodedRSAEncryptionOID[15] =
/* Sequence of length 0xd made up of OID followed by NULL */
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00
;
// OK - that gives us the "BITSTRING component of a full DER
// encoded RSA public key - we now need to build the rest
unsigned char builder[15];
NSMutableData * encKey = [[[NSMutableData alloc] init] autorelease];
int bitstringEncLength;
// When we get to the bitstring - how will we encode it?
if ([iosKey length ] + 1 < 128 )
bitstringEncLength = 1 ;
else
bitstringEncLength = (([iosKey length ] +1 ) / 256 ) + 2 ;
// Overall we have a sequence of a certain length
builder[0] = 0x30; // ASN.1 encoding representing a SEQUENCE
// Build up overall size made up of -
size_t i = sizeof(_encodedRSAEncryptionOID) + 2 + bitstringEncLength +
[iosKey length];
size_t j = [self encodeLen:&builder[1] length:i];
[encKey appendBytes:builder length:j +1];
// First part of the sequence is the OID
[encKey appendBytes:_encodedRSAEncryptionOID
length:sizeof(_encodedRSAEncryptionOID)];
// Now add the bitstring
builder[0] = 0x03;
j = [self encodeLen:&builder[1] length:[iosKey length] + 1];
builder[j+1] = 0x00;
[encKey appendBytes:builder length:j + 2];
// Now the actual key
[encKey appendData:iosKey];
return encKey;
【讨论】:
@jailani:这里缺少 encodeLen:length: 函数。能否请您也更新依赖函数?以上是关于将 NSData 转换为 SecKeyRef的主要内容,如果未能解决你的问题,请参考以下文章
将 NSData 转换为 NSString 后如何将 NSString 转换回 NSData?
将字符串转换为 NSData 并将 NSData 转换回字符串
将 UIImage 转换为 NSData 并在 Swift 中转换回 UIImage?