How to translate pem public key into openssl RSA* structure

10,076

Solution 1

I can't use BIO because i just want to transplant openssl into a bootloader which doesn't have a UNIX filesystem

I think you can still use a BIO, it just has to be a memory BIO, and not a file BIO.

If you can't use a memory BIO either, then I'm not sure you can use OpenSSL here.


How to translate pem public key into openssl RSA* structure

Use PEM_read_PUBKEY or PEM_read_bio_PUBKEY (or one of the *_PUBKEY routines). They return an EVP_PKEY. Then, use EVP_PKEY_get1_RSA to convert it to a RSA.

Something like:

static const char key[] = "-----BEGIN PUBLIC KEY-----\n"
    "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7vbqajDw4o6gJy8UtmIbkcpnk\n"
    "O3Kwc4qsEnSZp/TR+fQi62F79RHWmwKOtFmwteURgLbj7D/WGuNLGOfa/2vse3G2\n"
    "eHnHl5CB8ruRX9fBl/KgwCVr2JaEuUm66bBQeP5XeBotdR4cvX38uPYivCDdPjJ1\n"
    "QWPdspTBKcxeFbccDwIDAQAB\n"
    "-----END PUBLIC KEY-----\n";

BIO* bio = BIO_new_mem_buf(key, (int)sizeof(key));
ASSERT(bio != NULL);

EVP_PKEY* pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
ASSERT(pkey != NULL);

int type = EVP_PKEY_get_type(pkey);
ASSERT(type == EVP_PKEY_RSA || type == EVP_PKEY_RSA2);

RSA* rsa = EVP_PKEY_get1_RSA(pkey);
ASSERT(rsa != NULL);

...

EVP_PKEY_free(pkey);
RSA_free(rsa);
BIO_free(bio);

And here's the helper you need. EVP_PKEY_get_type is not part of the library:

int EVP_PKEY_get_type(EVP_PKEY *pkey)
{
    ASSERT(pkey);
    if (!pkey)
        return EVP_PKEY_NONE;

    return EVP_PKEY_type(pkey->type);
}

Related, you can also use the non-PEM version, which is the ASN.1/DER encoding of the key. This will allow you to save some size in the image. Then, you would use d2i_PUBKEY_bio to read the key.

Converting from PEM to ASN.1/DER will save about 110 bytes:

$ ls -al pubkey.*
-rw-r--r--  1 user  staff  162 Jun  5 00:36 pubkey.der
-rw-r--r--  1 user  staff  272 Jun  5 00:52 pubkey.pem

To convert the PEM encoded public key to ASN.1/DER, follow these steps. First, copy the key to the clipboard. Second, pipe it into openssl pkey with a tool like pbpaste on OS X or xclip on Linux:

$ pbpaste | openssl pkey -pubin -inform PEM -out pubkey.der -outform DER

Then, inspect the public key:

 $ dumpasn1 pubkey.der 
  0 159: SEQUENCE {
  3  13:   SEQUENCE {
  5   9:     OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
 16   0:     NULL
       :     }
 18 141:   BIT STRING, encapsulates {
 22 137:     SEQUENCE {
 25 129:       INTEGER
       :         00 BB BD BA 9A 8C 3C 38 A3 A8 09 CB C5 2D 98 86
       :         E4 72 99 E4 3B 72 B0 73 8A AC 12 74 99 A7 F4 D1
       :         F9 F4 22 EB 61 7B F5 11 D6 9B 02 8E B4 59 B0 B5
       :         E5 11 80 B6 E3 EC 3F D6 1A E3 4B 18 E7 DA FF 6B
       :         EC 7B 71 B6 78 79 C7 97 90 81 F2 BB 91 5F D7 C1
       :         97 F2 A0 C0 25 6B D8 96 84 B9 49 BA E9 B0 50 78
       :         FE 57 78 1A 2D 75 1E 1C BD 7D FC B8 F6 22 BC 20
       :         DD 3E 32 75 41 63 DD B2 94 C1 29 CC 5E 15 B7 1C
       :         0F
157   3:       INTEGER 65537
       :       }
       :     }
       :   }

Finally, do something like:

int main(int argc, char* argv[])
{
    UNUSED(argc), UNUSED(argv);

    static const unsigned char key[] = {
        0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
        0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0xbd, 0xba,
        0x9a, 0x8c, 0x3c, 0x38, 0xa3, 0xa8, 0x09, 0xcb, 0xc5, 0x2d, 0x98, 0x86, 0xe4, 0x72, 0x99, 0xe4,
        0x3b, 0x72, 0xb0, 0x73, 0x8a, 0xac, 0x12, 0x74, 0x99, 0xa7, 0xf4, 0xd1, 0xf9, 0xf4, 0x22, 0xeb,
        0x61, 0x7b, 0xf5, 0x11, 0xd6, 0x9b, 0x02, 0x8e, 0xb4, 0x59, 0xb0, 0xb5, 0xe5, 0x11, 0x80, 0xb6,
        0xe3, 0xec, 0x3f, 0xd6, 0x1a, 0xe3, 0x4b, 0x18, 0xe7, 0xda, 0xff, 0x6b, 0xec, 0x7b, 0x71, 0xb6,
        0x78, 0x79, 0xc7, 0x97, 0x90, 0x81, 0xf2, 0xbb, 0x91, 0x5f, 0xd7, 0xc1, 0x97, 0xf2, 0xa0, 0xc0,
        0x25, 0x6b, 0xd8, 0x96, 0x84, 0xb9, 0x49, 0xba, 0xe9, 0xb0, 0x50, 0x78, 0xfe, 0x57, 0x78, 0x1a,
        0x2d, 0x75, 0x1e, 0x1c, 0xbd, 0x7d, 0xfc, 0xb8, 0xf6, 0x22, 0xbc, 0x20, 0xdd, 0x3e, 0x32, 0x75,
        0x41, 0x63, 0xdd, 0xb2, 0x94, 0xc1, 0x29, 0xcc, 0x5e, 0x15, 0xb7, 0x1c, 0x0f, 0x02, 0x03, 0x01,
        0x00, 0x01
    };

    BIO* bio = BIO_new_mem_buf(key, (int)sizeof(key));
    ASSERT(bio != NULL);

    EVP_PKEY* pkey = d2i_PUBKEY_bio(bio, NULL);
    ASSERT(pkey != NULL);

    int type = EVP_PKEY_get_type(pkey);
    ASSERT(type == EVP_PKEY_RSA || type == EVP_PKEY_RSA2);

    RSA* rsa = EVP_PKEY_get1_RSA(pkey);
    ASSERT(rsa != NULL);

    // ...

    EVP_PKEY_free(pkey);
    RSA_free(rsa);
    BIO_free(bio);

    return 0;
}

Solution 2

To load the RSA pub key from the pem file

int loadKey(string &fileName, RSA ** pubkey)
{

  FILE *fp = fopen(fileName.data(), "r");
  if (fp < 0){
   printf("%s\n","Invalide certificate file name");
   return -1;
 }

 *pubkey = PEM_read_RSA_PUBKEY(fp,pubkey, NULL, NULL);

 if(*pubkey == NULL){
   printf("%s\n","error reading Public key");
   return -1;
 }

To verify the signature

int verify_signature(vector<uint8_t> &msg, vector<uint8_t> &sig, RSA* pkey)
{
  /* Returned to caller */
  int result = -1;
  vector <uint8_t> pDecrypted(PACKAGE_SIGNATURE_SIZE);


  result = RSA_public_decrypt(PACKAGE_SIGNATURE_SIZE, sig.data(), pDecrypted.data(), pkey, RSA_NO_PADDING);
  if (result == -1)
  {
     printf("RSA_public_decrypt failed with error %s\n", ERR_error_string(ERR_get_error(), NULL));
    //  goto prog_end;
  }


  /* verify the data */
  result = RSA_verify_PKCS1_PSS(pkey,msg.data() , EVP_sha256(), pDecrypted.data(), -2 /* salt length recovered from signature*/);
  if (result == 1)
  {
     printf("Signature verification successfull!\n");
  }
  else
  {
     printf("RSA_verify_PKCS1_PSS failed with error %s\n", ERR_error_string(ERR_get_error(), NULL));
  }

  return !!result;

}
Share:
10,076
demonguy
Author by

demonguy

Updated on June 04, 2022

Comments

  • demonguy
    demonguy almost 2 years

    Suppose i have to public pem key like this

    -----BEGIN PUBLIC KEY-----
    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7vbqajDw4o6gJy8UtmIbkcpnk
    O3Kwc4qsEnSZp/TR+fQi62F79RHWmwKOtFmwteURgLbj7D/WGuNLGOfa/2vse3G2
    eHnHl5CB8ruRX9fBl/KgwCVr2JaEuUm66bBQeP5XeBotdR4cvX38uPYivCDdPjJ1
    QWPdspTBKcxeFbccDwIDAQAB
    -----END PUBLIC KEY-----
    

    And i want to use openssl

    int RSA_public_decrypt(int flen, const unsigned char *from, unsigned char *to,RSA *rsa, int padding). How can i translate the pem key to RSA *rsa structure?

    FYI: I can't use BIO because i just want to transplant openssl into a bootloader which doesn't have a UNIX filesystem. The only way i can do is translate public key into a C array.

  • Greg Wittmeyer
    Greg Wittmeyer about 7 years
    EVP_PKEY_base_id() appears to do exactly what your EVP_PKEY_get_type() function does. Why they named it that, I don't know.
  • jww
    jww about 7 years
    @Gammadyne - yeah, I think you are right. Be careful if you use it. It will try to dereference a NULL pointer.