一、前言
- 項目要用到RSA簽名冲杀,因為以前沒接觸過剩檀,所以自己就先百度+谷歌一番沪猴,雖然內容不多运嗜,但對新手來說找起來是比較費勁的,所以這里就做個綜合總結奋救,文章中引用到別人博客內容尝艘,我都會在末尾貼出地址,尊重別人的知識成果。
- 全文分兩部分說1.加解密2.簽名與驗證
- ps: 如果你的需求只是用RSA簽名的話舷嗡,那么加解密部分就不需要看
二进萄、RSA介紹
- 加密:公鑰放在客戶端,并使用公鑰對數(shù)據(jù)進行加密援雇,服務端拿到數(shù)據(jù)后用私鑰進行解密惫搏。
- 加簽:私鑰放在客戶端铣猩,并使用私鑰對數(shù)據(jù)進行加簽达皿,服務端拿到數(shù)據(jù)后用公鑰進行驗簽峦椰。
- 前者完全為了加密;后者主要是為了防惡意攻擊冤竹,防止別人模擬我們的客戶端對我們的服務器進行攻擊,導致服務器癱瘓钟病。
三肠阱、加解密
- 加解密這篇文章說的很詳細朴读,我這里就不過多介紹了(http://www.reibang.com/p/74a796ec5038)
四屹徘、簽名與驗證
- 這塊我找了好幾個小時,主要是因為大部分文章都是對加解密進行說明衅金,最后終于被我找到噪伊,haha
- 以下是代碼,可直接復制使用
RSAHandler.h
#import <Foundation/Foundation.h>
typedef enum {
KeyTypePublic = 0,
KeyTypePrivate
}KeyType;
@interface RSAHandler : NSObject
- (BOOL)importKeyWithType:(KeyType)type andPath:(NSString*)path;
- (BOOL)importKeyWithType:(KeyType)type andkeyString:(NSString *)keyString;
//私鑰驗證簽名 Sha1 + RSA
- (BOOL)verifyString:(NSString *)string withSign:(NSString *)signString;
//驗證簽名 md5 + RSA
- (BOOL)verifyMD5String:(NSString *)string withSign:(NSString *)signString;
//私鑰簽名
- (NSString *)signString:(NSString *)string;
- (NSString *)signMD5String:(NSString *)string;
//公鑰加密
- (NSString *)encryptWithPublicKey:(NSString*)content;
//私鑰解密
- (NSString *)decryptWithPrivatecKey:(NSString*)content;
@end
RSAHandler.M
#import "RSAHandler.h"
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/md5.h>
typedef enum {
RSA_PADDING_TYPE_NONE = RSA_NO_PADDING,
RSA_PADDING_TYPE_PKCS1 = RSA_PKCS1_PADDING,
RSA_PADDING_TYPE_SSLV23 = RSA_SSLV23_PADDING
}RSA_PADDING_TYPE;
#define PADDING RSA_PADDING_TYPE_PKCS1
@implementation YWRSAHandler
{
RSA* _rsa_pub;
RSA* _rsa_pri;
}
#pragma mark - public methord
-(BOOL)importKeyWithType:(KeyType)type andPath:(NSString *)path
{
BOOL status = NO;
const char* cPath = [path cStringUsingEncoding:NSUTF8StringEncoding];
FILE* file = fopen(cPath, "rb");
if (!file) {
return status;
}
if (type == KeyTypePublic) {
_rsa_pub = NULL;
if((_rsa_pub = PEM_read_RSA_PUBKEY(file, NULL, NULL, NULL))){
status = YES;
}
}else if(type == KeyTypePrivate){
_rsa_pri = NULL;
if ((_rsa_pri = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL))) {
status = YES;
}
}
fclose(file);
return status;
}
- (BOOL)importKeyWithType:(KeyType)type andkeyString:(NSString *)keyString
{
if (!keyString) {
return NO;
}
BOOL status = NO;
BIO *bio = NULL;
RSA *rsa = NULL;
bio = BIO_new(BIO_s_file());
NSString* temPath = NSTemporaryDirectory();
NSString* rsaFilePath = [temPath stringByAppendingPathComponent:@"RSAKEY"];
NSString* formatRSAKeyString = [self formatRSAKeyWithKeyString:keyString andKeytype:type];
BOOL writeSuccess = [formatRSAKeyString writeToFile:rsaFilePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
if (!writeSuccess) {
return NO;
}
const char* cPath = [rsaFilePath cStringUsingEncoding:NSUTF8StringEncoding];
BIO_read_filename(bio, cPath);
if (type == KeyTypePrivate) {
rsa = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, "");
_rsa_pri = rsa;
if (rsa != NULL && 1 == RSA_check_key(rsa)) {
status = YES;
} else {
status = NO;
}
}
else{
rsa = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL);
_rsa_pub = rsa;
if (rsa != NULL) {
status = YES;
} else {
status = NO;
}
}
BIO_free_all(bio);
[[NSFileManager defaultManager] removeItemAtPath:rsaFilePath error:nil];
return status;
}
#pragma mark RSA sha1驗證簽名
//signString為base64字符串
- (BOOL)verifyString:(NSString *)string withSign:(NSString *)signString
{
if (!_rsa_pub) {
NSLog(@"please import public key first");
return NO;
}
const char *message = [string cStringUsingEncoding:NSUTF8StringEncoding];
int messageLength = (int)[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
NSData *signatureData = [[NSData alloc]initWithBase64EncodedString:signString options:0];
unsigned char *sig = (unsigned char *)[signatureData bytes];
unsigned int sig_len = (int)[signatureData length];
unsigned char sha1[20];
SHA1((unsigned char *)message, messageLength, sha1);
int verify_ok = RSA_verify(NID_sha1
, sha1, 20
, sig, sig_len
, _rsa_pub);
if (1 == verify_ok){
return YES;
}
return NO;
}
#pragma mark RSA MD5 驗證簽名
- (BOOL)verifyMD5String:(NSString *)string withSign:(NSString *)signString
{
if (!_rsa_pub) {
NSLog(@"please import public key first");
return NO;
}
const char *message = [string cStringUsingEncoding:NSUTF8StringEncoding];
// int messageLength = (int)[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
NSData *signatureData = [[NSData alloc]initWithBase64EncodedString:signString options:0];
unsigned char *sig = (unsigned char *)[signatureData bytes];
unsigned int sig_len = (int)[signatureData length];
unsigned char digest[MD5_DIGEST_LENGTH];
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, message, strlen(message));
MD5_Final(digest, &ctx);
int verify_ok = RSA_verify(NID_md5
, digest, MD5_DIGEST_LENGTH
, sig, sig_len
, _rsa_pub);
if (1 == verify_ok){
return YES;
}
return NO;
}
- (NSString *)signString:(NSString *)string
{
if (!_rsa_pri) {
NSLog(@"please import private key first");
return nil;
}
const char *message = [string cStringUsingEncoding:NSUTF8StringEncoding];
int messageLength = (int)strlen(message);
unsigned char *sig = (unsigned char *)malloc(256);
unsigned int sig_len;
unsigned char sha1[20];
SHA1((unsigned char *)message, messageLength, sha1);
int rsa_sign_valid = RSA_sign(NID_sha1
, sha1, 20
, sig, &sig_len
, _rsa_pri);
if (rsa_sign_valid == 1) {
NSData* data = [NSData dataWithBytes:sig length:sig_len];
NSString * base64String = [data base64EncodedStringWithOptions:0];
free(sig);
return base64String;
}
free(sig);
return nil;
}
- (NSString *)signMD5String:(NSString *)string
{
if (!_rsa_pri) {
NSLog(@"please import private key first");
return nil;
}
const char *message = [string cStringUsingEncoding:NSUTF8StringEncoding];
//int messageLength = (int)strlen(message);
unsigned char *sig = (unsigned char *)malloc(256);
unsigned int sig_len;
unsigned char digest[MD5_DIGEST_LENGTH];
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, message, strlen(message));
MD5_Final(digest, &ctx);
int rsa_sign_valid = RSA_sign(NID_md5
, digest, MD5_DIGEST_LENGTH
, sig, &sig_len
, _rsa_pri);
if (rsa_sign_valid == 1) {
NSData* data = [NSData dataWithBytes:sig length:sig_len];
NSString * base64String = [data base64EncodedStringWithOptions:0];
free(sig);
return base64String;
}
free(sig);
return nil;
}
- (NSString *)encryptWithPublicKey:(NSString*)content
{
if (!_rsa_pub) {
NSLog(@"please import public key first");
return nil;
}
int status;
int length = (int)[content length];
unsigned char input[length + 1];
bzero(input, length + 1);
int i = 0;
for (; i < length; i++)
{
input[i] = [content characterAtIndex:i];
}
NSInteger flen = [self getBlockSizeWithRSA_PADDING_TYPE:PADDING andRSA:_rsa_pub];
char *encData = (char*)malloc(flen);
bzero(encData, flen);
status = RSA_public_encrypt(length, (unsigned char*)input, (unsigned char*)encData, _rsa_pub, PADDING);
if (status){
NSData *returnData = [NSData dataWithBytes:encData length:status];
free(encData);
encData = NULL;
//NSString *ret = [returnData base64EncodedString];
NSString *ret = [returnData base64EncodedStringWithOptions: NSDataBase64Encoding64CharacterLineLength];
return ret;
}
free(encData);
encData = NULL;
return nil;
}
- (NSString *)decryptWithPrivatecKey:(NSString*)content
{
if (!_rsa_pri) {
NSLog(@"please import private key first");
return nil;
} int status;
//NSData *data = [content base64DecodedData];
NSData *data = [[NSData alloc]initWithBase64EncodedString:content options:NSDataBase64DecodingIgnoreUnknownCharacters];
int length = (int)[data length];
NSInteger flen = [self getBlockSizeWithRSA_PADDING_TYPE:PADDING andRSA:_rsa_pri];
char *decData = (char*)malloc(flen);
bzero(decData, flen);
status = RSA_private_decrypt(length, (unsigned char*)[data bytes], (unsigned char*)decData, _rsa_pri, PADDING);
if (status)
{
NSMutableString *decryptString = [[NSMutableString alloc] initWithBytes:decData length:strlen(decData) encoding:NSASCIIStringEncoding];
free(decData);
decData = NULL;
return decryptString;
}
free(decData);
decData = NULL;
return nil;
}
- (int)getBlockSizeWithRSA_PADDING_TYPE:(RSA_PADDING_TYPE)padding_type andRSA:(RSA*)rsa
{
int len = RSA_size(rsa);
if (padding_type == RSA_PADDING_TYPE_PKCS1 || padding_type == RSA_PADDING_TYPE_SSLV23) {
len -= 11;
}
return len;
}
- (NSString*)formatRSAKeyWithKeyString:(NSString*)keyString andKeytype:(KeyType)type
{
NSInteger lineNum = -1;
NSMutableString *result = [NSMutableString string];
if (type == KeyTypePrivate) {
[result appendString:@"-----BEGIN PRIVATE KEY-----\n"];
lineNum = 79;
}else if(type == KeyTypePublic){
[result appendString:@"-----BEGIN PUBLIC KEY-----\n"];
lineNum = 76;
}
int count = 0;
for (int i = 0; i < [keyString length]; ++i) {
unichar c = [keyString characterAtIndex:i];
if (c == '\n' || c == '\r') {
continue;
}
[result appendFormat:@"%c", c];
if (++count == lineNum) {
[result appendString:@"\n"];
count = 0;
}
}
if (type == KeyTypePrivate) {
[result appendString:@"\n-----END PRIVATE KEY-----"];
}else if(type == KeyTypePublic){
[result appendString:@"\n-----END PUBLIC KEY-----"];
}
return result;
}
@end
- ps:簽名要用到了openssl庫氮唯,如果你的項目中沒有這個庫鉴吹,那就用cocoaPods安裝,其實我們項目中也沒有這個庫惩琉,我本來打算裝一個豆励,結果一看,我擦琳水,18M這么大肆糕,我果斷的拒絕了般堆,最后發(fā)現(xiàn)其實支付寶的SDK中有這個庫,所以我就直接使用了
版本
- Xcode9.1
參考
- http://www.reibang.com/p/74a796ec5038
- http://www.reibang.com/p/3bf49e9a4cc8
- http://www.cocoachina.com/ios/20160112/14940.html
- https://github.com/Larrywanglong/RSAAndSignatureDemo
Next
- 大家诚啃,下次見