SHA1WithRSA簽名使用openssl 實現

引言:
2017年就要到了,想想自己使用阿里雲搭的博客 眼看就要到期了。 雖說沒寫幾遍有質量的文章吧,但是放其不管也於心不忍。這幾天就琢磨着把幾遍有內容的轉到博客中。在尋求着落點的時候發現Markdown。看了CSDN中Markdown的範文,再看看自己曾經寫的文章。只能呵呵了
想着就藉此機會學習一下Markdown的話語。


最近一個項目對接。要使用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;
}

源碼和祕鑰文件資源

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章