fpc/packages/fcl-hash/src/fprsa.pas
2024-03-16 20:06:38 +01:00

1358 lines
41 KiB
ObjectPascal

{
This file is part of the Free Component Library.
Copyright (c) 2023 by the Free Pascal team.
RSA routines.
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
}
{$IFNDEF FPC_DOTTEDUNITS}
unit fprsa;
{$ENDIF FPC_DOTTEDUNITS}
{$mode ObjFPC}
{$H+}
{$ModeSwitch advancedrecords}
interface
{off $DEFINE CRYPTO_DEBUG}
{$IFDEF FPC_DOTTEDUNITS}
uses
System.SysUtils, System.Classes, System.Hash.Sha1, System.Hash.Sha512, System.Hash.Sha256, System.Hash.Tlsbigint, System.Hash.Utils, System.Hash.Asn, Fcl.BaseNEnc;
{$ELSE FPC_DOTTEDUNITS}
uses
sysutils, Classes, sha1, fpsha512, fpsha256, fpTLSBigInt, fphashutils, fpasn, basenenc;
{$ENDIF FPC_DOTTEDUNITS}
const
RSAPublicKeyOID = '1.2.840.113549.1.1.1';
RSADigestInfoSHA256 = 1;
RSADigestInfoSHA384 = 2;
RSADigestInfoSHA512 = 3;
RSADigestInfoSHA224 = 4;
RSADigestInfoSHA512_224 = 5;
RSADigestInfoSHA512_256 = 6;
RSADigestInfoSHA3_224 = 7;
RSADigestInfoSHA3_256 = 8;
RSADigestInfoSHA3_384 = 9;
RSADigestInfoSHA3_512 = 10;
type
TRSA = record
M: PBigInt; // Modulus
E: PBigInt; // Public exponent
D: PBigInt; // Private exponent
P: PBigInt; // p in m = pq
Q: PBigInt; // q in m = pq
DP: PBigInt; // d mod (p-1)
DQ: PBigInt; // d mod (q-1)
QInv: PBigInt; // q^-1 mod p
ModulusLen: Integer; // in bytes
ModulusBits: Integer; // in bits
Context: TBigIntContext;
end;
{ TX509RSAPrivateKey }
TX509RSAPrivateKey = record
Version: integer;
Modulus, // m or n
PublicExponent, // e
PrivateExponent, // d
Prime1, // p
Prime2, // q
Exponent1, // dp
Exponent2, // dq
Coefficient: TBytes; // qi
procedure InitWithHexStrings(const n, e, d, p, q, dp, dq, qi: ansistring);
procedure InitWithBase64UrlEncoded(const n, e, d, p, q, dp, dq, qi: ansistring);
procedure WriteASN(ms: TMemoryStream);
function AsDER: TBytes;
end;
{ TX509RSAPublicKey }
TX509RSAPublicKey = record
Modulus: TBytes; // m or n
Exponent: TBytes; // e
procedure InitWithHexStrings(const n, e: AnsiString);
procedure InitWithBase64UrlEncoded(const n, e: ansistring);
procedure WriteASN(ms: TMemoryStream);
function AsDER: TBytes;
end;
procedure RSACreate(out RSA: TRSA);
procedure RSAFree(var RSA: TRSA);
procedure RsaPublicKeyToHexa(const Modulus, Exponent: AnsiString; var PublicKeyHexa: AnsiString);
procedure RsaPublicKeyFromHexa(const PublicKeyHexa: AnsiString; out Modulus, Exponent: AnsiString);
procedure RsaInitFromPublicKey(var RSA: TRSA; const Modulus, Exponent: AnsiString); overload;
procedure RSAInitFromPublicKey(var RSA: TRSA; const RSAPublicKey: TX509RSAPublicKey); overload;
procedure RSAInitFromPublicKeyDER(var RSA: TRSA; const PublicKeyDER: TBytes);
procedure X509RsaPublicKeyInitFromDER(out RSA: TX509RSAPublicKey; const PublicKeyDER: TBytes);
procedure RSAInitFromX509PrivateKey(var RSA: TRSA; const RSAPrivateKey: TX509RSAPrivateKey);
procedure RSAInitFromPrivateKeyDER(var RSA: TRSA; const PrivateKeyDER: TBytes);
procedure X509RsaPrivateKeyInitFromDER(out RSA: TX509RSAPrivateKey; const PrivateKeyDER: TBytes);
{ Perform PKCS1.5 Encryption or Signing
RSA: The RSA context containing Private and/or Public keys
Input: The data to be encrypted
Len: The size of the input data in bytes (Must be <= Modulus length - 11 to
make the padding at least 8 bytes as recommended by RFC2313)
Output: The buffer for the encrypted result (Must always be Modulus length)
Sign: If true then sign instead of encrypting
Result: The number of bytes encrypted or on error -1 or exception }
function RSAEncryptSign(var RSA: TRSA; const Input: PByte; Len: Integer; Output: PByte; Sign: Boolean): Integer;
{ Perform PKCS1.5 Decryption or Verification
RSA: The RSA context containing Private and/or Public keys
Input: The data to be decrypted (Must always be Modulus length)
Output: The buffer for the decrypted result
Len: The size of the output buffer in bytes
Verify: If true then verify instead of decrypting
Result: The number of bytes decrypted or on error -1 or exception }
function RSADecryptVerify(var RSA: TRSA; const Input: PByte; Output: PByte; Len: Integer; Verify: Boolean): Integer;
function RS256VerifyFromPublicKeyHexa(const PublicKeyHexa, SignatureBaseHash, Signature: String): Boolean;
function TestRS256Verify: Boolean;
function EncodeDigestInfoSHA(SHAType, len: byte): TBytes;
//------------------------------------------------------------------------------
// RSA-PSS
const
RSA_PSS_SaltLen_HashLen = -1;
RSA_PSS_SaltLen_Auto = -2; // only for verify
RSA_PSS_SaltLen_Max = -3;
type
TRSAHashFunction = procedure(Input: PByte; InLen: Integer; Output: PByte);
{ TRSAHashFuncInfo }
TRSAHashFuncInfo = record
Func: TRSAHashFunction;
DigestLen: Word;
procedure UseSHA1;
procedure UseSHA256;
procedure UseSHA384;
procedure UseSHA512;
end;
PRSAHashFuncInfo = ^TRSAHashFuncInfo;
{ Perform PSASSA-PSS signing using MGF1 and a hash function
RSA: The RSA context containing the private key
Input: The data to be signed
Len: The size of the input data in bytes
Output: The buffer for the signature result (Must always be RSA.ModulusLen)
SaltLen: length in bytes of the random number Salt, can be RSA_PSS_SaltLen_HashLen or RSA_PSS_SaltLen_Max
Result: The number of bytes of the signature or on error -1 or exception }
function RSASSA_PS256_Sign(var RSA: TRSA; Input: PByte; Len: Integer;
Output: PByte; SaltLen: integer = RSA_PSS_SaltLen_HashLen): Integer;
function RSASSA_PSS_Sign(var RSA: TRSA; Input: PByte; Len: Integer;
HashFunc: PRSAHashFuncInfo; Output: PByte; SaltLen: integer = RSA_PSS_SaltLen_HashLen): Integer;
{ Perform PSASSA-PSS verification using MGF1 and a hash function
RSA: The RSA context containing the public key
Input: The data to be verified
Len: The size of the input data in bytes
Signature: The buffer for the encrypted result (Must always be RSA.ModulusLen)
SaltLen: length in bytes of the random number Salt,
can be RSA_PSS_SaltLen_HashLen, RSA_PSS_SaltLen_Auto or RSA_PSS_SaltLen_Max
Result: 0 on success or an error number }
function RSASSA_PS256_Verify(var RSA: TRSA; Input: PByte; Len: Integer;
Signature: PByte; SaltLen: integer = RSA_PSS_SaltLen_Auto): int64;
function RSASSA_PSS_Verify(var RSA: TRSA; Input: PByte; Len: Integer;
HashFunc: PRSAHashFuncInfo; Signature: PByte; SaltLen: integer = RSA_PSS_SaltLen_Auto): int64;
{ Perform EMSA_PSS_Encode
Input: The data to be verified
Len: The size of the input data in bytes
Output: The buffer for the encoded hash result
(length = (RSA.ModulusBits-1+7) div 8 -> can be one less than RSA.ModulusLen)
ModBits: RSA.ModulusBits-1
SaltLen: length in bytes of the random number Salt, can be RSA_PSS_SaltLen_HashLen or RSA_PSS_SaltLen_Max
Result: 0 on success or an error number }
function EMSA_PSS_Encode(Input: PByte; InLen: Integer; HashFunc: PRSAHashFuncInfo;
Output: PByte; ModBits: DWord; SaltLen: integer = RSA_PSS_SaltLen_HashLen): int64;
function EMSA_PSS_Verify(Msg: PByte; MsgLen: DWord;
EncodedMsg: PByte; EncodedBits: DWord; HashFunc: PRSAHashFuncInfo;
SaltLen: integer = RSA_PSS_SaltLen_HashLen): int64;
// integer to octetstring
function I2OSP(c: DWord; Len: integer): string; overload;
procedure I2OSP(c: DWord; Dest: PByte; Len: integer); overload;
// MGF1 (Mask Generating Function 1) of PKCS1 (Public Key Cryptography Standard #1)
function MGF1(const InputStr: string; HashFunc: PRSAHashFuncInfo; Len: integer): string; overload;
procedure MGF1(Input: PByte; InLen: Integer; HashFunc: PRSAHashFuncInfo; Output: PByte; OutLen: integer); overload;
function MGF1SHA1(const InputStr: string; Len: integer): string;
function MGF1SHA256(const InputStr: string; Len: integer): string;
implementation
const
RSA_MODULUS_BYTES_MAX = 512; // 4096 bit maximum
procedure RSACreate(out RSA: TRSA);
begin
RSA:=Default(TRSA);
BIInitialize(RSA.Context);
end;
procedure RSAFree(var RSA: TRSA);
begin
if RSA.M = nil then
Exit;
BITerminate(RSA.Context);
end;
procedure RsaPublicKeyToHexa(const Modulus, Exponent: AnsiString;
var PublicKeyHexa: AnsiString);
begin
PublicKeyHexa:=PublicKeyHexa+BytesToHexStr(Exponent)+BytesToHexStr(Modulus);
end;
procedure RsaPublicKeyFromHexa(const PublicKeyHexa: AnsiString; out Modulus, Exponent: AnsiString);
var
aBytes: TBytes;
begin
HexStrToBytes(PublicKeyHexa,aBytes);
if length(aBytes)<4 then
raise Exception.Create('20220426235757');
SetLength(Exponent{%H-},3);
Move(aBytes[0],Exponent[1],3);
SetLength(Modulus{%H-},length(aBytes)-3);
Move(aBytes[3],Modulus[1],length(Modulus));
end;
procedure RsaInitFromPublicKey(var RSA: TRSA; const Modulus, Exponent: AnsiString);
begin
RSA.ModulusLen := length(Modulus);
RSA.M := BIImport(RSA.Context, Modulus);
RSA.ModulusBits := BIBitCount(RSA.M);
BISetMod(RSA.Context, RSA.M, BIGINT_M_OFFSET);
RSA.E := BIImport(RSA.Context, Exponent);
BIPermanent(RSA.E);
end;
procedure RSAInitFromPublicKey(var RSA: TRSA;
const RSAPublicKey: TX509RSAPublicKey);
begin
if RSAPublicKey.Modulus = nil then
Exit;
if RSAPublicKey.Exponent = nil then
Exit;
RSA.ModulusLen := length(RSAPublicKey.Modulus);
RSA.M := BIImport(RSA.Context, RSAPublicKey.Modulus);
RSA.ModulusBits := BIBitCount(RSA.M);
BISetMod(RSA.Context, RSA.M, BIGINT_M_OFFSET);
RSA.E := BIImport(RSA.Context, RSAPublicKey.Exponent);
BIPermanent(RSA.E);
end;
procedure RSAInitFromPublicKeyDER(var RSA: TRSA; const PublicKeyDER: TBytes);
var
X508PublicKey: TX509RSAPublicKey;
begin
X509RsaPublicKeyInitFromDER(X508PublicKey,PublicKeyDER);
RSAInitFromPublicKey(RSA,X508PublicKey);
end;
procedure X509RsaPublicKeyInitFromDER(out RSA: TX509RSAPublicKey;
const PublicKeyDER: TBytes);
var
ASNType, ASNSize, i: integer;
List: TStringList;
begin
RSA:=Default(TX509RSAPublicKey);
List:=TStringList.Create;
try
ASNParse(PublicKeyDER,List);
{$IFDEF TLS_DEBUG}
ASNDebugList('X509RsaPublicKeyInitFromDER',List);
{$ENDIF}
if List.Count<6 then
raise Exception.Create('20220428180055');
// check sequence
ASNParse_GetItem(List,0,ASNType,ASNSize);
if ASNType<>ASN1_SEQ then
raise Exception.Create('20220428180058');
// check sequence
ASNParse_GetItem(List,1,ASNType,ASNSize);
if ASNType<>ASN1_SEQ then
raise Exception.Create('20220428183025');
// check algorithm OID
ASNParse_GetItem(List,2,ASNType,ASNSize);
if ASNType<>ASN1_OBJID then
raise Exception.Create('20220428180512');
if List[2]<>RSAPublicKeyOID then
raise Exception.Create('20220428181542');
// check optional null
i:=3;
ASNParse_GetItem(List,i,ASNType,ASNSize);
if ASNType=ASN1_NULL then
inc(i);
// check algorithm params
ASNParse_GetItem(List,i,ASNType,ASNSize);
if ASNType<>ASN1_BITSTR then
raise Exception.Create('20220428181913');
inc(i);
if i+2>List.Count then
raise Exception.Create('20220428180055');
// check sequence
ASNParse_GetItem(List,i,ASNType,ASNSize);
if ASNType<>ASN1_SEQ then
raise Exception.Create('20220428181933');
// public key
RSA.Modulus:=ASNParse_GetIntBytes(List,i+1,20220428182235);
RSA.Exponent:=ASNParse_GetIntBytes(List,i+2,20220428182241);
{$IFDEF TLS_DEBUG}
writeln('X509RsaPublicKeyInitFromDER: ');
writeln(' Modulus=$',BytesToHexStr(RSA.Modulus));
writeln(' Exponent=$',BytesToHexStr(RSA.Exponent));
{$ENDIF}
finally
List.Free;
end;
end;
procedure RSAInitFromX509PrivateKey(var RSA: TRSA;
const RSAPrivateKey: TX509RSAPrivateKey);
begin
if RSAPrivateKey.PrivateExponent = nil then
Exit;
if RSAPrivateKey.Prime1 = nil then
Exit;
if RSAPrivateKey.Prime2 = nil then
Exit;
if RSAPrivateKey.Exponent1 = nil then
Exit;
if RSAPrivateKey.Exponent2 = nil then
Exit;
if RSAPrivateKey.Coefficient = nil then
Exit;
if RSAPrivateKey.Modulus = nil then
Exit;
if RSAPrivateKey.PublicExponent = nil then
Exit;
RSA.ModulusLen := length(RSAPrivateKey.Modulus);
RSA.M := BIImport(RSA.Context, RSAPrivateKey.Modulus);
RSA.ModulusBits := BIBitCount(RSA.M);
BISetMod(RSA.Context, RSA.M, BIGINT_M_OFFSET);
RSA.E := BIImport(RSA.Context, RSAPrivateKey.PublicExponent);
BIPermanent(RSA.E);
RSA.D := BIImport(RSA.Context, RSAPrivateKey.PrivateExponent);
BIPermanent(RSA.D);
RSA.P := BIImport(RSA.Context, RSAPrivateKey.Prime1);
RSA.Q := BIImport(RSA.Context, RSAPrivateKey.Prime2);
RSA.DP := BIImport(RSA.Context, RSAPrivateKey.Exponent1);
RSA.DQ := BIImport(RSA.Context, RSAPrivateKey.Exponent2);
RSA.QInv := BIImport(RSA.Context, RSAPrivateKey.Coefficient);
BIPermanent(RSA.DP);
BIPermanent(RSA.DQ);
BIPermanent(RSA.QInv);
BISetMod(RSA.Context, RSA.P, BIGINT_P_OFFSET);
BISetMod(RSA.Context, RSA.Q, BIGINT_Q_OFFSET);
end;
procedure RSAInitFromPrivateKeyDER(var RSA: TRSA; const PrivateKeyDER: TBytes);
var
X509RSA: TX509RSAPrivateKey;
begin
X509RsaPrivateKeyInitFromDER(X509RSA,PrivateKeyDER);
RSAInitFromX509PrivateKey(RSA,X509RSA);
end;
function ExtractRSAFromPKCS8(List : TStrings) : TBytes;
Const
SInvalid = 'Invalid PKCS#8 ';
var
ASNType, ASNSize: integer;
begin
Result:=[];
ASNParse_GetItem(List,0,ASNType,ASNSize);
if ASNType<>ASN1_SEQ then
raise Exception.Create(SInvalid+'Sequence 1');
ASNParse_GetItem(List,1,ASNType,ASNSize);
if ASNType<>ASN1_INT then
raise Exception.Create(SInvalid+'Int 1');
if StrToIntDef(List[1],-1)<>0 then
raise Exception.Create(SInvalid+'Int 1.a');
ASNParse_GetItem(List,2,ASNType,ASNSize);
if ASNType<>ASN1_SEQ then
raise Exception.Create(SInvalid+'Sequence 2');
ASNParse_GetItem(List,3,ASNType,ASNSize);
if ASNType<>ASN1_OBJID then
raise Exception.Create(SInvalid+'ObjID');
ASNParse_GetItem(List,4,ASNType,ASNSize);
if ASNType<>ASN1_NULL then
raise Exception.Create(SInvalid+'Attribute');
ASNParse_GetItem(List,5,ASNType,ASNSize);
if ASNType<>ASN1_OCTSTR then
raise Exception.Create(SInvalid+'RSA key');
Result:=HexStrToBytes(List[5]);
end;
procedure X509RsaPrivateKeyInitFromDER(out RSA: TX509RSAPrivateKey; const PrivateKeyDER: TBytes);
var
List: TStringList;
ASNType, ASNSize: integer;
B : TBytes;
begin
RSA:=Default(TX509RSAPrivateKey);
List:=TStringList.Create;
try
ASNParse(PrivateKeyDER,List);
if Not List.Count in [6,10] then
raise Exception.Create('20220428161533');
if List.Count = 6 then
begin
B:=ExtractRSAFromPKCS8(List);
X509RsaPrivateKeyInitFromDER(RSA,B);
end
else
begin
// check sequence
ASNParse_GetItem(List,0,ASNType,ASNSize);
if ASNType<>ASN1_SEQ then
raise Exception.Create('20220428161631');
// version
ASNParse_GetItem(List,1,ASNType,ASNSize);
if ASNType<>ASN1_INT then
raise Exception.Create('20220428161716');
RSA.Version:=StrToIntDef(List[1],0);
RSA.Modulus:=ASNParse_GetIntBytes(List,2,20220428173827);
RSA.PublicExponent:=ASNParse_GetIntBytes(List,3,20220428173840);
RSA.PrivateExponent:=ASNParse_GetIntBytes(List,4,20220428173852);
RSA.Prime1:=ASNParse_GetIntBytes(List,5,20220428173906);
RSA.Prime2:=ASNParse_GetIntBytes(List,6,20220428173915);
RSA.Exponent1:=ASNParse_GetIntBytes(List,7,20220428173923);
RSA.Exponent2:=ASNParse_GetIntBytes(List,8,20220428173930);
RSA.Coefficient:=ASNParse_GetIntBytes(List,9,20220428173939);
end;
{$IFDEF TLS_DEBUG}
with RSA do begin
writeln('RsaInitFromPrivateKey ');
writeln(' Modulus=',BytesToHexStr(Modulus));
writeln(' PublicExponent=',BytesToHexStr(PublicExponent));
writeln(' PrivateExponent=',BytesToHexStr(PrivateExponent));
writeln(' Prime1=',BytesToHexStr(Prime1));
writeln(' Prime2=',BytesToHexStr(Prime2));
writeln(' Exponent1=',BytesToHexStr(Exponent1));
writeln(' Exponent2=',BytesToHexStr(Exponent2));
writeln(' Coefficient=',BytesToHexStr(Coefficient));
end;
{$ENDIF}
finally
List.Free;
end;
end;
function RSAEncryptSign(var RSA: TRSA; const Input: PByte; Len: Integer;
Output: PByte; Sign: Boolean): Integer;
var
Size: Integer;
Padding: Integer;
Imported: PByte;
Decrypted: PBigInt;
Encrypted: PBigInt;
Block: array[0..RSA_MODULUS_BYTES_MAX-1] of Byte;
{$IFDEF CRYPTO_DEBUG}
i: integer;
{$ENDIF}
begin
Result := -1;
if Input = nil then
Exit;
if Output = nil then
Exit;
Size := RSA.ModulusLen;
Padding := Size-Len-3;
if Len > Size-8-3 then
Exit;
{$IFDEF CRYPTO_DEBUG}
writeln('RSAEncryptSign - Len = ' + IntToStr(Len) + ' Size = ' + IntToStr(Size) + ' Padding = ' + IntToStr(Padding)); //To Do
{$ENDIF}
if Size > RSA_MODULUS_BYTES_MAX then
Imported := GetMem(Size)
else
Imported := @Block[0];
try
// Leading zero to ensure encryption block is less than modulus (when converted to an integer)
Imported[0]:=0;
// Check Sign
if Sign then
begin
// Block Type 1
Imported[1]:=1;
// Pad with 0xff bytes
FillByte(Imported[2],Padding,$FF);
end else
begin
// Block Type 2
Imported[1]:=2;
// Pad with random non-zero bytes
if not CryptoGetRandomBytes(@Imported[2], Padding, false) then
Exit;
{$IFDEF CRYPTO_DEBUG}
for i:=0 to Padding-1 do
if Imported[2+i]=0 then
raise Exception.Create('20220429000653');
{$ENDIF}
end;
// Trailing zero after padding bytes
Imported[2 + Padding]:=0;
// Copy Input to Block
System.Move(Input^,Imported[3 + Padding],Len);
{$IFDEF CRYPTO_DEBUG}
writeln('RSAEncryptSign - Imported Size = ' + IntToStr(Size) + ' Len = ',Len);
{$ENDIF}
// Encrypt the Block
// Note: the RSA.M was set in RSAInitFromPublicKey
Decrypted:=BIImport(RSA.Context,Imported,Size);
if Sign then
begin
// Sign with Private Key
Encrypted:=BICRT(RSA.Context,Decrypted,RSA.DP,RSA.DQ,RSA.P,RSA.Q,RSA.QInv); // this releases Decrypted
end else
begin
// Encrypt with Public Key
RSA.Context.ModOffset:=BIGINT_M_OFFSET;
Encrypted:=BIModPower(RSA.Context,Decrypted,RSA.E); // this releases Decrypted
end;
BIExport(RSA.Context,Encrypted,Output,Size); // this releases Encrypted
{$IFDEF CRYPTO_DEBUG}
writeln('RSAEncryptSign - Output Size = ' + IntToStr(Size) + ' Len = ',Len);
{$ENDIF}
// Return Result
Result:=Size;
finally
if Size > RSA_MODULUS_BYTES_MAX then
FreeMem(Imported);
end;
end;
function RSADecryptVerify(var RSA: TRSA; const Input: PByte; Output: PByte;
Len: Integer; Verify: Boolean): Integer;
var
Size: Integer;
Count: Integer;
Padding: Integer;
Exported: PByte;
Encrypted: PBigInt;
Decrypted: PBigInt;
Block: array[0..RSA_MODULUS_BYTES_MAX-1] of Byte;
begin
Result := -1;
if Input = nil then
Exit;
if Output = nil then
Exit;
Size := RSA.ModulusLen;
Count := 0;
Padding := 0;
{$IFDEF CRYPTO_DEBUG}
writeln('RSA', 'RSADecryptVerify Len: ', Len, ' Size: ', Size, ' Padding: ', Padding);
{$ENDIF}
Encrypted := BIImport(RSA.Context, Input, Size);
if Verify then
begin
// Verify with Public Key
RSA.Context.ModOffset := BIGINT_M_OFFSET;
Decrypted := BIModPower(RSA.Context, Encrypted, RSA.E); // this releases Encrypted
end else
begin
// Decrypt with Private Key
Decrypted := BICRT(RSA.Context,Encrypted,RSA.DP,RSA.DQ,RSA.P,RSA.Q,RSA.QInv); // this releases Encrypted
end;
Exported := @Block[0];
if Size > RSA_MODULUS_BYTES_MAX then
begin
Exported := GetMem(Size);
if Exported = nil then
begin
{$IFDEF CRYPTO_DEBUG}
writeln('RSADecryptVerify GetMem failed');
{$ENDIF}
Exit;
end;
end;
try
BIExport(RSA.Context, Decrypted, Exported, Size); // this releases Decrypted
if Exported[Count] <> 0 then
begin
{$IFDEF CRYPTO_DEBUG}
writeln('RSADecryptVerify leading zero missing');
{$ENDIF}
Exit; // Check Leading Zero
end;
Inc(Count);
if Verify then
begin
// Check Block Type 1
if Exported[Count] <> 1 then
begin
{$IFDEF CRYPTO_DEBUG}
writeln('RSADecryptVerify Verify Blockt Type<>1');
{$ENDIF}
Exit;
end;
Inc(Count);
while (Exported[Count] = $FF) and (Count < Size) do
begin
// Padded with 0xff bytes
Inc(Count);
Inc(Padding);
end;
end else
begin
// Check Block Type 2
if Exported[Count] <> 2 then
begin
{$IFDEF CRYPTO_DEBUG}
writeln('RSADecryptVerify Decrypt Blockt Type<>2');
{$ENDIF}
Exit;
end;
Inc(Count);
while (Exported[Count] <> 0) and (Count < Size) do
begin
// Padded with random non-zero bytes
Inc(Count);
Inc(Padding);
end;
end;
// Check trailing zero byte and padding size
if (Count = Size) or (Padding < 8) then
begin
{$IFDEF CRYPTO_DEBUG}
writeln('RSADecryptVerify invalid padding');
{$ENDIF}
Exit;
end;
if Exported[Count] <> 0 then
begin
{$IFDEF CRYPTO_DEBUG}
writeln('RSADecryptVerify after padding zero missing');
{$ENDIF}
Exit;
end;
Inc(Count);
Result := Size-Count;
if Len < Result then
begin
{$IFDEF CRYPTO_DEBUG}
writeln('RSADecryptVerify Output too small');
{$ENDIF}
Result := -1;
Exit;
end;
if Len > Result then
FillByte(Output[Result], Len-Result, 0);
System.Move(Exported[Count], Output^, Result);
{$IFDEF CRYPTO_DEBUG}
writeln('RSADecryptVerify - Output Size: ', Result);
//writeln('Output: ', Output);
{$ENDIF}
finally
if Size > RSA_MODULUS_BYTES_MAX then
FreeMem(Exported);
end;
end;
function RsaVerify(const Modulus, Exponent, Hash, Signature: AnsiString): Boolean;
var
ASNType, ASNSize: Int32;
Data: array[0..4095] of byte;
Digest: AnsiString;
DataP, DataEnd: PByte;
OID: AnsiString;
RSA: TRSA;
Size: Integer;
begin
Result := False;
{$IFDEF TLS_DEBUG}
writeln('RsaVerify - Modulus: ', Modulus,' Exponent: ', Exponent,
' Hash: ', Hash, ' Signature: ', Signature,
' Signature Length: ', Signature.Length,
' Modulus Length: ', Modulus.Length);
{$ENDIF}
if length(Modulus) <> length(Signature) then
Exit;
if length(Signature)>length(Data) then
Exit;
RsaCreate(RSA);
try
RsaInitFromPublicKey(RSA, Modulus, Exponent);
DataP:=PByte(@Data[0]);
DataEnd:=DataP+length(Data);
Size := RSADecryptVerify(RSA, PByte(Signature), DataP, length(Signature), True); // Decrypt the signature
if Size = -1 then
Exit;
if not ASNFetch(DataP, DataEnd, ASNType, ASNSize) then // Sequence: DigestInfo
Exit;
if ASNType <> ASN1_SEQ then
Exit;
if not ASNFetch(DataP, DataEnd, ASNType, ASNSize) then // Sequence: AlgorithmIdentifier
Exit;
if ASNType <> ASN1_SEQ then
Exit;
if not ASNFetchOID(DataP, DataEnd, OID) then // OID: Algorithm
Exit;
if not ASNFetch(DataP, DataEnd, ASNType, ASNSize) then // ASN1_NULL
Exit;
if ASNType = ASN1_NULL then
begin
if not ASNFetch(DataP, DataEnd, ASNType, ASNSize) then // OctetString: Digest
Exit;
end;
if ASNType <> ASN1_OCTSTR then
begin
{$IFDEF TLS_DEBUG}
writeln('RsaVerify - OCTETSTRING Digest not found in decoded Signature');
{$ENDIF}
Exit;
end;
if ASNSize <> length(Hash) then
begin
{$IFDEF TLS_DEBUG}
writeln('RsaVerify - Mismatch digest size: ',ASNSize,' != Hash.Length: ', length(Hash));
{$ENDIF}
Exit;
end;
if DataEnd-DataP<ASNSize then
Exit;
SetLength(Digest{%H-},ASNSize);
System.Move(DataP^,Digest[1],ASNSize);
{$IFDEF TLS_DEBUG}
writeln('RsaVerify - Compare Digest: ', Digest);
writeln('RsaVerify - with Hash: ', Hash);
{$ENDIF}
Result := Digest=Hash;
{$IFDEF TLS_DEBUG}
if Result then
writeln('RsaVerify - Success')
else
writeln('RsaVerify - Failed');
{$ENDIF}
finally
RSAFree(RSA);
end;
end;
function RS256VerifyFromPublicKeyHexa(const PublicKeyHexa, SignatureBaseHash,
Signature: String): Boolean;
var
Modulus, Exponent: AnsiString;
begin
RsaPublicKeyFromHexa(PublicKeyHexa, Modulus, Exponent);
Result := RsaVerify(Modulus, Exponent, SignatureBaseHash, Signature);
end;
function TestRS256Verify: Boolean;
const
_Modulus = 'BB32B4D0D89E9A9E8C79294C2BA8EF5C43D4933B9478FF3054C71BC8E52F1B99CD108D67C8540C075AE4F4067FC7D684B42CCD2D4E4426011AEA37BEEFF4C715'
+'07C3164C6B261909D2FF5910445B8A8981941DFEE25F9A5F8A36D8B0E91F6F802254ACAC29435552D815BE92687B94565118D0A7D5C35A47A8D83CC61D72DC04'
+'369DACCF152C2E87D7F0FD497755AEEC4AA9DB8B291E3567FE9D9520DD798D600A7873DC2875A586DF31FB130936A6C3E02D46DC252B76F6ADF4C77DF868C23B'
+'B3335E542ADF9BAEBFDC1019408D04EF6BCAEEB5853D2BD38D825FA91B6BBB06FE75E83C26372F31CFDC0E8D378EA5E87433D37F7B0ABC7206D1F3B2C56B18B5';
_Hash = 'A135E3608E956E91743421E0677C03FBE2C7CE0890FF06423B66335E3428EF9A';
_Exponent = '010001';
_Signature = '75BDCF54B21FD4F2891EEC91D1E9F6D82ADEB63BBB1DB4E03A389B525E8F5B97669FEB2E9C87EF4E785124F5499918771E03E4FF83E31CE0CF4A8276809C35AA'
+'FBF9B45B7918F5D891D863CA441D5803DFD1C4190640A73ADA10DC05C2EF480C449FDD157AB4CD1ADE0B067930E07607134ED425BE5A0A1F78AFD6045BA638E7'
+'18BFB311B8377C0FACDED4CD2B1E2692E480BE260BE355F050EBABF89E24F2833F56F0A74C185225DB3B47B63612FB9BDEE1E1B8707807093E1551F24527A763'
+'1947D033ED7052C439E50B8A46E4D0C06DBC38AF1D64B49766A5CF9A82644650FFD733B61942DB0BD8D47C8EF24A02DC9FD2EF557B12DED804519F2B2B6C284D';
var
Exponent, Modulus, Hash, Signature: Ansistring;
begin
Exponent:=HexStrToString(_Exponent);
Modulus:=HexStrToString(_Modulus);
Hash:=HexStrToString(_Hash);
Signature:=HexStrToString(_Signature);
Result := RsaVerify(Modulus, Exponent, Hash, Signature);
end;
function EncodeDigestInfoSHA(SHAType, len: byte): TBytes;
begin
Result:= [
ASN1_SEQ, 17 + len,
ASN1_SEQ, 13,
ASN1_OBJID, 9, 2*40 + 16, $86, $48, 1, 101, 3, 4, 2, SHAType,
ASN1_NULL, 0,
ASN1_OCTSTR, len
];
end;
function RSASSA_PS256_Sign(var RSA: TRSA; Input: PByte; Len: Integer;
Output: PByte; SaltLen: integer): Integer;
var
HashFunc: TRSAHashFuncInfo;
begin
HashFunc.UseSHA256;
Result:=RSASSA_PSS_Sign(RSA,Input,Len,@HashFunc,Output,SaltLen);
end;
function RSASSA_PSS_Sign(var RSA: TRSA; Input: PByte; Len: Integer;
HashFunc: PRSAHashFuncInfo; Output: PByte; SaltLen: integer): Integer;
// RFC 3447 Signature generation operation
var
EncodedMsg: TBytes;
EncodedBI, Encrypted: PBigInt;
EncodedLen, ModBits: DWord;
r: Int64;
begin
Result:=-1;
if ((RSA.ModulusBits+7) div 8)<>RSA.ModulusLen then
raise Exception.Create('20220502000942 RSA n has leading zeroes');
ModBits:=RSA.ModulusBits-1;
EncodedLen:=(ModBits+7) div 8; // can be one less than RSA.ModulusLen
SetLength(EncodedMsg{%H-},EncodedLen);
r:=EMSA_PSS_Encode(Input,Len, HashFunc, @EncodedMsg[0], ModBits, SaltLen);
if r<>0 then
raise Exception.Create(IntToStr(r));
EncodedBI:=BIImport(RSA.Context,EncodedMsg);
// Sign with Private Key
Encrypted:=BICRT(RSA.Context,EncodedBI,RSA.DP,RSA.DQ,RSA.P,RSA.Q,RSA.QInv); // this releases EncodedBI
BIExport(RSA.Context,Encrypted,Output,RSA.ModulusLen); // this releases Encrypted
Result:=RSA.ModulusLen;
end;
function RSASSA_PS256_Verify(var RSA: TRSA; Input: PByte; Len: Integer;
Signature: PByte; SaltLen: integer): int64;
var
HashFunc: TRSAHashFuncInfo;
begin
HashFunc.UseSHA256;
Result:=RSASSA_PSS_Verify(RSA,Input,Len,@HashFunc,Signature,SaltLen);
end;
function RSASSA_PSS_Verify(var RSA: TRSA; Input: PByte; Len: Integer;
HashFunc: PRSAHashFuncInfo; Signature: PByte; SaltLen: integer): int64;
// RFC 3447 8.1.2 Signature verification operation
var
BISignature, BIEncodedMsg: PBigInt;
Size: Integer;
EncodedMsg: TBytes;
EncodedMsgP: PByte;
begin
Result:=0;
// "1. Length checking: If the length of the signature S is not k octets, error"
Size:=RSA.ModulusLen;
if ((RSA.ModulusBits+7) div 8)<>Size then
// RSA.n has leading zeroes
exit(20220502214238);
// 2. using RSAVP1 verification primitive with public key
BISignature := BIImport(RSA.Context, Signature, Size);
RSA.Context.ModOffset := BIGINT_M_OFFSET;
BIEncodedMsg := BIModPower(RSA.Context, BISignature, RSA.E); // this releases BISignature
// "c. Convert the message representative m to an encoded message EM
// of length emLen = \ceil ((modBits - 1)/8) octets, where modBits
// is the length in bits of the RSA modulus n
// Note that emLen will be one less than k if modBits - 1 is
// divisible by 8 and equal to k otherwise."
SetLength(EncodedMsg{%H-},Size);
BIExport(RSA.Context, BIEncodedMsg, @EncodedMsg[0], Size); // this releases BIEncodedMsg
EncodedMsgP:=@EncodedMsg[0];
if ((RSA.ModulusBits-1) and 7)=0 then
begin
if (EncodedMsg[0]<>0) then
exit(20220502213942);
inc(EncodedMsgP);
end;
// "3. EMSA-PSS verification
// Result = EMSA-PSS-VERIFY (M, EM, modBits - 1)."
Result:=EMSA_PSS_Verify(Input,Len,EncodedMsgP,RSA.ModulusBits-1,HashFunc,SaltLen);
end;
function EMSA_PSS_Encode(Input: PByte; InLen: Integer;
HashFunc: PRSAHashFuncInfo; Output: PByte; ModBits: DWord; SaltLen: integer
): int64;
// RFC 3447 9.1.1 Encoding operation
var
ZeroesHashSalt, H, DB, DBMask, MaskedDB: TBytes;
MsgHashP, SaltP: PByte;
Padding, HashLen, i, EncodedLen, DBLen: DWord;
begin
Result:=0;
HashLen:=HashFunc^.DigestLen;
EncodedLen:=(ModBits+7) div 8;
if SaltLen = RSA_PSS_SaltLen_HashLen then
SaltLen:=HashLen
else if SaltLen = RSA_PSS_SaltLen_Max then
SaltLen:=EncodedLen-HashLen-2
else if SaltLen < 0 then
exit(20220501233610);
// "2. Let mHash = Hash(M), an octet string of length hLen."
// Note: directly into ZeroesHashSalt
// "3. If emLen < hLen + sLen + 2, error"
if EncodedLen < HashLen + DWord(SaltLen) + 2 then
exit(20220501221837);
// "4. Generate a random octet string salt of length sLen; if sLen = 0,
// then salt is the empty string."
// Note: directly into ZeroesHashSalt
// "5. Let M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt;
// M' is an octet string of length 8 + hLen + sLen with eight
// initial zero octets."
SetLength(ZeroesHashSalt{%H-},8+HashLen+DWord(SaltLen));
FillByte(ZeroesHashSalt[0],8,0);
MsgHashP:=@ZeroesHashSalt[8];
HashFunc^.Func(Input,InLen,MsgHashP);
SaltP:=MsgHashP+HashLen;
if SaltLen>0 then
if not CryptoGetRandomBytes(SaltP,SaltLen) then
exit(20220501222748);
// "6. Let H = Hash(M'), an octet string of length hLen."
SetLength(H{%H-},HashLen);
HashFunc^.Func(@ZeroesHashSalt[0],length(ZeroesHashSalt),@H[0]);
// "7. Generate an octet string PS consisting of emLen - sLen - hLen - 2
// zero octets. The length of PS may be 0."
// Note: directly in DB
// "8. Let DB = PS || 0x01 || salt;
// DB is an octet string of length emLen - hLen - 1."
DBLen:=EncodedLen-HashLen-1;
SetLength(DB{%H-},DBLen); // -1 for the trailing $bc
Padding:=length(DB)-SaltLen-1; // -1 for the $01 separator
if Padding>0 then
FillByte(DB[0],Padding,0);
DB[Padding]:=$01;
System.Move(SaltP^,DB[Padding+1],SaltLen);
// "9. Let dbMask = MGF(H, emLen - hLen - 1)."
SetLength(DBMask{%H-},DBLen);
MGF1(@H[0],HashLen,HashFunc,@DBMask[0],DBLen);
// "10. Let maskedDB = DB xor dbMask."
SetLength(MaskedDB{%H-},DBLen);
for i:=0 to DBLen-1 do
MaskedDB[i]:=DB[i] xor DBMask[i];
// "11. Set the leftmost 8emLen - emBits bits of the leftmost octet in maskedDB to zero."
if (ModBits and 7)>0 then
MaskedDB[0] := MaskedDB[0] and ($ff shr (8-(ModBits and 7)));
// "12. Let EM = maskedDB || H || 0xbc."
System.Move(MaskedDB[0],Output^,DBLen);
inc(Output,DBLen);
System.Move(H[0],Output^,HashLen);
inc(Output,HashLen);
Output^:=$bc;
end;
function EMSA_PSS_Verify(Msg: PByte; MsgLen: DWord; EncodedMsg: PByte;
EncodedBits: DWord; HashFunc: PRSAHashFuncInfo; SaltLen: integer): int64;
// RFC 3447 9.1.2 Verification operation
var
HashLen, EncodedLen, DBLen, i, Padding: DWord;
MaskedDB, HashP, SaltP: PByte;
MsgHash, DBMask, Msg2, Hash2, DB: TBytes;
begin
Result:=0;
HashLen:=HashFunc^.DigestLen;
EncodedLen:=(EncodedBits+7) div 8; // to octets round up
if SaltLen = RSA_PSS_SaltLen_HashLen then
SaltLen:=HashLen
else if SaltLen = RSA_PSS_SaltLen_Auto then
else if SaltLen = RSA_PSS_SaltLen_Max then
begin
if EncodedLen < HashLen - 2 then
exit(20220502220403);
SaltLen:=EncodedLen - HashLen - 2;
end
else if SaltLen < RSA_PSS_SaltLen_Max then
exit(20220502205808);
// "2. Let mHash = MsgHash(M), an octet string of length hLen."
SetLength(MsgHash{%H-},HashLen);
HashFunc^.Func(Msg,MsgLen,@MsgHash[0]);
// "3. If emLen < hLen + sLen + 2, error."
if SaltLen = RSA_PSS_SaltLen_Auto then
begin
if EncodedLen < HashLen + 2 then
exit(20220502222313);
end else if EncodedLen < HashLen + DWord(SaltLen) + 2 then
exit(20220502205834);
// "4. If the rightmost octet of EM does not have hexadecimal value 0xbc, error."
if EncodedMsg[EncodedLen-1]<>$bc then
exit(20220502205918);
// "5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and let H be the next hLen octets."
MaskedDB:=@EncodedMsg[0];
DBLen:=EncodedLen-HashLen-1;
HashP:=MaskedDB+DBLen;
// "6. If the leftmost 8emLen - emBits bits of the leftmost octet in
// maskedDB are not all equal to zero, error"
if MaskedDB^ and ($ff shl (EncodedBits and 7))>0 then
exit(20220502210729);
// "7. Let dbMask = MGF(H, emLen - hLen - 1)."
SetLength(DBMask{%H-},DBLen);
MGF1(HashP,HashLen,HashFunc,@DBMask[0],DBLen);
// "8. Let DB = maskedDB xor dbMask."
SetLength(DB{%H-},DBLen);
for i:=0 to DBLen-1 do
DB[i]:=MaskedDB[i] xor DBMask[i];
// 9. Set the leftmost 8emLen - emBits bits of the leftmost octet in DB to zero.
if EncodedBits and 7 > 0 then
DB[0]:=DB[0] and ($ff shr (8-(EncodedBits and 7)));
// "10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero
// or if the octet at position emLen - hLen - sLen - 1 (the leftmost
// position is "position 1") does not have hexadecimal value 0x01, error.
if SaltLen = RSA_PSS_SaltLen_Auto then
begin
Padding:=0;
while DB[Padding]=0 do
begin
inc(Padding);
if Padding=DBLen then
exit(20220502222756);
end;
SaltLen:=EncodedLen-HashLen-Padding-2;
end else begin
Padding:=EncodedLen-HashLen-SaltLen-2;
for i:=0 to Padding-1 do
if DB[i]<>0 then
exit(20220502211521);
end;
if DB[Padding]<>$01 then
exit(20220502211919);
// "11. Let salt be the last sLen octets of DB."
SaltP:=@DB[0]+DBLen-SaltLen;
// "12. Let M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt ;
// M' is an octet string of length 8 + hLen + sLen with eight
// initial zero octets.
SetLength(Msg2{%H-},8 + HashLen + DWord(SaltLen));
FillByte(Msg2[0],8,0);
System.Move(MsgHash[0],Msg2[8],HashLen);
System.Move(SaltP^,Msg2[8+HashLen],SaltLen);
// "13. Let H' = Hash(M'), an octet string of length hLen."
SetLength(Hash2{%H-},HashLen);
HashFunc^.Func(@Msg2[0],length(Msg2),@Hash2[0]);
// "14. If H = H', output consistent. Otherwise, output inconsistent."
if not CompareMem(HashP,@Hash2[0],HashLen) then
exit(20220502212747);
end;
function I2OSP(c: DWord; Len: integer): string;
begin
SetLength(Result{%H-},Len);
I2OSP(c,@Result[1],Len);
end;
procedure I2OSP(c: DWord; Dest: PByte; Len: integer);
var
i: Integer;
begin
for i:=Len-1 downto 0 do
begin
Dest[i]:=c and $ff;
c:=c shr 8;
end;
if c>0 then
raise Exception.Create('20220501190124');
end;
function MGF1(const InputStr: string; HashFunc: PRSAHashFuncInfo; Len: integer): string;
begin
SetLength(Result{%H-},Len);
if Len=0 then exit;
MGF1(PByte(PAnsiChar(InputStr)){InputStr might be empty!},length(InputStr), HashFunc, @Result[1], Len);
end;
procedure MGF1(Input: PByte; InLen: Integer; HashFunc: PRSAHashFuncInfo;
Output: PByte; OutLen: integer);
var
p, CounterP, InpP: PByte;
i, r: Integer;
HashLen: Word;
InputCounted, Tmp: TBytes;
Counter: DWord;
begin
Counter:=0;
HashLen:=HashFunc^.DigestLen;
SetLength(InputCounted{%H-},InLen+4);
InpP:=@InputCounted[0];
if InLen>0 then
System.Move(Input^,InpP^,InLen);
CounterP:=InpP+InLen;
p:=Output;
for i:=1 to (OutLen div HashLen) do
begin
I2OSP(Counter,CounterP,4);
HashFunc^.Func(InpP,InLen+4,p);
inc(p,HashLen);
inc(Counter);
end;
r:=OutLen mod HashLen;
if r>0 then
begin
I2OSP(Counter,CounterP,4);
SetLength(Tmp{%H-},HashLen);
HashFunc^.Func(InpP,InLen+4,@Tmp[0]);
System.Move(Tmp[0],p^,r);
end;
end;
function MGF1SHA1(const InputStr: string; Len: integer): string;
var
HashFunc: TRSAHashFuncInfo;
begin
HashFunc.UseSHA1;
Result:=MGF1(InputStr,@HashFunc,Len);
end;
function MGF1SHA256(const InputStr: string; Len: integer): string;
var
HashFunc: TRSAHashFuncInfo;
begin
HashFunc.UseSHA256;
Result:=MGF1(InputStr,@HashFunc,Len);
end;
procedure HashFuncSHA1(Input: PByte; InLen: Integer; Output: PByte);
var
Context: TSHA1Context;
Digest: TSHA1Digest;
begin
SHA1Init(Context);
SHA1Update(Context,Input^,InLen);
SHA1Final(Context,Digest);
System.Move(Digest[0],Output^,SizeOf(Digest));
end;
procedure HashFuncSHA256(Input: PByte; InLen: Integer; Output: PByte);
var
SHA256: TSHA256;
begin
SHA256.Init;
SHA256.Update(Input,InLen);
SHA256.Final;
System.Move(SHA256.Digest[0],Output^,SHA256_DIGEST_SIZE);
end;
procedure HashFuncSHA384(Input: PByte; InLen: Integer; Output: PByte);
var
SHA384: TSHA384;
begin
SHA384.Init;
SHA384.Update(Input,InLen);
SHA384.Final;
System.Move(SHA384.Digest[0],Output^,SHA384_DIGEST_SIZE);
end;
procedure HashFuncSHA512(Input: PByte; InLen: Integer; Output: PByte);
var
SHA512: TSHA512;
begin
SHA512.Init;
SHA512.Update(Input,InLen);
SHA512.Final;
System.Move(SHA512.Digest[0],Output^,SHA512_DIGEST_SIZE);
end;
{ TRSAHashFuncInfo }
procedure TRSAHashFuncInfo.UseSHA1;
begin
Func:=@HashFuncSHA1;
DigestLen:=SizeOf(TSHA1Digest);
end;
procedure TRSAHashFuncInfo.UseSHA256;
begin
Func:=@HashFuncSHA256;
DigestLen:=SHA256_DIGEST_SIZE;
end;
procedure TRSAHashFuncInfo.UseSHA384;
begin
Func:=@HashFuncSHA384;
DigestLen:=SHA384_DIGEST_SIZE;
end;
procedure TRSAHashFuncInfo.UseSHA512;
begin
Func:=@HashFuncSHA512;
DigestLen:=SHA512_DIGEST_SIZE;
end;
{ TX509RSAPrivateKey }
procedure TX509RSAPrivateKey.InitWithHexStrings(const n, e, d, p, q, dp, dq, qi: Ansistring
);
begin
Version:=0;
Modulus:=HexStrToBytes(n);
PublicExponent:=HexStrToBytes(e);
PrivateExponent:=HexStrToBytes(d);
Prime1:=HexStrToBytes(p);
Prime2:=HexStrToBytes(q);
Exponent1:=HexStrToBytes(dp);
Exponent2:=HexStrToBytes(dq);
Coefficient:=HexStrToBytes(qi);
end;
procedure TX509RSAPrivateKey.InitWithBase64UrlEncoded(const n, e, d, p, q, dp,
dq, qi: Ansistring);
begin
Version:=0;
Modulus:=Base64URL.Decode(n,false);
PublicExponent:=Base64URL.Decode(e,false);
PrivateExponent:=Base64URL.Decode(d,false);
Prime1:=Base64URL.Decode(p,false);
Prime2:=Base64URL.Decode(q,false);
Exponent1:=Base64URL.Decode(dp,false);
Exponent2:=Base64URL.Decode(dq,false);
Coefficient:=Base64URL.Decode(qi,false);
end;
procedure TX509RSAPrivateKey.WriteASN(ms: TMemoryStream);
var
SeqBegin: Int64;
begin
SeqBegin:=ASNWriteSequenceBegin(ms);
ASNWriteInt(Version,ms);
ASNWriteBigInt(Modulus,ms);
ASNWriteBigInt(PublicExponent,ms);
ASNWriteBigInt(PrivateExponent,ms);
ASNWriteBigInt(Prime1,ms);
ASNWriteBigInt(Prime2,ms);
ASNWriteBigInt(Exponent1,ms);
ASNWriteBigInt(Exponent2,ms);
ASNWriteBigInt(Coefficient,ms);
ASNWriteSequenceEnd(SeqBegin,ms);
end;
function TX509RSAPrivateKey.AsDER: TBytes;
var
ms: TMemoryStream;
begin
Result:=[];
ms:=TMemoryStream.Create;
try
WriteASN(ms);
SetLength(Result,ms.Size);
Move(ms.Memory^,Result[0],ms.Size);
finally
ms.Free;
end;
end;
{ TX509RSAPublicKey }
procedure TX509RSAPublicKey.InitWithHexStrings(const n, e: ansistring);
begin
Modulus:=HexStrToBytes(n);
Exponent:=HexStrToBytes(e);
end;
procedure TX509RSAPublicKey.InitWithBase64UrlEncoded(const n, e: ansistring);
begin
Modulus:=Base64URL.Decode(n,false);
Exponent:=Base64URL.Decode(e,false);
end;
procedure TX509RSAPublicKey.WriteASN(ms: TMemoryStream);
var
SeqBegin, AlgoSeqBegin, RSASeqBegin, BitStrBegin: Int64;
begin
SeqBegin:=ASNWriteSequenceBegin(ms);
AlgoSeqBegin:=ASNWriteSequenceBegin(ms);
ASNWriteObjID(RSAPublicKeyOID,ms);
ASNWriteNull(ms);
ASNWriteSequenceEnd(AlgoSeqBegin,ms);
BitStrBegin:=ASNWriteBitStrBegin(ms);
RSASeqBegin:=ASNWriteSequenceBegin(ms);
ASNWriteBigInt(Modulus,ms);
ASNWriteBigInt(Exponent,ms);
ASNWriteSequenceEnd(RSASeqBegin,ms);
ASNWriteBitStrEnd(BitStrBegin,ms);
ASNWriteSequenceEnd(SeqBegin,ms);
end;
function TX509RSAPublicKey.AsDER: TBytes;
var
ms: TMemoryStream;
begin
Result:=[];
ms:=TMemoryStream.Create;
try
WriteASN(ms);
SetLength(Result,ms.Size);
Move(ms.Memory^,Result[0],ms.Size);
finally
ms.Free;
end;
end;
end.