RSA 简介
RSA是一种非对称加密算法,使用公钥加密就可以使用私钥解密,使用私钥加密就可以使用公钥解密。RSA公钥对外公开,私钥自己保留。RSA既能加密、解密,也能加签、验签
加密解密:RSA是公钥加密,私钥私密。数据发送方使用接收方的公钥来对数据进行加密,接收方接收到数据之后用自己的私钥解密
加签验签:RSA私钥加签,公钥验签。数据发送方使用自己的私钥对数据进行签名,数据接收方数据发送方的公钥来验签。
我遇到的业务场景是:服务端接口开发人员给了我一套客户端的PKCS1格式的私钥字符串和服务端的公钥字符串。我给服务端发送数据的时候要使用私钥对数据进行签名之后再发送。接收到服务端返回的数据之后要使用服务端的公钥验签。下面接详细讲解在iOS上SHA1WithRSA的实现过程。
加签的流程:
验签的流程
在数据传输的过程中如果原文是以明文的形式传输,数据还是会被泄漏。所以我这里对原文数据进行了一次AES加密,然后再走加签的流程
下面讲解iOS中使用Security.framework系统框架来实现RSA相关功能,以及使用
t 下的CC_SHA1
实现sha1算法
- PKCS1格式的RSA私钥的字符串转SecKeyRef
- 公钥字符串转SeckeyRef
- SHA1算法的实现
- 使用RSA私钥签名的实现
- 使用RSA公钥验签的实现
1. PKCS1格式的RSA私钥的字符串转SecKeyRef
将
PKCS#1
格式的RSA私钥转成可供Security.framework
框架中使用的SecKeyRef
+ (SecKeyRef)addPrivateKey:(NSString *)key{
// This is a base64 encoded key. so, decode it.
NSData *data = [[NSData alloc] initWithBase64EncodedString:key options:NSDataBase64DecodingIgnoreUnknownCharacters];
if(!data){ return nil; }
//a tag to read/write keychain storage
NSString *tag = @"RSA_PRIVATE_KEY";
NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]];
// Delete any old lingering key with the same tag
NSMutableDictionary *privateKey = [[NSMutableDictionary alloc] init];
[privateKey setObject:(__bridge id) kSecClassKey forKey:(__bridge id)kSecClass];
[privateKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[privateKey setObject:d_tag forKey:(__bridge id)kSecAttrApplicationTag];
SecItemDelete((__bridge CFDictionaryRef)privateKey);
// Add persistent version of the key to system keychain
[privateKey setObject:data forKey:(__bridge id)kSecValueData];
[privateKey setObject:(__bridge id) kSecAttrKeyClassPrivate forKey:(__bridge id)kSecAttrKeyClass];
[privateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnPersistentRef];
CFTypeRef persistKey = nil;
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)privateKey, &persistKey);
if (persistKey != nil){ CFRelease(persistKey); }
if ((status != noErr) && (status != errSecDuplicateItem)) { return nil; }
[privateKey removeObjectForKey:(__bridge id)kSecValueData];
[privateKey removeObjectForKey:(__bridge id)kSecReturnPersistentRef];
[privateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
[privateKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
// Now fetch the SecKeyRef version of the key
SecKeyRef keyRef = nil;
status = SecItemCopyMatching((__bridge CFDictionaryRef)privateKey, (CFTypeRef *)&keyRef);
if(status != noErr){
return nil;
}
return keyRef;
}
2. 公钥字符串转SeckeyRef
+ (SecKeyRef)addPublicKey:(NSString *)pubKey
{
NSData *data = [[NSData alloc] initWithBase64EncodedString:pubKey options:NSDataBase64DecodingIgnoreUnknownCharacters];
//a tag to read/write keychain storage
NSString *tag = @"RSA_PUBLIC_KEY";
NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]];
// Delete any old lingering key with the same tag
NSMutableDictionary *publicKey = [[NSMutableDictionary alloc] init];
[publicKey setObject:(__bridge id) kSecClassKey forKey:(__bridge id)kSecClass];
[publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[publicKey setObject:d_tag forKey:(__bridge id)kSecAttrApplicationTag];
SecItemDelete((__bridge CFDictionaryRef)publicKey);
// Add persistent version of the key to system keychain
[publicKey setObject:data forKey:(__bridge id)kSecValueData];
[publicKey setObject:(__bridge id) kSecAttrKeyClassPublic forKey:(__bridge id)kSecAttrKeyClass];
[publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnPersistentRef];
CFTypeRef persistKey = nil;
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)publicKey, &persistKey);
if (persistKey != nil){
CFRelease(persistKey);
}
if ((status != noErr) && (status != errSecDuplicateItem)) { return nil; }
[publicKey removeObjectForKey:(__bridge id)kSecValueData];
[publicKey removeObjectForKey:(__bridge id)kSecReturnPersistentRef];
[publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
[publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
// Now fetch the SecKeyRef version of the key
SecKeyRef keyRef = nil;
status = SecItemCopyMatching((__bridge CFDictionaryRef)publicKey, (CFTypeRef *)&keyRef);
if(status != noErr){
return nil;
}
return keyRef;
}
3. SHA1算法的实现
// digest message with sha1
+ (NSData *)sha1:(NSString *)str
{
const void *data = [str cStringUsingEncoding:NSUTF8StringEncoding];
CC_LONG len = (CC_LONG)strlen(data);
uint8_t * md = malloc( CC_SHA1_DIGEST_LENGTH * sizeof(uint8_t) );;
CC_SHA1(data, len, md);
return [NSData dataWithBytes:md length:CC_SHA1_DIGEST_LENGTH];
}
4. 使用RSA私钥签名的实现
// Using the RSA private key to sign the specified message
+ (NSString *)sign:(NSString *)content withPriKey:(NSString *)priKey
{
SecKeyRef privateKeyRef = [self addPrivateKey:priKey];
if (!privateKeyRef) { NSLog(@"添加私钥失败"); return nil; }
NSData *sha1Data = [self sha1:content];
unsigned char *sig = (unsigned char *)malloc(256);
size_t sig_len;
OSStatus status = SecKeyRawSign(privateKeyRef, kSecPaddingPKCS1SHA1, [sha1Data bytes], CC_SHA1_DIGEST_LENGTH, sig, &sig_len);
if (status != noErr) { NSLog(@"加签失败:%d",status); return nil; }
NSData *outData = [NSData dataWithBytes:sig length:sig_len];
return [outData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
}
5. 使用RSA公钥验签的实现
// verify Signature
+ (BOOL)verify:(NSString *)content signature:(NSString *)signature withPublivKey:(NSString *)publicKey {
SecKeyRef publicKeyRef = [self addPublicKey:publicKey];
if (!publicKeyRef) { NSLog(@"添加公钥失败"); return NO; }
NSData *originData = [self sha1:content];
NSData *signatureData = [[NSData alloc] initWithBase64EncodedString:signature options:NSDataBase64DecodingIgnoreUnknownCharacters];
if (!originData || !signatureData) { return NO; }
OSStatus status = SecKeyRawVerify(publicKeyRef, kSecPaddingPKCS1SHA1, [originData bytes], originData.length, [signatureData bytes], signatureData.length);
if (status ==noErr) { return YES; }
else{ NSLog(@"验签失败:%d",status); return NO; }
}
完整的示例代码可以在GitHub上查看
附:
本文介绍的加签是基于RSA的PKCS#1格式的私钥来完成。如实你使用的是PKCS#8格式的私钥请参考另一个库Encrypt。关于RSA私钥的格式请自行百度了解。关于PKCS1转PKCS8请看这篇文章RSA私钥PKCS1 转 PKCS8