grijjy / DelphiOpenSsl

Using OpenSsl with Delphi on all OS platforms for X.509 certificates and JWT
Other
97 stars 23 forks source link

Functions for JWK to RSA PEM conversion #1

Open geoffsmith82 opened 4 years ago

geoffsmith82 commented 4 years ago

Hi Just thought I would add these 2 example functions to show the hard parts of converting an RSA PEM file to a JWK file as well as a JWK to a RSA PEM file.

For the missing TBase64URLURLEncoding class, have a look at

https://quality.embarcadero.com/browse/RSP-20316

which has instructions on how to create the class based on the System.NetEncoding.pas file included with Delphi.


// These declarations could be added to the OpenSsl.Api_11.pas file
function BN_bn2bin(ARet: PBIGNUM; ABin: Pointer): Integer; cdecl external LIB_CRYPTO name PREFIX + 'BN_bn2bin';
function BIO_ctrl(bp: PBIO; cmd: Integer; larg: longint; parg: Pointer): longint; cdecl external LIB_CRYPTO name PREFIX + 'BIO_ctrl';
function BIO_get_data(a: PBIO): Pointer; cdecl external LIB_CRYPTO name PREFIX + 'BIO_get_data';
function BIO_get_mem_data(a : PBIO; var pp : PAnsiChar) : Integer; cdecl external LIB_CRYPTO name PREFIX + 'BIO_get_mem_data';
function EVP_PKEY_get1_RSA(pkey: PEVP_PKEY): PRSA; cdecl external LIB_CRYPTO name PREFIX + 'EVP_PKEY_get1_RSA';
function PEM_read_bio_PUBKEY(ABIO: PBIO; x: PPEVP_PKEY; cb: TPEM_Password_Callback; u: Pointer ): PEVP_PKEY; cdecl external LIB_CRYPTO name PREFIX + 'PEM_read_bio_PUBKEY';
function PEM_write_bio_RSA_PUBKEY(bp: PBIO; x: PRSA): integer; cdecl external LIB_CRYPTO name PREFIX + 'PEM_write_bio_RSA_PUBKEY';
function PEM_write_bio_RSAPrivateKEY(bp: PBIO; x: PRSA): integer; cdecl external LIB_CRYPTO name PREFIX + 'PEM_write_bio_RSAPrivateKey';
function RSA_set0_key(RSA: PRSA; n: PBIGNUM; e: PBIGNUM; d: PBIGNUM): Integer; cdecl external LIB_CRYPTO name PREFIX + 'RSA_set0_key';
function RSA_set0_factors(RSA: PRSA; p: PBIGNUM; q: PBIGNUM): Integer; cdecl external LIB_CRYPTO name PREFIX + 'RSA_set0_factors';
function RSA_set0_crt_params(RSA: PRSA; dmp1: PBIGNUM; dmq1 :PBIGNUM; iqmp: PBIGNUM): Integer; cdecl external LIB_CRYPTO name PREFIX + 'RSA_set0_crt_params';
procedure RSA_get0_key(RSA: PRSA; var n: PBIGNUM; var e: PBIGNUM; var d: PBIGNUM); cdecl external LIB_CRYPTO name PREFIX + 'RSA_get0_key';

// Actual functions of interest

function CreatePublicJWKFromPEM(PEMString: AnsiString): string;
var
  rr : PRSA;
  ff : PBIO;
  modul : PBIGNUM;
  expon : PBIGNUM;
  d : PBIGNUM;
  nnBin : TArray<Byte>;
  nnLen : Integer;
  eeBin : TArray<Byte>;
  eeLen : Integer;
  base64URL : TBase64URLURLEncoding;
  JSONkey : TJSONObject;
  evp_key : PEVP_PKEY;
begin
  base64URL := nil;
  rr := nil;
  ff := nil;
  JSONkey := nil;
  try
    base64URL := TBase64URLURLEncoding.Create;
    JSONkey := TJSONObject.Create;
    ff := BIO_new_mem_buf(PAnsiChar(PEMString), Length(PEMString));
    evp_key := PEM_read_bio_PUBKEY(ff, nil, nil, nil);
    rr := EVP_PKEY_get1_RSA(evp_key);
    RSA_get0_key(rr, modul, expon, d);

    SetLength(nnBin, 1024);
    SetLength(eeBin, 1024);
    nnLen := BN_bn2bin(modul, nnBin);
    eeLen := BN_bn2bin(expon, eeBin);
    JSONkey.AddPair('e', base64URL.EncodeBytesToString(eeBin, eeLen));
    JSONkey.AddPair('n', base64URL.EncodeBytesToString(nnBin, nnLen));

    Result := JSONkey.ToJSON;
  finally
    BIO_free(ff);
    EVP_PKEY_free(evp_key);
    RSA_free(rr);
    FreeAndNil(base64URL);
    FreeAndNil(JSONkey);
  end;
end;

function CreatePublicPEMFromJWK(json: TJSONObject): string;
var
  rr : PRSA;
  ff : PBIO;
  modul : PBIGNUM;
  expon : PBIGNUM;
  nnBin : TArray<Byte>;
  nnLen : Integer;
  eeBin : TArray<Byte>;
  eeLen : Integer;
  base64URL : TBase64URLURLEncoding;
  p : PAnsiChar;
  res : Integer;
begin
  base64URL := nil;
  rr := nil;
  ff := nil;
  try
    base64URL := TBase64URLURLEncoding.Create;

    nnBin := base64URL.DecodeStringToBytes(json.Values['n'].Value);
    eeBin := base64URL.DecodeStringToBytes(json.Values['e'].Value);

    nnLen := Length(nnBin);
    eeLen := Length(eeBin);
    modul := BN_bin2bn(nnBin, nnLen, nil);
    expon := BN_bin2bn(eeBin, eeLen, nil);
    rr := RSA_new();
    RSA_set0_key(rr, modul, expon, nil);
    ff := BIO_new(BIO_s_mem());
    PEM_write_bio_RSA_PUBKEY(ff, rr);
    res := BIO_ctrl(ff, BIO_CTRL_INFO, 0, @p);
    Result := Copy(p, 1, res);
  finally
    BIO_free(ff);
    RSA_free(rr);
    FreeAndNil(base64URL);
  end;
end;
Morrismx commented 4 years ago

Hi,

Very good example, and this is really useful. is it possible to create public key from your library?

Thank you. Mauricio Herrera