HeathRow
HeathRow

Reputation: 119

How to convert JSON Web Key to PEM format in Delphi?

Earlier I found out that before using the signature in JWT Library, I need to convert the JSON Web Key (JWK) to PEM format.

Original private key in JWK format:

{
  "kty": "EC",
  "d": "Rwyv99W3GnfjYbI0X-b5Umhvh88oRCKQkPxiwCPVGgg",
  "crv": "P-256",
  "x": "sDbcYT8HzBk1tUl849ZHrhpIn8ZV7HfD1DwYdsP1ip0",
  "y": "EWodfKWQ6oE0ppyi7tRO_61BgAQsZyDjDGj9kLZiUts"
}

Need to get PEM format, like here:

-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIEcMr/fVtxp342GyNF/m+VJob4fPKEQikJD8YsAj1RoIoAoGCCqGSM49
AwEHoUQDQgAEsDbcYT8HzBk1tUl849ZHrhpIn8ZV7HfD1DwYdsP1ip0Rah18pZDq
gTSmnKLu1E7/rUGABCxnIOMMaP2QtmJS2w==
-----END EC PRIVATE KEY-----

There is an online converter that does what I need. Is it possible to do the same conversion in Delphi?

Upvotes: 2

Views: 3560

Answers (2)

HeathRow
HeathRow

Reputation: 119

The solution was found. Read the details here.

Simplified example:

uses
  JSON,
  EncdDecd;

function Base64urlToBase64(Base64urlStr: String): String;
begin
  Result := StringReplace(Base64urlStr,'_','/', [rfReplaceAll]);
  Result := StringReplace(Result,'-','+', [rfReplaceAll]);
end;

function JwkToPem(JWK: TJSONObject): String;
var
  BinKey: TBytes;
begin
  BinKey :=

  [$30] + // ASN.1
  [$77] + // Length of all following bytes (119 bytes)

  [$02] + // Type (integer)
  [$01] + // Length of integer (1 byte)
  [$01] + // Value of integer (1)

  [$04] + // Type (octet string)
  [$20] + // Length of string (32 bytes)
  DecodeBase64(Base64urlToBase64(JWK.Get('d').JsonValue.Value)) +  // Private Key

  [$A0] + // Tag 0
  [$0A] + // Length of tag (10 bytes)
  [$06] + // Type (Object ID)
  [$08] + // Length of the Object ID (8 bytes)
  [$2A, $86, $48, $CE, $3D, $03, $01, $07] + // - The object ID of the curve prime256v1

  [$A1] + // Tag 1
  [$44] + // Length of tag (68 bytes)
  [$03] + // Type – Bit string
  [$42] + // Length of the bit string (66 bytes)
  [$00] + // ???
  [$04] + // Uncompressed Public Key
  DecodeBase64(Base64urlToBase64(JWK.Get('x').JsonValue.Value))+ // Public Key X coord
  DecodeBase64(Base64urlToBase64(JWK.Get('y').JsonValue.Value)); // Public Key Y coord

  Result :=
  '-----BEGIN EC PRIVATE KEY-----'+#13#10+
  EncodeBase64(Pointer(BinKey), Length(BinKey))+#13#10+
  '-----END EC PRIVATE KEY-----';
end;

Upvotes: 0

Arnaud Bouchez
Arnaud Bouchez

Reputation: 43043

You would have everything you need in low-level OpenSSL.

Its API is a little cryptic but you have the EC_POINT*() functions for doing it.

Check what we did in mormot.crypt.openssl to work with low-level ECC private keys and integrate them with OpenSSL:

  • ecdsa_sign_osl which takes a raw private key and convert it into OpenSSL PEC_KEY;
  • OpenSslSaveKeys which saves this key as PEM.

You need to only export the "d": "Rwyv99W3GnfjYbI0X-b5Umhvh88oRCKQkPxiwCPVGgg" parameter. It seems to be the same layout than TEccPrivateKey as used as input parameter in ecdsa_sign_osl().

You may find also some pure pascal code computing ECC prime256v1 in mormot.crypt.ecc256r1.pas.

Upvotes: 3

Related Questions