美文网首页iOS开发数据安全探讨
iOS RSA签名与加密(全)

iOS RSA签名与加密(全)

作者: 溯溪mvp | 来源:发表于2017-11-30 15:51 被阅读223次

    一、前言

    • 项目要用到RSA签名,因为以前没接触过,所以自己就先百度+谷歌一番,虽然内容不多,但对新手来说找起来是比较费劲的,所以这里就做个综合总结,文章中引用到别人博客内容,我都会在末尾贴出地址,尊重别人的知识成果。
    • 全文分两部分说1.加解密2.签名与验证
    • ps: 如果你的需求只是用RSA签名的话,那么加解密部分就不需要看

    二、RSA介绍

    • 加密:公钥放在客户端,并使用公钥对数据进行加密,服务端拿到数据后用私钥进行解密。
    • 加签:私钥放在客户端,并使用私钥对数据进行加签,服务端拿到数据后用公钥进行验签。
    • 前者完全为了加密;后者主要是为了防恶意攻击,防止别人模拟我们的客户端对我们的服务器进行攻击,导致服务器瘫痪。

    三、加解密

    四、签名与验证

    • 这块我找了好几个小时,主要是因为大部分文章都是对加解密进行说明,最后终于被我找到,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这么大,我果断的拒绝了,最后发现其实支付宝的SDK中有这个库,所以我就直接使用了

    版本

    • Xcode9.1

    参考

    Next

    • 大家,下次见

    相关文章

      网友评论

        本文标题:iOS RSA签名与加密(全)

        本文链接:https://www.haomeiwen.com/subject/tdfbmttx.html