zoukankan      html  css  js  c++  java
  • RSA加密:利用模数和指数生成公钥加密

    引子
      目前做一款金融产品,由于涉及到资金安全,采用动态公钥的方式,即客户端每次登录服务端返回一个不同的XML串,由公钥的模数和指数构成,我需要用这个串生成公钥加密相关信息。
    服务端返回的XML串形如:

    <RSAKeyValue>
    <Modulus>
    wVwBKuePO3ZZbZ//gqaNuUNyaPHbS3e2v5iDHMFRfYHS/bFw+79GwNUiJ+wXgpA7SSBRhKdLhTuxMvCn1aZNlXaMXIOPG1AouUMMfr6kEpFf/V0wLv6NCHGvBUK0l7O+2fxn3bR1SkHM1jWvLPMzSMBZLCOBPRRZ5FjHAy8d378=
    </Modulus>
    <Exponent>AQAB</Exponent>
    </RSAKeyValue>
    View Code

    推荐看下这篇博客快速了解一下RSA:http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html

    问题

    • 对RSA不了解。
    • 如何用所谓的模数和指数生成公钥来加密相关信息。

    过程

      熟悉RSA。先看下openssl库中RSA结构体的定义。

    struct rsa_st
    {
    /* The first parameter is used to pickup errors where
    * this is passed instead of aEVP_PKEY, it is set to 0 */
    int pad;
    long version;
    const RSA_METHOD *meth;
    /* functional reference if 'meth' is ENGINE-provided */
    ENGINE *engine;
    BIGNUM *n;
    BIGNUM *e;
    BIGNUM *d;
    BIGNUM *p;
    BIGNUM *q;
    BIGNUM *dmp1;
    BIGNUM *dmq1;
    BIGNUM *iqmp;
    /* be careful using this if the RSA structure is shared */
    CRYPTO_EX_DATA ex_data;
    int references;
    int flags;
    
    /* Used to cache montgomery values */
    BN_MONT_CTX *_method_mod_n;
    BN_MONT_CTX *_method_mod_p;
    BN_MONT_CTX *_method_mod_q;
    
    /* all BIGNUM values are actually in the following data, if it is not
    * NULL */
    char *bignum_data;
    BN_BLINDING *blinding;
    BN_BLINDING *mt_blinding;
    };
    View Code

      开始推荐的博客中有关于RSA模数和指数的介绍,对应到结构中分别是其中的 n 和 e ,模反数对应d,最开始的质数因子对应 p和 q。n和e决定了公钥,n和d决定了私钥。结构体中其它元素不论,能知道的是模数和指数决定了公钥。

       如果能生成一个der或者pem文件,就可以用系统的方法去获取公钥,如下:

     // 1 der证书的base64编码形式
        NSString *cert = @"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBXAEq5487dlltn/+Cpo25Q3Jo8dtLd7a/mIMcwVF9gdL9sXD7v0bA1SIn7BeCkDtJIFGEp0uFO7Ey8KfVpk2Vdoxcg48bUCi5Qwx+vqQSkV/9XTAu/o0Ica8FQrSXs77Z/GfdtHVKQczWNa8s8zNIwFksI4E9FFnkWMcDLx3fvwIDAQAB";
    
        // 2 解码base64
        NSData *publicKeyFileContent = [NSData dataFromBase64String:cert];
        
        // 3 创建der证书对象
        certificate = SecCertificateCreateWithData(kCFAllocatorDefault, ( __bridge CFDataRef)publicKeyFileContent);
        if (certificate == nil) {
            DLog(@"Can not read certificate from pub.der");
            return nil;
        }
        // 4 验证证书
        policy = SecPolicyCreateBasicX509();
        OSStatus returnCode = SecTrustCreateWithCertificates(certificate, policy, &trust);
        if (returnCode != 0) {
            DLog(@"SecTrustCreateWithCertificates fail. Error Code: %ld", returnCode);
            return nil;
        }
        
            // 5 返回公钥
        SecTrustResultType trustResultType;
        returnCode = SecTrustEvaluate(trust, &trustResultType);
        if (returnCode != 0) {
            DLog(@"SecTrustEvaluate fail. Error Code: %ld", returnCode);
            return nil;
        }
        
           // 不管是否信任都会尝试返回公钥,也有可能被信任但是返不回公钥
        publicKey = SecTrustCopyPublicKey(trust);
        if (publicKey == nil) {
            DLog(@"SecTrustCopyPublicKey fail");
            return nil;
        }
        
        maxPlainLen = SecKeyGetBlockSize(publicKey) - 12;
    View Code

      一番尝试后没有搞定,之后转向第三方库,比如openssl,openssl用C++实现。可以借助该库用模数和指数构造公钥。在网上也找到一篇解决这个问题的博客:Converting RSA public key Modulus and Exponent into PEM file

    unsigned char *base64_decode(const char* base64data, int* len) {
        BIO *b64, *bmem;
        size_t length = strlen(base64data);
        unsigned char *buffer = (unsigned char *)malloc(length);
        b64 = BIO_new(BIO_f_base64());
        BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
        bmem = BIO_new_mem_buf((void*)base64data, length);
        bmem = BIO_push(b64, bmem);
        *len = BIO_read(bmem, buffer, length);
        BIO_free_all(bmem);
        return buffer;
    }
    
    BIGNUM* bignum_base64_decode(const char* base64bignum) {
        BIGNUM* bn = NULL;
        int len;
        unsigned char* data = base64_decode(base64bignum, &len);
        if (len) {
            bn = BN_bin2bn(data, len, NULL);
        }
        free(data);
        return bn;
    }
    
    EVP_PKEY* RSA_fromBase64(const char* modulus_b64, const char* exp_b64) {
        BIGNUM *n = bignum_base64_decode(modulus_b64);
        BIGNUM *e = bignum_base64_decode(exp_b64);
        
        if (!n) printf("Invalid encoding for modulus
    ");
        if (!e) printf("Invalid encoding for public exponent
    ");
        
        if (e && n) {
            EVP_PKEY* pRsaKey = EVP_PKEY_new();
            RSA* rsa = RSA_new();
            rsa->e = e;
            rsa->n = n;
            EVP_PKEY_assign_RSA(pRsaKey, rsa);
            return pRsaKey;
        } else {
            if (n) BN_free(n);
            if (e) BN_free(e);
            return NULL;
        }
    }
    
    void assert_syntax(int argc, char** argv) {
        if (argc != 4) {
            fprintf(stderr, "Description: %s takes a RSA public key modulus and exponent in base64 encoding and produces a public key file in PEM format.
    ", argv[0]);
            fprintf(stderr, "syntax: %s <modulus_base64> <exp_base64> <output_file>
    ", argv[0]);
            exit(1);
        }
    }
    View Code

      生成pem文件之后可以从pem文件里面读取公钥加密相关信息。

      最后封装了一下,放在github上:https://github.com/yrs244742688/GeneratePemWithMoAndEx

    注解

      ASN.1:ASN.1抽象语法标记(Abstract Syntax Notation One) ASN.1是一种 ISO/ITU-T 标准,描述了一种对数据进行表示、编码、传输和解码的数据格式。它提供了一整套正规的格式用于描述对象的结构,而不管语言上如何执行及这些数据的具体指 代,也不用去管到底是什么样的应用程序。标准的ASN.1编码规则有基本编码规则(BER,Basic Encoding Rules)、规范编码规则(CER,Canonical Encoding Rules)、唯一编码规则(DER,Distinguished Encoding Rules)、压缩编码规则(PER,Packed Encoding Rules)和XML编码规则(XER,XML Encoding Rules)。

      x.509 : 常见通用的证书格式。

      der:DER是ASN.1编码规则的其中一种。x.509证书通过DER编码(ASCII)后缀是:.DER .CER .CRT,通过PAM编码(Base64)的后缀是:.PEM .CER .CRT。.cer/.crt是用于存放证书,它是2进制形式存放的,不含私钥。

      pem文件 : der文件经过base64转码后的文件。

     (Email: yangxu0905@foxmail.com) 

    终于明白,“喜欢”是一种莫大的能量!
  • 相关阅读:
    跳台阶问题
    腾讯,百度,网易游戏,华为笔面经验
    进程、线程、应用程序之间的关系
    const用法小结
    vc快捷键
    文献阅读以及如何管理
    数据类型转换
    vc Debug Release
    如何阅读文献
    如何提高表达能力
  • 原文地址:https://www.cnblogs.com/tml839720759/p/3926006.html
Copyright © 2011-2022 走看看