weidai11 / cryptopp

free C++ class library of cryptographic schemes
https://cryptopp.com
Other
4.8k stars 1.49k forks source link

Suggestion : Could you ease interop with the windows API ? #479

Closed malickf closed 7 years ago

malickf commented 7 years ago

I have a suggestion, I think it could be very nice to ease interop with the windows API.

This API has a windows blob key structure, that I believe could be used to instantiate cryptopp keys. Conversely, I think it could be nice to be able to export keys as windows blob.

This will also ease interop with the c# world as it is based on this API.

I do not know enough cryptopp and cryptography to do it myself, it is just a suggestion. In any cases, thanks A LOT for maintaining and developing this libray.

A pseudo example of what I'm thinking for the public blob key (blob encoded as a 64bit string but we could let the raw blob) for RSA digital signature :


CryptoPP::RSA::PublicKey LoadPublicWindowsBlob(string public_key_blob_64_bit_encoded )
{
                //1024 rsa with 512 rsa
             byte  decoded_Blob[148] ;
         size_t size_pubkey = Base64Decode(public_key_blob_64_bit_encoded, decoded_Blob, 148);
         byte modulus[128];
         byte modulusreversed[128];
         byte pubexp[4]  ;

         pubexp[0] = decoded_Blob[19];
         pubexp[1] = decoded_Blob[18];
         pubexp[2] = decoded_Blob[17];
         pubexp[3] = decoded_Blob[16];

         for (int i = 0; i < 128; i++)
             modulus[i] = decoded_Blob[20 + i];
         for (int i = 0; i < 128; i++)
             modulusreversed[i] = modulus[128 - 1 - i];
            CryptoPP::Integer pub(pubexp,4);
            CryptoPP::Integer mod(modulusreversed,128);
            CryptoPP::InvertibleRSAFunction params;
            params.SetModulus(mod);
            params.SetPublicExponent(pub);
            CryptoPP::RSA::PublicKey publicKey2(params);
            return publicKey2;
}

Edit : based on this structure :

PUBLICKEYSTRUC  publickeystruc;
RSAPUBKEY rsapubkey;
BYTE modulus[rsapubkey.bitlen/8];         

with :


typedef struct _PUBLICKEYSTRUC {
  BYTE   bType;
  BYTE   bVersion;
  WORD   reserved;
  ALG_ID aiKeyAlg; (unsigned int)
} BLOBHEADER, PUBLICKEYSTRUC;

typedef struct _RSAPUBKEY {
  DWORD magic;     
  DWORD bitlen;   
  DWORD pubexp;   
} RSAPUBKEY;

see :

noloader commented 7 years ago

Can you provide a complete example? You know, something with a main that I can run?

Shooting from the hip, it seems like a good topic for documentation in the wiki.

We added additional Integer constructors intended to help with the little-endian byte array. See integer.h : 145. Also see Integer on the Crypto++ wiki.

malickf commented 7 years ago

Thanks for your answer and details about integers. See below a more advanced example. I included the windows api in headers in order to be able to cast the blob to its corresponding structure .

I think that using the msdn documentation it should be possible to create a real import/export class or function for blobs.

Full example :

In c# to get blob and signature in 64 bit encoded string :

using System;
using System.Security.Cryptography;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {

            var messageToSign = "hello";

            RSAParameters privateKeyInfo;
            RSAParameters publicKeyInfo;
            CspParameters parameters = new CspParameters();
            parameters.KeyNumber = (int)KeyNumber.Signature;
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(1024, parameters))
            {
                rsa.PersistKeyInCsp = false;
                privateKeyInfo = rsa.ExportParameters(true);
                publicKeyInfo = rsa.ExportParameters(false);
            }

            var bytes = System.Text.Encoding.UTF8.GetBytes(messageToSign);
            byte[] signedHash;
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
            {
                // Import the key information.
                rsa.ImportParameters(privateKeyInfo);

                var cspBlob = rsa.ExportCspBlob(false); // false -> export only public key

                // [A] export the key as a window blob 64 bit encoded string 
                var cspBlobBase64Signature = Convert.ToBase64String(cspBlob);
                Console.WriteLine(String.Format("CSP Blob : {0} ", cspBlobBase64Signature));

                // Sign the data, using SHA512 as the hashing algorithm 
                signedHash = rsa.SignData(bytes, CryptoConfig.MapNameToOID("SHA512"));

                //[B] export the signed hash as a 64 bit encoded string 
                var signatureBase64Signature = Convert.ToBase64String(signedHash);
                Console.WriteLine(String.Format("Signature : {1} ", signatureBase64Signature));

            }

        }
    }
}

C++ :

#include "stdafx.h"

//cryptopp
#include "md5.h"
#include <cryptlib.h>
#include <sha.h>
#include <filters.h>
#include <rsa.h>
#include <files.h>
#include <base64.h>
#include <osrng.h>

//windowsAPI
#include "windows.h"
#include "minwindef.h"
#include "Wincrypt.h"

using CryptoPP::Integer;
using CryptoPP::StringSource;
using CryptoPP::Base64Decoder;
using namespace std;

using std::vector;

#pragma comment(lib, "cryptlibmtd")

bool Get_rsa_publicKey_from_windows_blob(std::string public_key_blob_64_bit_encoded, CryptoPP::RSA::PublicKey &publickey);

int main()
{

    string message = "hello";

    std::string public_key_blob_64_bit_encoded = "BgIAAACkAABSU0ExAAQAAAEAAQBNvUNW8njYzN8gMEIBJUnK9DczJ9ScEza/gehdIGhNVOUVE3iG8c5vO7zKhcFLthev0ysqMW/E10STW+YLDyfkHMWHy1Ai6AvvjY0zhluePzwPhXp2jXwxPCbkxdTjP2p/ngWBOs/PjJPP1MbARYMpjAajTlQT/k3cKcGN6bwRwA==";
    std::string signature_64bit = "P3Uw36KgQ7ucngODaxoA2EG2qAR1qbwmlYGFLcMVCCvwBzNUQzTh6Pxa1FKhLeqoFn3siFGIfdus9Av5H5Pbe4gxllts/kIEmhVpUr1kGqfNX/x1AeCKUw+aZQaSunUEiUHTLvd7LufkkcGPTGJhHdo2M9J02bzBNsKKqQBFJ4w=";

    CryptoPP::RSA::PublicKey publicKey;

    if(!Get_rsa_publicKey_from_windows_blob(public_key_blob_64_bit_encoded, publicKey))
    {
        cout << "failure to get public key from blob ... " <<  endl;
        return -1;
    }

    // decode signature to std::string
    string decoded_signature;
    Base64Decoder decoder;
    decoder.Put( (byte*)signature_64bit.data(), signature_64bit.size() );
    decoder.MessageEnd();
    CryptoPP::word64 size = decoder.MaxRetrievable();
    decoded_signature.resize(size);     
    decoder.Get((byte*)decoded_signature.data(), decoded_signature.size());

    CryptoPP::RSASS<CryptoPP::PKCS1v15, CryptoPP::SHA512>::Verifier verif(publicKey);
    bool res = verif.VerifyMessage( reinterpret_cast<const byte*>(message.c_str()), message.length(), reinterpret_cast<const byte*>(decoded_signature.c_str()), decoded_signature.length() ); 

    if(res)
        cout << "OK Message verified " <<endl;
    else
         cout << " Bad signature " <<endl;

    return 0;
}

bool Get_rsa_publicKey_from_windows_blob(std::string public_key_blob_64_bit_encoded, CryptoPP::RSA::PublicKey &publickey)
{

    // Decode public blob and convert it to vector<byte>
    StringSource blob64(public_key_blob_64_bit_encoded, true, new Base64Decoder());
    Integer blob_integer(blob64, blob64.MaxRetrievable());
    size_t sz = blob_integer.MinEncodedSize();
    vector<byte> vector_blob;
    vector_blob.resize(sz);
    blob_integer.Encode(&vector_blob[0], vector_blob.size());

    PUBLICKEYSTRUC* ptr_pubkeustruc ;
    RSAPUBKEY * ptr_RSAPUBKEY;
    ptr_pubkeustruc = reinterpret_cast<PUBLICKEYSTRUC*>(&vector_blob[0]);
    BYTE bType = ptr_pubkeustruc->bType;

    if (bType != PUBLICKEYBLOB)
    {
        cout << "Invalid Blob - not a public blob " <<  endl;
        return false;
    }

    ALG_ID alg = ptr_pubkeustruc->aiKeyAlg;

    if (alg == CALG_RSA_KEYX) //  "RSA public key exchange algorithm  "
    {

        ptr_RSAPUBKEY = reinterpret_cast<RSAPUBKEY*>(&vector_blob[sizeof(PUBLICKEYSTRUC)]);
        DWORD bitlen = ptr_RSAPUBKEY->bitlen;
        DWORD pubexp = ptr_RSAPUBKEY->pubexp;
        byte* exp = reinterpret_cast<byte*>(&pubexp);

        if(bitlen != 1024)
        {
        cout << "Not 1024 rsa key ... " <<  endl;
        return false;
        }

        BYTE*  modulus = &vector_blob[sizeof(PUBLICKEYSTRUC) + sizeof(RSAPUBKEY)];// not sure if problem of paddings can occurs here ...

        Integer pub(exp,4,Integer::UNSIGNED,CryptoPP::LITTLE_ENDIAN_ORDER);
        Integer mod(modulus,128,Integer::UNSIGNED,CryptoPP::LITTLE_ENDIAN_ORDER);

        CryptoPP::InvertibleRSAFunction params;
        params.SetModulus(mod);
        params.SetPublicExponent(pub);
        CryptoPP::RSA::PublicKey pK(params); 
        CryptoPP::AutoSeededRandomPool rnd;  

        if(!pK.Validate(rnd, 3))
        {

            std::cout << "Invalid pubkey " << std::endl;
            return false;
        }
        publickey  =  pK;
        return true;

    }
    return false;
}
noloader commented 7 years ago

@malickf,

What do you suggest we do? Do you have specific actionable items?

I think the choices are:

  1. do nothing
  2. add a filter and/or a source and sink
  3. add a wiki page with sample code
  4. create a patch and place it on the patch page
  5. something else?

I have some code that provides access to Windows DPAPI. It seemed too specialized for Crypto++, so I took the course of (4) and added it as a patch. You can find it at Protected Storage Pack.

malickf commented 7 years ago

@noloader I had some difficulties to interop with the windows API and cryptopp, even-those I finally succeed, mainly because I couldn't find a place summarizing info related to windows . I believe that the 3rd option (wiki page) could be a nice choice. Especially, it could be valuable to get a page that present briefly the different ways a user can take to initialize public/private key ( from raw values (exponent=....., modulus.... obtained via the FromXmlString() ) , from a blob, the preferable format/certificate to use to export/import keys ... etc dedicated to windows.

A "quick start guide for interop with Windows API" in short. With some specific informations (such as the PKCS1_v1.5 signature is required for RSA signature, maybe some compilation specific remarks, a remark about little-endian ...).

Also all cryptopp resources related to windows could be listed, such as your protected Storage Pack.

Thanks

noloader commented 7 years ago

Closing. I'll add a wiki page if I have some time.