分类 工作 下的文章

RSA签名及验签原理解析

一、名词解释

公钥

  RSA 密钥体系中对外公开的部分,通常用于数据加密、验证数字签名。

私钥

  RSA 密钥体系中非公开的部分,通常用于数据解密、数据签名。

数字签名

  就是只有信息的发送者才能产生的,别人无法伪造的一段数字串,它同时也是对发送者发送的信息的真实性的一个证明。

二、为什么要使用签名

  在系统间报文交互为保证上送报文可靠性[防止中间人攻击],通常做法是在报文中加上校验字[数字签名],并约定好相关算法。

三、实现

  为方便系统间交互,本文直接讲述原理,对于RSA_sign 及 RSA_verify的使用下期进行介绍。

签名

  1. 对需要上送的报文[en_data]计算特征值[sign_block],相关算法包括[md5、sha1、sha256、sha512等等]。
  2. 使用私钥[pri_key]对sign_block加密获得数字签名[sign]。
  3. 将sign与en_data打包发送给对方。

验签

  1. 解析收到的报文,拆分为sign 及 en_data。
  2. 对en_data计算特征值[sign_block],相关算法包括[md5、sha1、sha256、sha512等等双方协商]。
  3. 使用公钥[pub_key]对sign解密,获得sign_block1。
  4. 比较sign_block 和 sign_block1,若匹配则验证成功,报文未被篡改。

生成公私钥对

int rsa_gen_der_key(int KEY_LENGTH, unsigned char * prik, unsigned char * pubk) {
    int ret = 1;
    RSA * keypair = RSA_generate_key(KEY_LENGTH, RSA_F4, NULL, NULL);

    if (!RSA_check_key(keypair)) {
        ret = 0;
    }
    /* public key */
    int size = i2d_RSAPublicKey(keypair, NULL); //提取公钥大小
    if (!size) {
        ret = 0;
    }
    unsigned char* pubbuf = (unsigned char*) malloc(size + 1); //生成公钥存储空间
    unsigned char *p = pubbuf;

    if (pubbuf == NULL) {
        ret = 0;
    }
    size = i2d_RSAPublicKey(keypair, &p); //必须用p,不能直接&pubbuf,因为参数会改变
    if (!size) {
        ret = 0;
    }
    binToStr(pubbuf, pubk, size);
    free(pubbuf);

    /* private key*/
    size = i2d_RSAPrivateKey(keypair, NULL); //提取公钥大小
    if (!size) {
        ret = 0;
    }
    unsigned char* pribuf = (unsigned char*) malloc(size + 1); //生成公钥存储空间
    p = pribuf;

    if (pribuf == NULL) {
        ret = 0;
    }
    size = i2d_RSAPrivateKey(keypair, &p);
    if (!size) {
        ret = 0;
    }
    binToStr(pribuf, prik, size);
    free(pribuf);

    RSA_free(keypair);
    return 0;
}

私钥加密[签名]代码

int prik_encrypt(unsigned char * pri_key, unsigned char * ming,unsigned char * mi) {
    int iRet = 0;
    int pubk_len = strlen(pri_key) / 2;
    unsigned char * prik;
    prik = (unsigned char*) malloc(pubk_len + 1);
    strToBin(pri_key, prik, pubk_len);

    RSA* prikey;
    unsigned char *p = prik;
    prikey = d2i_RSAPrivateKey(NULL, (const unsigned char**) &p, pubk_len);

    int len_patch = 116;

    int in_len = strlen(ming);
    int in_len_x = in_len % len_patch;
    int in_len_y = (int)(in_len / len_patch);
    if(in_len_x != 0) in_len_y += 1;
    unsigned char rsa_in[1024],rsa_out[1024];

    int rsa_i;
    int rsa_j=0;
    int start_index=0;
    for(rsa_i=0;rsa_i<in_len_y;rsa_i++){
        memset(rsa_in,  0x00, sizeof(rsa_in));
        memset(rsa_out, 0x00, sizeof(rsa_out));

        int deal_len = in_len >= len_patch ? len_patch : in_len;

        memcpy(rsa_in, ming + start_index, deal_len);
        start_index += deal_len;
        in_len -= deal_len;

        iRet = RSA_private_encrypt(deal_len, (unsigned char*) rsa_in,
                (unsigned char*) rsa_out, prikey, RSA_PKCS1_PADDING);
        if(iRet < 0){
            iRet = -1;
            break;
        }else{
            binToStr(rsa_out,mi + rsa_j,iRet);
            rsa_j += iRet * 2;
        }
    }
    if (prik)
        free(prik);
    if (prikey)
        RSA_free(prikey);

    return iRet;
}

公钥解密[验签]代码

int pubk_decrypt(unsigned char * pub_key, unsigned char * mi,unsigned char * ming) {
    int iRet = 0;
    int prik_len = strlen(pub_key) / 2;
    unsigned char * pubk;
    pubk = (unsigned char*) malloc(prik_len + 1);
    strToBin(pub_key, pubk, prik_len);

    RSA* pubkey;
    unsigned char *p = pubk;
    pubkey = d2i_RSAPublicKey(NULL, (const unsigned char**) &p, prik_len);

    int in_len = strlen(mi) / 2;

    int len_patch = 128;

    int in_len_x = in_len % len_patch;
    int in_len_y = (int)(in_len / len_patch);
    if(in_len_x != 0) in_len_y += 1;
    unsigned char rsa_128_in[130];
    unsigned char rsa_out[1024];


    int rsa_i;
    int rsa_j=0;
    int start_index = 0;
    for(rsa_i=0;rsa_i<in_len_y;rsa_i++){
        memset(rsa_128_in,0x00,sizeof(rsa_128_in));
        memset(rsa_out,0x00,sizeof(rsa_out));

        int deal_len = in_len >= len_patch ? len_patch : in_len;
        strToBin(mi + start_index, rsa_128_in, deal_len);
        start_index += deal_len * 2;
        in_len -= deal_len;

        iRet = RSA_public_decrypt(deal_len, (unsigned char*) rsa_128_in,
                (unsigned char*) rsa_out, pubkey, RSA_PKCS1_PADDING);
        if(iRet < 0){
            iRet = -1;
            break;
        }else{
            memcpy(ming + rsa_j,rsa_out,iRet);
            rsa_j += iRet;
        }
    }
    if (pubk)
        free(pubk);
    if (pubkey)
        RSA_free(pubkey);
    return iRet;
}

误区

公钥[pub_key]、私钥[pri_key]在密钥体系中是平等的;并不是公钥只能加密,私钥只能解密;私钥也可以用来加密,然后使用公钥解密,只不过我们对这一操作区了个别名叫[签名]和[验签]。

统一登陆认证平台设计

应用场景

  具体是酱紫的,公司内部搞了好多单独的应用系统,比如考核、wiki、OA、git服务、项目管理...好多好多用户名密码需要记哦...忘了肿么办呀!这时候就想啊!能不能搞个统一认证的平台[后面简称UAS],登陆这个平台后直接从这个平台获得其他平台的权限,然后就可以直接免登陆访问啦!想想就有点小激动。

- 阅读剩余部分 -