Open geoffsmith82 opened 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;
Hi,
Very good example, and this is really useful. is it possible to create public key from your library?
Thank you. Mauricio Herrera
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.