在 C 中读取和写入 rsa 密钥到 pem 文件

Posted

技术标签:

【中文标题】在 C 中读取和写入 rsa 密钥到 pem 文件【英文标题】:Reading and writing rsa keys to a pem file in C 【发布时间】:2012-09-20 18:20:30 【问题描述】:

我正在编写一个 C 程序来为 RSA 生成密钥并将它们写入文件,然后从中读取。作业要求我生成 openssl 格式的文件。所以,我选择了PEM。现在,我有以下创建文件的功能

rsa = RSA_new();
// These 3 keys are generated beforehand
rsa->e = e;
rsa->n = n;
rsa->d = d;

fp = fopen(pubkey_file, "w");
if(!PEM_write_RSAPublicKey(fp, rsa))

    printf("\n%s\n", "Error writing public key");

fflush(fp);
fclose(fp);

fp = fopen(privkey_file, "w");
// pRsaKey = EVP_PKEY_new();
// EVP_PKEY_assign_RSA(pRsaKey, rsa);
if(!PEM_write_RSAPrivateKey(fp, rsa, NULL, 0, 0, NULL, NULL))
// if (!PEM_write_PrivateKey(fp, pRsaKey, NULL, NULL, 0, 0, NULL))

    printf("\n%s\n", "Error writing private key");

fflush(fp);
fclose(fp);

这是读取文件的功能

rsa = RSA_new();
fp = fopen(pubkey_file, "r");
if(PEM_read_RSAPublicKey(fp, &rsa, NULL, NULL) == NULL)

    printf("\n%s\n", "Error Reading public key");
    return;


fclose(fp);
BN_bn2bin(rsa->n, (unsigned char *)modulus);
BN_bn2bin(rsa->e, (unsigned char *)exp);
printf("\n%s\n%s\n", exp, modulus);
RSA_free(rsa);

// pRsaKey = EVP_PKEY_new();
fp = fopen(privkey_file, "r");
if(fp)
    // if((PEM_read_PrivateKey(fp, &pRsaKey, NULL, NULL)) == NULL)
    if((PEM_read_RSAPrivateKey(fp, &rsa, NULL, NULL)) == NULL)
    
        printf("\n%s\n", "Error Reading private key");
        return;
    
// rsa = RSA_new();
// rsa = EVP_PKEY_get1_RSA(pRsaKey);
fclose(fp);

根据需要写入和读取公钥,但 provate 密钥失败。我尝试过同时使用 rsa 和 evp 进行编写(在上面的代码中对此进行了注释)。但是,两者都失败了。我无法理解为什么会发生这种情况,也无法尝试找到调试此问题的位置。任何人都可以为此提供一些指示吗?

【问题讨论】:

我会调查是否有功能可以提取错误,以便您了解这里发生的情况。调用 fopen 后还要检查 fp 并在那里打印任何错误。照原样,无法调试。 我不熟悉您正在使用的工具包,但 PKCS#1 需要的不仅仅是 en 和 d(我最后还是检查了)。 PEM 真正在做的就是采用 PKCS#1 DER 编码、b64 编码并标记一些页眉和页脚。是否有可能在保存之前关键数据没有完全充实? @LucasHolt 我检查了文件指针。他们没有任何错误。但是,只有读取私钥的函数调用会导致错误。 @WhozCraig 我会调查的。但是,据我所知,RSA 对象只需要 n、e、d。休息可以为空。 openssl.org/docs/crypto/rsa.html#DESCRIPTION 因此,我假设它应该将对象写入文件(它确实如此)。它读回来,这是一个问题 【参考方案1】:

RSA 密钥是对称的,您可以将它们中的任何一个用作私钥或公钥,这只是您的选择(但 DSA 密钥对称)。下面的程序生成两个 2048 位长的 RSA 密钥,然后将它们保存到文件中并将它们读回内存。这应该让你知道如何去做。

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/pem.h>

const char* pcszPassphrase = "open sezamee";

static void gen_callback(int iWhat, int inPrime, void* pParam);
static void init_openssl(void);
static void cleanup_openssl(void);
static int passwd_callback(char *pcszBuff,int size,int rwflag, void *pPass);
static EVP_PKEY* create_rsa_key(void);
static void handle_openssl_error(void);

int main(int argc, char **argv)

    int iRet = EXIT_SUCCESS;
    EVP_PKEY* pPrivKey = NULL;
    EVP_PKEY* pPubKey  = NULL;
    FILE*     pFile    = NULL;
    const EVP_CIPHER* pCipher = NULL;
    init_openssl();

    pPrivKey = create_rsa_key();
    pPubKey  = create_rsa_key();

    if(pPrivKey && pPubKey)
    /* Save the keys */
        if((pFile = fopen("privkey.pem","wt")) && (pCipher = EVP_aes_256_cbc()))
        

            if(!PEM_write_PrivateKey(pFile,pPrivKey,pCipher,
                                    (unsigned char*)pcszPassphrase,
                                    (int)strlen(pcszPassphrase),NULL,NULL))
            
                fprintf(stderr,"PEM_write_PrivateKey failed.\n");
                handle_openssl_error();
                iRet = EXIT_FAILURE;
            
            fclose(pFile);
            pFile = NULL;
            if(iRet == EXIT_SUCCESS)
            
                if((pFile = fopen("pubkey.pem","wt")) && PEM_write_PUBKEY(pFile,pPubKey))
                    fprintf(stderr,"Both keys saved.\n");
                else
                
                    handle_openssl_error();
                    iRet = EXIT_FAILURE;
                
                if(pFile)
                
                    fclose(pFile);
                    pFile = NULL;
                
            
        
        else
        
            fprintf(stderr,"Cannot create \"privkey.pem\".\n");
            handle_openssl_error();
            iRet = EXIT_FAILURE;
            if(pFile)
            
                fclose(pFile);
                pFile = NULL;
            
        
        if(iRet == EXIT_SUCCESS)
        /* Read the keys */
            EVP_PKEY_free(pPrivKey);
            pPrivKey = NULL;
            EVP_PKEY_free(pPubKey);
            pPubKey = NULL;

            if((pFile = fopen("privkey.pem","rt")) && 
               (pPrivKey = PEM_read_PrivateKey(pFile,NULL,passwd_callback,(void*)pcszPassphrase)))
            
                fprintf(stderr,"Private key read.\n");
            
            else
            
                fprintf(stderr,"Cannot read \"privkey.pem\".\n");
                handle_openssl_error();
                iRet = EXIT_FAILURE;
            
            if(pFile)
            
                fclose(pFile);
                pFile = NULL;
            

            if((pFile = fopen("pubkey.pem","rt")) && 
               (pPubKey = PEM_read_PUBKEY(pFile,NULL,NULL,NULL)))
            
                fprintf(stderr,"Public key read.\n");
            
            else
            
                fprintf(stderr,"Cannot read \"pubkey.pem\".\n");
                handle_openssl_error();
                iRet = EXIT_FAILURE;
            
        
    

    if(pPrivKey)
    
        EVP_PKEY_free(pPrivKey);
        pPrivKey = NULL;
    
    if(pPubKey)
    
        EVP_PKEY_free(pPubKey);
        pPubKey = NULL;
    
    cleanup_openssl();
    return iRet;


EVP_PKEY* create_rsa_key(void)

    RSA *pRSA      = NULL;
    EVP_PKEY* pKey = NULL;
    pRSA = RSA_generate_key(2048,RSA_3,gen_callback,NULL);
    pKey = EVP_PKEY_new();
    if(pRSA && pKey && EVP_PKEY_assign_RSA(pKey,pRSA))
    
        /* pKey owns pRSA from now */
        if(RSA_check_key(pRSA) <= 0)
        
            fprintf(stderr,"RSA_check_key failed.\n");
            handle_openssl_error();
            EVP_PKEY_free(pKey);
            pKey = NULL;
        
    
    else
    
        handle_openssl_error();
        if(pRSA)
        
            RSA_free(pRSA);
            pRSA = NULL;
        
        if(pKey)
        
            EVP_PKEY_free(pKey);
            pKey = NULL;
        
    
    return pKey;


void gen_callback(int iWhat, int inPrime, void* pParam)

    char c='*';
    switch(iWhat)
    
        case 0: c = '.';  break;
        case 1: c = '+';  break;
        case 2: c = '*';  break;
        case 3: c = '\n'; break;
    
    fprintf(stderr,"%c",c);


int passwd_callback(char *pcszBuff,int size,int rwflag, void *pPass)

    size_t unPass = strlen((char*)pPass);
    if(unPass > (size_t)size)
        unPass = (size_t)size;
    memcpy(pcszBuff, pPass, unPass);
    return (int)unPass;


void init_openssl(void)

    if(SSL_library_init())
    
        SSL_load_error_strings();
        OpenSSL_add_all_algorithms();
        RAND_load_file("/dev/urandom", 1024);
    
    else
        exit(EXIT_FAILURE);


void cleanup_openssl(void)

    CRYPTO_cleanup_all_ex_data();
    ERR_free_strings();
    ERR_remove_thread_state(0);
    EVP_cleanup();


void handle_openssl_error(void)

    ERR_print_errors_fp(stderr);

【讨论】:

HI @Sirgeorge 我已在我的应用程序中添加了此代码,但出现错误:“#include 文件未找到”如何解决此错误。在此先感谢... @Nitesh Meshram:你安装了 openssl 吗?我猜你没有。 - 不,我已经下载了 openssl 并将其添加到我的 Xcode 项目中?没问题还是安装是什么意思?? 请注意下面的其他答案,此处生成的 2 个 RSA 密钥彼此不匹配 -- RSA_generate_key 不应被调用两次。【参考方案2】:

sirgeorge 给出的答案在 Code 中有错误。 create_rsa_key 不应被调用两次。如果它被调用两次,那么私钥没有匹配的公钥。这会导致解密期间出现问题。 main 方法中需要修改

RSA *pRSA      = NULL;
pRSA = RSA_generate_key(2048,RSA_3,gen_callback,NULL);    
pPrivKey = create_rsa_key(pRSA);
pPubKey  = create_rsa_key(pRSA);

create_rsa_key 中需要修改

EVP_PKEY* create_rsa_key(RSA *pRSA)

    EVP_PKEY* pKey = NULL;
    pKey = EVP_PKEY_new();
    if(pRSA && pKey && EVP_PKEY_assign_RSA(pKey,pRSA))
    
        /* pKey owns pRSA from now */
        if(RSA_check_key(pRSA) <= 0)
        
            fprintf(stderr,"RSA_check_key failed.\n");
            handle_openssl_error();
            EVP_PKEY_free(pKey);
            pKey = NULL;
        
    
    else
    
        handle_openssl_error();
        if(pRSA)
        
            RSA_free(pRSA);
            pRSA = NULL;
        
        if(pKey)
        
            EVP_PKEY_free(pKey);
            pKey = NULL;
        
    
    return pKey;

【讨论】:

好收获。我很惊讶一直没有人对此发表评论。 谢谢 jww .. 其实我在使用这段代码时偶然发现了这个问题 请编辑并删除“eEVP_PKEY* pKey = NULL;”中多余的'e' @Rndp13:已修复

以上是关于在 C 中读取和写入 rsa 密钥到 pem 文件的主要内容,如果未能解决你的问题,请参考以下文章

OpenSSL笔记-生成RSA公私密钥以PEM格式到char*中(非保存为文件)

为啥我无法使用 PEM_read_RSAPublicKey 读取 openssl 生成的 RSA pub 密钥?

C# 将私有/公共 RSA 密钥从 RSACryptoServiceProvider 导出到 PEM 字符串

如何使用pkcs11接口读取证书的密钥进行RSA

将 PEM RSA 密钥转换为 C# RSACryptoServiceProvider.FromXmlString 的 xml

全面解决.Net与Java互通时的RSA加解密问题,使用PEM格式的密钥文件