Search code examples
crypto++

DER Encoding Public Key created by cryptopp is different then openssl


I've been trying to create a DER encoded public key using an RSA private key. The way I normally create it is using the command line:

openssl rsa -pubout -outform DER -in ~/.keys/api_key.pem -out der_pub.der

When I use CryptoPP to create this file, they are slightly different. It seems it has an extra section. The one created by openssl has a little extra section. I'm assuming this is the BIT STRING mentioned in the CryptoPP API. https://www.cryptopp.com/docs/ref/class_r_s_a_function.html

void    DEREncodePublicKey (BufferedTransformation &bt) const
encode subjectPublicKey part of subjectPublicKeyInfo, without the BIT STRING header 

This is what my code looks like:

    ...
    CryptoPP::RSA::PrivateKey rsaPrivate;
    rsaPrivate.BERDecodePrivateKey(queue, false /*paramsPresent*/, queue.MaxRetrievable());

    CryptoPP::ByteQueue bq;
    rsaPrivate.DEREncodePublicKey(bq);
    CryptoPP::FileSink fs1("cryptopp_pub.der", true);
    bq.TransferTo(fs1);

Solution

  • CryptoPP::RSA::DEREncodePublicKey encodes subjectPublicKey part of subjectPublicKeyInfo, without the BIT STRING header

    Try CryptoPP::RSA::PublicKey::DEREncode. Being careful to apply this to only the public key, as RSA::PrivateKey does overload the DEREncode method.

    Here I'm using CryptoPP 8.2

    1. Load DER encoded private key from disk

      CryptoPP::RSA::PrivateKey private_key;
      {
          CryptoPP::FileSource file{"my.key", true};
          private_key.BERDecodePrivateKey(file, false, -1);
      }
      
    2. Save out DER encoded public key

      CryptoPP::FileSink sink{"my.pub", true};
      CryptoPP::RSA::PublicKey{private_key}.DEREncode(sink);
      

    OpenSSL:

    # generate a new RSA private key (DER format)
    openssl genrsa | openssl rsa -outform DER -out my.key
    
    # hash/fingerprint the public key
    openssl rsa -in my.key -inform DER -pubout -outform DER | openssl sha256
    writing RSA key
    362945ad4a5f87f27d3db3b4adbacaee0ebc3f778ee2fe76ef4fb09933148372
    
    # compare against hash of our code sample's generated public key
    cat my.pub | openssl sha256
    362945ad4a5f87f27d3db3b4adbacaee0ebc3f778ee2fe76ef4fb09933148372
    

    As another example; if we want CryptoPP to generate a SHA256 fingerprint:

    std::string hash_out_str;
    {
        CryptoPP::SHA256 sha256;
        CryptoPP::HashFilter filter{
            sha256,
            new CryptoPP::HexEncoder{
                new CryptoPP::StringSink{hash_out_str}
            }
        };
        CryptoPP::RSA::PublicKey{private_key}.DEREncode(filter); // intentionally slice to ensure we aren't exposing a public key
        filter.MessageEnd();
    }
    std::cout << hash_out_str << '\n';
    

    Outputs:

    362945AD4A5F87F27D3DB3B4ADBACAEE0EBC3F778EE2FE76EF4FB09933148372
    

    i.e., we need to copy/slice to a RSA::PublicKey to invoke the OpenSSL compatible DER encoding method