Featured image of post c语言使用openssl实现SHA1WithRSA签名验签

c语言使用openssl实现SHA1WithRSA签名验签

前言

最近一个项目对接。要使用SHA1WithRSA签名验签。 之前接触过DES、3DES、 AES、 SHA1、 MD5、RSA 看这加密想当然的觉得是就是先对数据做个SHA1摘要再做个RSA加密嘛,简单不是。 man 了一下 openssl 关于RSA加解密。霹雳啪啦几个小时就把 理解的“SHA1WithRSA”实现了。 但是测试时对方就是验签不过。 后面还是问广大的网络。才让我得了解自己的无知。

简单阅读一下就能大概知道实际上可以通过openssl的RSA_sign实现 来看一下RSA_sign

#include <openssl/rsa.h>

 int RSA_sign(int type, const unsigned char *m, unsigned int m_len,
    unsigned char *sigret, unsigned int *siglen, RSA *rsa);

 int RSA_verify(int type, const unsigned char *m, unsigned int m_len,
    unsigned char *sigbuf, unsigned int siglen, RSA *rsa);

DESCRIPTION RSA_sign() signs the message digest m of size m_len using the private key rsa as specified in PKCS #1 v2.0. It stores the signature in sigret and the signature size in siglen. sigret must point to RSA_size(rsa) bytes of memory. Note that PKCS #1 adds meta-data, placing limits on the size of the key that can be used. See RSA_private_encrypt for lower-level operations. type denotes the message digest algorithm that was used to generate m. If type is NID_md5_sha1, an SSL signature (MD5 and SHA1 message digests with PKCS #1 padding and no algorithm identifier) is created. RSA_verify() verifies that the signature sigbuf of size siglen matches a given message digest m of size m_len. type denotes the message digest algorithm that was used to generate the signature. rsais the signer’s public key. RETURN VALUES RSA_sign() returns 1 on success. RSA_verify() returns 1 on successful verification.

简单看看,其实RSA_sing()函数第一为送NID_sha1

通过这篇文章知道需要签名的是SHA1摘要而非所有报文。所以要先对明文数据做SHA1。再调用 RSA_sign()签名。

结论:

对openSSL源码一知半解的我,竟把RSA加密|解密和RSA签名|验证混淆。 最后 私钥加密 ≠ 签名


20170519更新

这几天看了下这文章阅读人数相比还挺多的。空闲之余就把整理下自己以前写的test程序提供给大家参考下。

秘钥对是我是使用openssl命令生产的给出的参考如下。另外秘钥文件格式有很多种,请注意。

生产私钥
openssl genrsa -out userkey.pem 1024   
从私钥中导出公钥
openssl rsa in userkey.pem -pubout -out userpub.key

签名测试代码

#include <string.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>

RSA* getPrivateKey(char* in_szKeyPath)
{
    FILE    *fp = NULL; 
    char    szKeyPath[1024];
    RSA     *priRsa = NULL, *pubRsa = NULL, *pOut = NULL;

    memset(szKeyPath, 0 ,sizeof(szKeyPath));

    if(256 < strlen(in_szKeyPath))
        strncpy(szKeyPath, in_szKeyPath, 256);
    else
        strncpy(szKeyPath, in_szKeyPath, strlen(in_szKeyPath));

    printf("密钥文件路径[%s]", szKeyPath);

    /*  打开密钥文件 */
    if(NULL == (fp = fopen(szKeyPath, "rb")))
    {
        printf( "打开密钥文件[%s]出错", szKeyPath);
        return NULL;
    }
    /*  获取私密钥 */
    if(NULL == (priRsa = PEM_read_RSAPrivateKey(fp, &priRsa, NULL,NULL)))
    {
        printf( "读出私钥内容出错\n");
        fclose(fp);
        return NULL;
    }
    fclose(fp);

    printf("提取私钥\n");
    pOut = priRsa;
    return pOut;
}

RSA* getPublicKey(char* in_szKeyPath)
{
    FILE    *fp = NULL; 
    char    szKeyPath[1024];
    RSA     *priRsa = NULL, *pubRsa = NULL, *pOut = NULL;

    memset(szKeyPath, 0 ,sizeof(szKeyPath));

    if(256 < strlen(in_szKeyPath))
        strncpy(szKeyPath, in_szKeyPath, 256);
    else
        strncpy(szKeyPath, in_szKeyPath, strlen(in_szKeyPath));

    printf("密钥文件路径[%s]", szKeyPath);

    /*  打开密钥文件 */
    if(NULL == (fp = fopen(szKeyPath, "rb")))
    {
        printf( "打开密钥文件[%s]出错", szKeyPath);
        return NULL;
    }
    /*  获取公密钥 */
    if(NULL == (priRsa = PEM_read_RSA_PUBKEY(fp, &priRsa, NULL,NULL)))
    {
        printf("读出私钥内容出错\n");
        fclose(fp);
        return NULL;
    }
    fclose(fp);
    printf("提取公钥\n");
    pOut = priRsa;
    return pOut;
}

int main(void)
{
    int     flen,rsa_len, ienLen, iRet;
    RSA     *prsa = NULL;
    char    szEnData[]="orderId=01010500201502000004reqTime=20150205012727ext=20151120ext2=1";
    char    szTmp[10240], szTmp1[10240];

    if(NULL == (prsa = getPrivateKey("userkey.pem")))
    {
        RSA_free(prsa);
        printf("获取私钥失败\n");
        return -1;
    }

//  RSA_print_fp(stdout, prsa, 11);
    flen = strlen(szEnData);
    printf("待签名数据:[%s]\n", szEnData);

    memset(szTmp, 0, sizeof(szTmp));
    memset(szTmp1, 0, sizeof(szTmp1));
    //  对待签名数据做SHA1摘要
    SHA1(szEnData, flen, szTmp);
    //使用私钥对SHA1摘要做签名
    ienLen = RSA_sign(NID_sha1, (unsigned char *)szTmp, 20, (unsigned char*)szTmp1, &iRet, prsa);
    if(ienLen != 1 )
    {
        printf("签名失败\n");
        RSA_free(prsa);
        return -1;
    }
    RSA_free(prsa);
    printf("签名成功\n");
    //签名串szTmp1二进制数据需要转成base64编码
    //mac=base64encode(szTmp1)这是伪码,生产MAC值,给对方去校验



    //验证签名
    //验证签名的是需要获取MAC值,明文签名数据,对“明文签名数据”做SHA1,获得摘要。在对MAC做basedecode(mac),然后调用函数验证签名
    if(NULL == (prsa = getPublicKey("userpub.key")))
    {
        RSA_free(prsa);
        printf("获取私钥失败\n");
        return -1;
    }
    flen = strlen(szEnData);
    printf("待签名数据:[%s]\n", szEnData);//签名数据 和 mac 因该是由通信报文中获得,这里演示直接用使用同一变量

    memset(szTmp, 0, sizeof(szTmp));
//    memset(szTmp1, 0, sizeof(szTmp1));
    //  对待签名数据做SHA1摘要
    SHA1(szEnData, flen, szTmp);

    ienLen = RSA_verify(NID_sha1, (unsigned char *)szTmp, 20, (unsigned char*)szTmp1, iRet, prsa);
    if(ienLen != 1 )
    {
        printf("签名不合法\n");
        RSA_free(prsa);
        return -1;
    }
    else
        printf("验签成功\n");
    RSA_free(prsa);
    return 0;
}
Licensed under CC BY-NC-SA 4.0
最后更新于 2024-05-03 07:44 UTC
 赣ICP备18015110号-1   
使用 Hugo 构建 主题 StackJimmy 设计