美文网首页算法
[iOS] SHA256withRSA加签验签

[iOS] SHA256withRSA加签验签

作者: 世上无南西 | 来源:发表于2020-03-26 14:10 被阅读0次

    一般来说这种加密方式,主要在金融行业遇到的。主要流程先是对内容进行了base64加解密,然后使用SHA256加密方式进行加解密,再对其内容进行RSA加签验签。这里要注意下,RSA加签、验签与RSA加密解密,不要搞混淆了,如果能与后台联调的话,建议抽点时间一步一步和后台对数据,基本上能一遍过。

    Base64与SHA256这一块的理论知识,可以百度搜到很多资料,本文就不提了。
    下面直接贴上干货。

    加签

    需要用到的系统库

    #import <CommonCrypto/CommonDigest.h>
    #import <CommonCrypto/CommonHMAC.h>
    #import <Security/Security.h>
    

    加签外面的代码不方便贴,但是我这儿还是说一下我需要处理数据的方式:根据后台要求,加签之前,表单内容须按字母顺序排序,并拼以" &***=**** "的形式接成字符串,然后再以utf-8的编码形式处理成字符数组,而并非字符串形式。然后把加签结果放入原表单进行网络请求。

    + (NSData *)getHashBytes:(NSData *)plainText {
        CC_SHA256_CTX ctx;
        uint8_t * hashBytes = NULL;
        NSData * hash = nil;
        
        // Malloc a buffer to hold hash.
        hashBytes = malloc( CC_SHA256_DIGEST_LENGTH * sizeof(uint8_t) );
        memset((void *)hashBytes, 0x0, CC_SHA256_DIGEST_LENGTH);
        // Initialize the context.
        CC_SHA256_Init(&ctx);
        // Perform the hash.
        CC_SHA256_Update(&ctx, (void *)[plainText bytes], (CC_LONG)[plainText length]);
        // Finalize the output.
        CC_SHA256_Final(hashBytes, &ctx);
        
        // Build up the SHA1 blob.
        hash = [NSData dataWithBytes:(const void *)hashBytes length:CC_SHA256_DIGEST_LENGTH];
        if (hashBytes) free(hashBytes);
        
        return hash;
    }
    
    +(NSString *)signTheDataBySHA256WithRSA:(NSString *)plainText
    {
        uint8_t* signedBytes = NULL;
        size_t signedBytesSize = 0;
        OSStatus sanityCheck = noErr;
        NSData* signedHash = nil;
        
        // 按路径读取证书内容
        NSString * path = [[NSBundle mainBundle]pathForResource:@"Your Certificate" ofType:@"pfx"];
        NSData * data = [NSData dataWithContentsOfFile:path];
        
        // 证书的密码
        NSMutableDictionary * options = [[NSMutableDictionary alloc] init];
        [options setObject:@"Certificate Passphrase" forKey:(id)kSecImportExportPassphrase];
        CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
        OSStatus securityError = SecPKCS12Import((CFDataRef) data, (CFDictionaryRef)options, &items);
        if (securityError!=noErr) {
            return nil ;
        }
        CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
        SecIdentityRef identityApp =(SecIdentityRef)CFDictionaryGetValue(identityDict,kSecImportItemIdentity);
        SecKeyRef privateKeyRef=nil;
        SecIdentityCopyPrivateKey(identityApp, &privateKeyRef);
        signedBytesSize = SecKeyGetBlockSize(privateKeyRef);
        
        signedBytes = malloc( signedBytesSize * sizeof(uint8_t) );
        memset((void *)signedBytes, 0x0, signedBytesSize);
        
        NSData *plainTextBytes = [plainText dataUsingEncoding:NSUTF8StringEncoding];
        
        sanityCheck = SecKeyRawSign(privateKeyRef,
                                    kSecPaddingPKCS1SHA256,
                                    (const uint8_t *)[[self getHashBytes:plainTextBytes] bytes],
                                    CC_SHA256_DIGEST_LENGTH,
                                    (uint8_t *)signedBytes,
                                    &signedBytesSize);
        
        if (sanityCheck == noErr)
        {
            signedHash = [NSData dataWithBytes:(const void *)signedBytes length:(NSUInteger)signedBytesSize];
        }
        else
        {
            return nil;
        }
        
        if (signedBytes)
        {
            free(signedBytes);
        }
        NSString *signatureResult = [[NSString alloc]initWithData:[signedHash base64EncodedDataWithOptions:0] encoding:NSUTF8StringEncoding];
        
        return signatureResult;
    }
    

    验签

    后台返回会了一个data字符串和sign字符串,我们就要拿这个sign去验证data的正确性,并对data进行解码,就可以拿到里面的内容了。

    // 验签
    
    +(BOOL)VerifyBytesSHA256withRSA:(NSData *)plainData cer:(NSData *)signature{
        if (!plainData || !signature) {
            return NO;
        }
        SecKeyRef publicKey = [self getPublicKey];
        
        size_t signedHashBytesSize = SecKeyGetBlockSize(publicKey);
        const uint8_t * signedHashBytes = [signature bytes];
        size_t hashBytesSize = CC_SHA256_DIGEST_LENGTH;
        uint8_t * hashBytes = malloc(hashBytesSize);
        if (!CC_SHA256([plainData bytes], (CC_LONG)[plainData length], hashBytes)) {
            return NO;
        }
        OSStatus status = SecKeyRawVerify(publicKey,
                                          kSecPaddingPKCS1SHA256,
                                          hashBytes,
                                          hashBytesSize,
                                          signedHashBytes,
                                          signedHashBytesSize);
        return status == errSecSuccess;
    }
    
    + (SecKeyRef)getPublicKey {
    
        NSString * path = [[NSBundle mainBundle]pathForResource:@"Your Public Certificate" ofType:@"cer"];
        NSData * data = [NSData dataWithContentsOfFile:path];
        
        SecCertificateRef myCertificate = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)data);
        SecPolicyRef myPolicy = SecPolicyCreateBasicX509();
        SecTrustRef myTrust;
        OSStatus status = SecTrustCreateWithCertificates(myCertificate,myPolicy,&myTrust);
        CFErrorRef trustError;
        if (status == noErr) {
            status = SecTrustEvaluateWithError(myTrust, &trustError);
            if(trustError){
                NSLog(@"%@",trustError);
            }
        }
        SecKeyRef securityKey = SecTrustCopyPublicKey(myTrust);
        CFRelease(myCertificate);
        CFRelease(myPolicy);
        CFRelease(myTrust);
        return securityKey;
    }
    

    总结

    1、加签验签都是对于加解密之后的数据进行处理,加解密之前的数据,每个人遇到的都可能不一样,比如我接收到的data和sign,都是进过一次编码的,这属于额外处理,得跟后台沟通。我之前处理这个东西搞到凌晨两三点,就是因为跟后台沟通不畅。
    2、加签验签其实就是SecKeyRawSign和SecKeyRawVerify这两个方法。建议可以把加签验签的代码读一读,基本能理解。
    3、这个证书有各种后缀的,但是基本都能直接读取,但是注意后台给的证书可能采用不同的结构,这种情况读取证书的时候可能要指定证书架构,具体内容需查找其他资料。iOS默认支持的是x.509,一般情况下大部分的证书都是这种。

    /*!
        @typedef SecTrustRef
        @abstract CFType used for performing X.509 certificate trust evaluations.
     */
    typedef struct CF_BRIDGED_TYPE(id) __SecTrust *SecTrustRef;
    

    4、加解密与加签验签中,经常有NSData的转换,需要细心一点。

    参考

    https://www.jianshu.com/p/67aeca97f25d

    相关文章

      网友评论

        本文标题:[iOS] SHA256withRSA加签验签

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