583 lines
17 KiB
ObjectPascal
583 lines
17 KiB
ObjectPascal
|
unit frxPDFX509Certificate;
|
||
|
|
||
|
{$I frx.inc}
|
||
|
|
||
|
interface
|
||
|
|
||
|
uses
|
||
|
{$IFNDEF Linux}
|
||
|
Windows,
|
||
|
{$ENDIF}
|
||
|
SysUtils, Classes,
|
||
|
frxHelpers, frxASN1;
|
||
|
|
||
|
type
|
||
|
|
||
|
TfrxRSAPrivateKey = class
|
||
|
private
|
||
|
FVersion: Integer;
|
||
|
FAlgorithmName: TOIDName;
|
||
|
|
||
|
function GetExponent1: TfrxASN1Integer;
|
||
|
function GetExponent2: TfrxASN1Integer;
|
||
|
function GetModulus: TfrxASN1Integer;
|
||
|
function GetPrime1: TfrxASN1Integer;
|
||
|
function GetPrime2: TfrxASN1Integer;
|
||
|
function GetPrivateExponent: TfrxASN1Integer;
|
||
|
function GetPublicExponent: TfrxASN1Integer;
|
||
|
function GetCoeficient: TfrxASN1Integer;
|
||
|
protected
|
||
|
FKey: TfrxASN1Container;
|
||
|
public
|
||
|
destructor Destroy; override;
|
||
|
procedure Read(PrivateKeyInfo: TfrxASN1Container;
|
||
|
Attributes: TfrxASN1Container);
|
||
|
procedure ReadCrypted(EncryptedPrivateKeyInfo,
|
||
|
Attributes: TfrxASN1Container; Password: UTF8String);
|
||
|
|
||
|
property Version: Integer read FVersion;
|
||
|
property Modulus: TfrxASN1Integer read GetModulus; // n
|
||
|
property PublicExponent: TfrxASN1Integer read GetPublicExponent; // e
|
||
|
property PrivateExponent: TfrxASN1Integer read GetPrivateExponent; // d
|
||
|
property Prime1: TfrxASN1Integer read GetPrime1; // p
|
||
|
property Prime2: TfrxASN1Integer read GetPrime2; // q
|
||
|
property Exponent1: TfrxASN1Integer read GetExponent1; // d mod (p - 1)
|
||
|
property Exponent2: TfrxASN1Integer read GetExponent2; // d mod (q - 1)
|
||
|
property Coeficient: TfrxASN1Integer read GetCoeficient; // (inverse of q) mod p
|
||
|
end;
|
||
|
|
||
|
TfrxX509KeyValue = class
|
||
|
private
|
||
|
FKey: TOIDName;
|
||
|
FValue: TfrxASN1Base;
|
||
|
public
|
||
|
constructor Create(AKey: TOIDName; AValue: TfrxASN1Base);
|
||
|
function IsEqual(X509KeyValue: TfrxX509KeyValue): Boolean;
|
||
|
|
||
|
property Key: TOIDName read FKey;
|
||
|
property Value: TfrxASN1Base read FValue;
|
||
|
end;
|
||
|
|
||
|
TfrxX509KeyValueList = class(TOwnObjList)
|
||
|
private
|
||
|
function GetX509KeyValue(Index: Integer): TfrxX509KeyValue;
|
||
|
public
|
||
|
procedure AddX509KeyValue(Key: TOIDName; Value: TfrxASN1Base);
|
||
|
function IsEqual(KeyValueList: TfrxX509KeyValueList): Boolean;
|
||
|
|
||
|
property Items[Index: Integer]: TfrxX509KeyValue read GetX509KeyValue; default;
|
||
|
end;
|
||
|
|
||
|
TfrxX509Certificate = class
|
||
|
private
|
||
|
FPrivateKey: TfrxRSAPrivateKey;
|
||
|
FIssuer: TfrxX509KeyValueList;
|
||
|
FSubject: TfrxX509KeyValueList;
|
||
|
FPublicKey: TfrxASN1Container;
|
||
|
FASN1Certificate: TfrxASN1Container;
|
||
|
FOwner: TfrxX509Certificate;
|
||
|
// FValidTo: TDateTime;
|
||
|
// FValidFrom: TDateTime;
|
||
|
|
||
|
procedure LoadValidity(Validity: TfrxASN1Base);
|
||
|
procedure LoadSubjectPublicKey(subjectPublicKeyInfo: TfrxASN1Base);
|
||
|
procedure LoadName(ASN1Name: TfrxASN1Base; Name: TfrxX509KeyValueList);
|
||
|
public
|
||
|
constructor Create;
|
||
|
destructor Destroy; override;
|
||
|
procedure Load(Container: TfrxASN1Container);
|
||
|
function CheckPrivateKey(PrivateKey: TfrxRSAPrivateKey): Boolean;
|
||
|
function CheckOwner(Owner: TfrxX509Certificate): Boolean;
|
||
|
|
||
|
class function GetVersionShift(Version: TfrxASN1Base): Integer;
|
||
|
|
||
|
property PrivateKey: TfrxRSAPrivateKey read FPrivateKey;
|
||
|
property Owner: TfrxX509Certificate read FOwner;
|
||
|
property ASN1Certificate: TfrxASN1Container read FASN1Certificate;
|
||
|
// property ValidFrom: TDateTime read FValidFrom;
|
||
|
// property ValidTo: TDateTime read FValidTo;
|
||
|
end;
|
||
|
|
||
|
function EncriptData(AlgorithmIdentifier, Data: TfrxASN1Base;
|
||
|
Password: UTF8String): AnsiString;
|
||
|
|
||
|
implementation
|
||
|
|
||
|
uses frxCipher, frxPDFPFX, frxUtils;
|
||
|
|
||
|
const
|
||
|
InvalidEncryptedData = 'Invalid Encrypted Data.';
|
||
|
InvalidNameOfCertificate = 'Invalid name of certificate';
|
||
|
UnsupportedAlgorithm = 'Unsupported algorithm';
|
||
|
InvalidPrivateKey = 'Invalid private key';
|
||
|
|
||
|
function EncriptData(AlgorithmIdentifier, Data: TfrxASN1Base;
|
||
|
Password: UTF8String): AnsiString;
|
||
|
var
|
||
|
i: Integer;
|
||
|
EncryptedData, Salt: AnsiString;
|
||
|
Container, EncryptionAlgorithm, Parameters: TfrxASN1Container;
|
||
|
Algorithm: TfrxASN1ObjectID;
|
||
|
IVLen, KeyLen: Integer;
|
||
|
CipherClass: TfrxCipherClass;
|
||
|
IV, Key: AnsiString;
|
||
|
Iterations: Cardinal;
|
||
|
begin
|
||
|
if Data is TfrxASN1Container then
|
||
|
begin
|
||
|
// Fix for EncryptedContent
|
||
|
// [0] (2 elem)
|
||
|
// OCTET STRING (... byte) ...
|
||
|
// OCTET STRING (... byte) ...
|
||
|
//
|
||
|
// https://tools.ietf.org/html/rfc5652
|
||
|
// EncryptedContent ::= OCTET STRING
|
||
|
//
|
||
|
Container := TfrxASN1Container(Data);
|
||
|
RaiseIf(Container.IsEmpty, InvalidEncryptedData);
|
||
|
EncryptedData := '';
|
||
|
for i := 0 to Container.Count - 1 do
|
||
|
begin
|
||
|
RaiseIf(not (Container[i] is TfrxASN1Data), InvalidEncryptedData);
|
||
|
EncryptedData := EncryptedData + TfrxASN1Data(Container[i]).Data;
|
||
|
end;
|
||
|
end
|
||
|
else if Data is TfrxASN1Data then
|
||
|
EncryptedData := TfrxASN1Data(Data).Data
|
||
|
else
|
||
|
raise ESignException.Create(InvalidEncryptedData);
|
||
|
|
||
|
// AlgorithmIdentifier ::= SEQUENCE {
|
||
|
// algorithm OBJECT IDENTIFIER,
|
||
|
// parameters ANY DEFINED BY algorithm OPTIONAL }
|
||
|
RaiseIf(AlgorithmIdentifier.IsNot(ASN1_TAG_SEQUENCE), InvalidEncryptedData);
|
||
|
EncryptionAlgorithm := TfrxASN1Container(AlgorithmIdentifier);
|
||
|
|
||
|
RaiseIf((EncryptionAlgorithm.Count < 2) or
|
||
|
(EncryptionAlgorithm[0].IsNot(ASN1_TAG_OBJECT_ID)) or
|
||
|
(EncryptionAlgorithm[1].IsNot(ASN1_TAG_SEQUENCE)), InvalidEncryptedData);
|
||
|
|
||
|
Algorithm := TfrxASN1ObjectID(EncryptionAlgorithm[0]);
|
||
|
|
||
|
Parameters := EncryptionAlgorithm[1] as TfrxASN1Container;
|
||
|
RaiseIf(Parameters.IsEmpty or Parameters[0].IsNot(ASN1_TAG_OCTET_STRING),
|
||
|
InvalidEncryptedData);
|
||
|
|
||
|
Salt := TfrxASN1Data(Parameters[0]).Data;
|
||
|
|
||
|
if (Parameters.Count = 1) or Parameters[1].IsNot(ASN1_TAG_INTEGER) then
|
||
|
Iterations := 1
|
||
|
else
|
||
|
Iterations := TfrxASN1Integer(Parameters[1]).Value;
|
||
|
|
||
|
case Algorithm.OIDName of
|
||
|
OID_pbeWithSHA1And3_KeyTripleDES_CBC:
|
||
|
begin
|
||
|
IVLen := 8;
|
||
|
KeyLen := 24;
|
||
|
CipherClass := TfrxDES3Cipher;
|
||
|
end;
|
||
|
OID_pbeWithSHA1And2_KeyTripleDES_CBC:
|
||
|
begin
|
||
|
IVLen := 8;
|
||
|
KeyLen := 16;
|
||
|
CipherClass := TfrxDES3Cipher;
|
||
|
end;
|
||
|
OID_pbeWithSHA1And128BitRC2_CBC:
|
||
|
begin
|
||
|
IVLen := 8;
|
||
|
KeyLen := 16;
|
||
|
CipherClass := TfrxRC2Cipher;
|
||
|
end;
|
||
|
OID_pbeWithSHA1And40BitRC2_CBC:
|
||
|
begin
|
||
|
IVLen := 8;
|
||
|
KeyLen := 5;
|
||
|
CipherClass := TfrxRC2Cipher;
|
||
|
end;
|
||
|
else
|
||
|
raise ESignException.Create('Invalid Encryption Algorithm.');
|
||
|
end;
|
||
|
|
||
|
IV := TfrxPKCS12Document.DerivingKey(Password, Salt, 2, OID_sha1,
|
||
|
Iterations, IVLen);
|
||
|
Key := TfrxPKCS12Document.DerivingKey(Password, Salt, 1, OID_sha1,
|
||
|
Iterations, KeyLen);
|
||
|
with CipherClass.Create(Key, IV) do
|
||
|
try
|
||
|
Result := DecodeToStr(EncryptedData);
|
||
|
finally
|
||
|
Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function ProcessEncryptedData(EncryptedPrivateKeyInfo: TfrxASN1Container;
|
||
|
Password: UTF8String): AnsiString;
|
||
|
begin
|
||
|
RaiseIf(Password = '', 'Empty Password');
|
||
|
|
||
|
// [0]
|
||
|
RaiseIf(EncryptedPrivateKeyInfo[0].IsNot(ASN1_TAG_SEQUENCE),
|
||
|
InvalidEncryptedData);
|
||
|
EncryptedPrivateKeyInfo := EncryptedPrivateKeyInfo[0] as TfrxASN1Container;
|
||
|
|
||
|
// EncryptedPrivateKeyInfo ::= SEQUENCE {
|
||
|
// encryptionAlgorithm EncryptionAlgorithmIdentifier,
|
||
|
// encryptedData EncryptedData }
|
||
|
RaiseIf(EncryptedPrivateKeyInfo.Count < 2, InvalidEncryptedData);
|
||
|
|
||
|
Result := EncriptData(EncryptedPrivateKeyInfo[0], EncryptedPrivateKeyInfo[1],
|
||
|
Password);
|
||
|
end;
|
||
|
|
||
|
{ TPrivateKey }
|
||
|
|
||
|
destructor TfrxRSAPrivateKey.Destroy;
|
||
|
begin
|
||
|
FreeAndNil(FKey);
|
||
|
inherited;
|
||
|
end;
|
||
|
|
||
|
function TfrxRSAPrivateKey.GetCoeficient: TfrxASN1Integer;
|
||
|
begin
|
||
|
Result := FKey[8] as TfrxASN1Integer;
|
||
|
end;
|
||
|
|
||
|
function TfrxRSAPrivateKey.GetExponent1: TfrxASN1Integer;
|
||
|
begin
|
||
|
Result := FKey[6] as TfrxASN1Integer;
|
||
|
end;
|
||
|
|
||
|
function TfrxRSAPrivateKey.GetExponent2: TfrxASN1Integer;
|
||
|
begin
|
||
|
Result := FKey[7] as TfrxASN1Integer;
|
||
|
end;
|
||
|
|
||
|
function TfrxRSAPrivateKey.GetModulus: TfrxASN1Integer;
|
||
|
begin
|
||
|
Result := FKey[1] as TfrxASN1Integer;
|
||
|
end;
|
||
|
|
||
|
function TfrxRSAPrivateKey.GetPrime1: TfrxASN1Integer;
|
||
|
begin
|
||
|
Result := FKey[4] as TfrxASN1Integer;
|
||
|
end;
|
||
|
|
||
|
function TfrxRSAPrivateKey.GetPrime2: TfrxASN1Integer;
|
||
|
begin
|
||
|
Result := FKey[5] as TfrxASN1Integer;
|
||
|
end;
|
||
|
|
||
|
function TfrxRSAPrivateKey.GetPrivateExponent: TfrxASN1Integer;
|
||
|
begin
|
||
|
Result := FKey[3] as TfrxASN1Integer;
|
||
|
end;
|
||
|
|
||
|
function TfrxRSAPrivateKey.GetPublicExponent: TfrxASN1Integer;
|
||
|
begin
|
||
|
Result := FKey[2] as TfrxASN1Integer;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxRSAPrivateKey.Read(PrivateKeyInfo, Attributes: TfrxASN1Container);
|
||
|
var
|
||
|
PrivateKeyAlgorithm: TfrxASN1Container;
|
||
|
KeyData: AnsiString;
|
||
|
KeyObj: TfrxASN1Base;
|
||
|
i: Integer;
|
||
|
begin
|
||
|
// https://tools.ietf.org/html/rfc5208
|
||
|
// PrivateKeyInfo ::= SEQUENCE {
|
||
|
// version Version,
|
||
|
// privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
|
||
|
// privateKey PrivateKey,
|
||
|
// attributes [0] IMPLICIT Attributes OPTIONAL }
|
||
|
//
|
||
|
// Attributes ::= SET OF Attribute
|
||
|
|
||
|
RaiseIf((PrivateKeyInfo.Count < 3)
|
||
|
or PrivateKeyInfo[0].IsNot(ASN1_TAG_INTEGER)
|
||
|
or PrivateKeyInfo[1].IsNot(ASN1_TAG_SEQUENCE)
|
||
|
or PrivateKeyInfo[2].IsNot(ASN1_TAG_OCTET_STRING),
|
||
|
InvalidPrivateKey);
|
||
|
|
||
|
// Version ::= INTEGER
|
||
|
FVersion := TfrxASN1Integer(PrivateKeyInfo[0]).Value;
|
||
|
|
||
|
// PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
|
||
|
PrivateKeyAlgorithm := TfrxASN1Container(PrivateKeyInfo[1]);
|
||
|
RaiseIf(PrivateKeyAlgorithm.IsEmpty or
|
||
|
PrivateKeyAlgorithm[0].IsNot(ASN1_TAG_OBJECT_ID),
|
||
|
InvalidPrivateKey);
|
||
|
|
||
|
FAlgorithmName := TfrxASN1ObjectID(PrivateKeyAlgorithm[0]).OIDName;
|
||
|
RaiseIf(FAlgorithmName <> OID_rsaEncryption, UnsupportedAlgorithm);
|
||
|
|
||
|
// PrivateKey ::= OCTET STRING
|
||
|
KeyData := TfrxASN1Data(PrivateKeyInfo[2]).Data;
|
||
|
RaiseIf(KeyData = '', InvalidPrivateKey);
|
||
|
|
||
|
KeyObj := ReadASN1Object(KeyData);
|
||
|
try
|
||
|
RaiseIf(not (KeyObj is TfrxASN1Container) or
|
||
|
(TfrxASN1Container(KeyObj).Count < 8), InvalidPrivateKey);
|
||
|
for i := 0 to 7 do
|
||
|
RaiseIf(not (TfrxASN1Container(KeyObj)[i] is TfrxASN1Integer),
|
||
|
InvalidPrivateKey);
|
||
|
except
|
||
|
on Exception do
|
||
|
begin
|
||
|
KeyObj.Free;
|
||
|
raise;
|
||
|
end;
|
||
|
end;
|
||
|
FKey := TfrxASN1Container(KeyObj);
|
||
|
end;
|
||
|
|
||
|
procedure TfrxRSAPrivateKey.ReadCrypted(EncryptedPrivateKeyInfo,
|
||
|
Attributes: TfrxASN1Container; Password: UTF8String);
|
||
|
var
|
||
|
Obj: TfrxASN1Base;
|
||
|
Data: AnsiString;
|
||
|
begin
|
||
|
Data := ProcessEncryptedData(EncryptedPrivateKeyInfo, Password);
|
||
|
Obj := ReadASN1Object(Data);
|
||
|
RaiseIf(not (Obj is TfrxASN1Container), InvalidPrivateKey);
|
||
|
Read(Obj as TfrxASN1Container, Attributes);
|
||
|
Obj.Free;
|
||
|
end;
|
||
|
|
||
|
{ TfrxX509Certificate }
|
||
|
|
||
|
function TfrxX509Certificate.CheckOwner(Owner: TfrxX509Certificate): Boolean;
|
||
|
begin
|
||
|
Result := FIssuer.IsEqual(Owner.FSubject);
|
||
|
if Result then
|
||
|
FOwner := Owner;
|
||
|
end;
|
||
|
|
||
|
function TfrxX509Certificate.CheckPrivateKey(PrivateKey: TfrxRSAPrivateKey): Boolean;
|
||
|
var
|
||
|
Obj: TfrxASN1Base;
|
||
|
Data1, Data2: AnsiString;
|
||
|
begin
|
||
|
Result := False;
|
||
|
if FPublicKey = nil then
|
||
|
Exit;
|
||
|
Obj := FPublicKey[0];
|
||
|
if not (Obj is TfrxASN1Data) then
|
||
|
Exit;
|
||
|
Data1 := PrivateKey.Modulus.Data;
|
||
|
Data2 := TfrxASN1Data(Obj).Data;
|
||
|
if Length(Data1) <> Length(Data2) then
|
||
|
Exit;
|
||
|
Result := CompareMem(@Data1[1], @Data2[1], Length(Data1));
|
||
|
if Result then
|
||
|
FPrivateKey := PrivateKey;
|
||
|
end;
|
||
|
|
||
|
constructor TfrxX509Certificate.Create;
|
||
|
begin
|
||
|
FIssuer := TfrxX509KeyValueList.Create;
|
||
|
FSubject := TfrxX509KeyValueList.Create;
|
||
|
FASN1Certificate := nil;
|
||
|
FPublicKey := nil;
|
||
|
FOwner := nil;
|
||
|
FPrivateKey := nil;
|
||
|
end;
|
||
|
|
||
|
destructor TfrxX509Certificate.Destroy;
|
||
|
begin
|
||
|
FPublicKey.Free;
|
||
|
FASN1Certificate.Free;
|
||
|
FIssuer.Free;
|
||
|
FSubject.Free;
|
||
|
inherited;
|
||
|
end;
|
||
|
|
||
|
class function TfrxX509Certificate.GetVersionShift(Version: TfrxASN1Base): Integer;
|
||
|
var
|
||
|
IsPresent: Boolean;
|
||
|
Ver: Integer;
|
||
|
begin
|
||
|
// version [0] EXPLICIT Version DEFAULT v1,
|
||
|
// Version ::= INTEGER { v1(0), v2(1), v3(2) }
|
||
|
// -- If present, version MUST be v2 or v3
|
||
|
with Version do
|
||
|
IsPresent := IsTag(ASN1_TAG_EOC) and IsClass(ASN1_CLASS_CONTEXT);
|
||
|
if IsPresent then
|
||
|
begin
|
||
|
Ver := TfrxASN1Integer(TfrxASN1Container(Version)[0]).Value;
|
||
|
RaiseIf(not (Ver in [1..2]), 'Invalid certificate version');
|
||
|
Result := 1;
|
||
|
end
|
||
|
else
|
||
|
Result := 0;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxX509Certificate.Load(Container: TfrxASN1Container);
|
||
|
var
|
||
|
tbsCertificate: TfrxASN1Container;
|
||
|
Shift: Integer;
|
||
|
begin
|
||
|
// https://tools.ietf.org/html/rfc5280#section-4.1
|
||
|
// Certificate ::= SEQUENCE {
|
||
|
// tbsCertificate TBSCertificate,
|
||
|
// signatureAlgorithm AlgorithmIdentifier,
|
||
|
// signatureValue BIT STRING }
|
||
|
FASN1Certificate := TfrxASN1Container(Container.CreateCopy);
|
||
|
|
||
|
// TBSCertificate ::= SEQUENCE {
|
||
|
// version [0] EXPLICIT Version DEFAULT v1,
|
||
|
// serialNumber CertificateSerialNumber,
|
||
|
// signature AlgorithmIdentifier,
|
||
|
// issuer Name,
|
||
|
// validity Validity,
|
||
|
// subject Name,
|
||
|
// subjectPublicKeyInfo SubjectPublicKeyInfo,
|
||
|
// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
|
||
|
tbsCertificate := FASN1Certificate[0] as TfrxASN1Container;
|
||
|
|
||
|
Shift := TfrxX509Certificate.GetVersionShift(tbsCertificate[0]);
|
||
|
|
||
|
RaiseIf(tbsCertificate.Count < 6 + Shift, 'Invalid certificate');
|
||
|
RaiseIf(not (tbsCertificate[0 + Shift] is TfrxASN1Integer),
|
||
|
'Invalid serial number of certificate');
|
||
|
|
||
|
LoadName(tbsCertificate[2 + Shift], FIssuer);
|
||
|
LoadValidity(tbsCertificate[3 + Shift]);
|
||
|
LoadName(tbsCertificate[4 + Shift], FSubject);
|
||
|
LoadSubjectPublicKey(tbsCertificate[5 + Shift]);
|
||
|
end;
|
||
|
|
||
|
procedure TfrxX509Certificate.LoadName(ASN1Name: TfrxASN1Base; Name: TfrxX509KeyValueList);
|
||
|
var
|
||
|
i, j: Integer;
|
||
|
RDNSequence, RelativeDistinguishedName, AttributeTypeAndValue: TfrxASN1Container;
|
||
|
begin
|
||
|
// Name ::= CHOICE { -- only one possibility for now --
|
||
|
// rdnSequence RDNSequence }
|
||
|
//
|
||
|
// RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
|
||
|
//
|
||
|
// RelativeDistinguishedName ::=
|
||
|
// SET SIZE (1..MAX) OF AttributeTypeAndValue
|
||
|
//
|
||
|
// AttributeTypeAndValue ::= SEQUENCE {
|
||
|
// type AttributeType,
|
||
|
// value AttributeValue }
|
||
|
//
|
||
|
// AttributeType ::= OBJECT IDENTIFIER
|
||
|
//
|
||
|
// AttributeValue ::= ANY -- DEFINED BY AttributeType
|
||
|
RaiseIf(not (ASN1Name is TfrxASN1Container), InvalidNameOfCertificate);
|
||
|
Name.Clear;
|
||
|
RDNSequence := TfrxASN1Container(ASN1Name);
|
||
|
for i := 0 to RDNSequence.Count - 1 do
|
||
|
begin
|
||
|
if not (RDNSequence[i] is TfrxASN1Container) then
|
||
|
Continue;
|
||
|
RelativeDistinguishedName := RDNSequence[i] as TfrxASN1Container;
|
||
|
for j := 0 to RelativeDistinguishedName.Count - 1 do
|
||
|
if RelativeDistinguishedName[j] is TfrxASN1Container then
|
||
|
begin
|
||
|
AttributeTypeAndValue := RelativeDistinguishedName[j] as TfrxASN1Container;
|
||
|
if (AttributeTypeAndValue.Count >= 2) and
|
||
|
(AttributeTypeAndValue[0] is TfrxASN1ObjectID) then
|
||
|
Name.AddX509KeyValue(TfrxASN1ObjectID(AttributeTypeAndValue[0]).OIDName,
|
||
|
AttributeTypeAndValue[1]);
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxX509Certificate.LoadSubjectPublicKey(subjectPublicKeyInfo: TfrxASN1Base);
|
||
|
var
|
||
|
algorithmIdentifier, subjectPublicKey, Key: TfrxASN1Base;
|
||
|
EncryptionAlgorithm: TfrxASN1Container;
|
||
|
Data: AnsiString;
|
||
|
begin
|
||
|
// SubjectPublicKeyInfo ::= SEQUENCE {
|
||
|
// algorithmIdentifier AlgorithmIdentifier,
|
||
|
// subjectPublicKey BIT STRING }
|
||
|
RaiseIf(not (subjectPublicKeyInfo is TfrxASN1Container) or
|
||
|
(TfrxASN1Container(subjectPublicKeyInfo).Count < 2),
|
||
|
InvalidNameOfCertificate);
|
||
|
|
||
|
algorithmIdentifier := TfrxASN1Container(subjectPublicKeyInfo)[0];
|
||
|
RaiseIf(not (algorithmIdentifier is TfrxASN1Container), InvalidNameOfCertificate);
|
||
|
EncryptionAlgorithm := TfrxASN1Container(AlgorithmIdentifier);
|
||
|
RaiseIf(not (EncryptionAlgorithm[0] is TfrxASN1ObjectID), InvalidNameOfCertificate);
|
||
|
RaiseIf(TfrxASN1ObjectID(EncryptionAlgorithm[0]).OIDName <> OID_rsaEncryption, UnsupportedAlgorithm);
|
||
|
|
||
|
subjectPublicKey := TfrxASN1Container(subjectPublicKeyInfo)[1];
|
||
|
RaiseIf(not (subjectPublicKey is TfrxASN1Data), InvalidNameOfCertificate);
|
||
|
Data := TfrxASN1Data(subjectPublicKey).Data;
|
||
|
RaiseIf(Length(Data) < 1, InvalidNameOfCertificate);
|
||
|
Key := ReadASN1Object(Data, 1);
|
||
|
try
|
||
|
RaiseIf(not (Key is TfrxASN1Container), InvalidNameOfCertificate);
|
||
|
FPublicKey := TfrxASN1Container(Key.CreateCopy);
|
||
|
finally
|
||
|
Key.Free;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxX509Certificate.LoadValidity(Validity: TfrxASN1Base);
|
||
|
begin
|
||
|
// https://tools.ietf.org/html/rfc5280#section-4.1
|
||
|
// Validity ::= SEQUENCE {
|
||
|
// notBefore Time,
|
||
|
// notAfter Time }
|
||
|
RaiseIf(not (Validity is TfrxASN1Container) or
|
||
|
(TfrxASN1Container(Validity).Count < 2),
|
||
|
'Wrong validity data');
|
||
|
|
||
|
//
|
||
|
// Time ::= CHOICE {
|
||
|
// utcTime UTCTime,
|
||
|
// generalTime GeneralizedTime }
|
||
|
|
||
|
{ TODO: Append support of the validity }
|
||
|
// flcX509Certificate: DecodeX509Validity + X509ValidityParseProc
|
||
|
// flcASN1: ASN1Parse
|
||
|
end;
|
||
|
|
||
|
{ TfrxX509KeyValue }
|
||
|
|
||
|
constructor TfrxX509KeyValue.Create(AKey: TOIDName; AValue: TfrxASN1Base);
|
||
|
begin
|
||
|
FKey := AKey;
|
||
|
FValue := AValue; // Should not freed
|
||
|
end;
|
||
|
|
||
|
function TfrxX509KeyValue.IsEqual(X509KeyValue: TfrxX509KeyValue): Boolean;
|
||
|
begin
|
||
|
Result := (Key = X509KeyValue.Key) and Value.IsEqual(X509KeyValue.FValue);
|
||
|
end;
|
||
|
|
||
|
{ TfrxX509KeyValueList }
|
||
|
|
||
|
procedure TfrxX509KeyValueList.AddX509KeyValue(Key: TOIDName; Value: TfrxASN1Base);
|
||
|
begin
|
||
|
Add(TfrxX509KeyValue.Create(Key, Value));
|
||
|
end;
|
||
|
|
||
|
function TfrxX509KeyValueList.GetX509KeyValue(Index: Integer): TfrxX509KeyValue;
|
||
|
begin
|
||
|
Result := (inherited Items[Index]) as TfrxX509KeyValue;
|
||
|
end;
|
||
|
|
||
|
function TfrxX509KeyValueList.IsEqual(KeyValueList: TfrxX509KeyValueList): Boolean;
|
||
|
var
|
||
|
i: Integer;
|
||
|
begin
|
||
|
Result := False;
|
||
|
if Count <> KeyValueList.Count then
|
||
|
Exit;
|
||
|
for i := 0 to Count - 1 do
|
||
|
if not Items[i].IsEqual(KeyValueList[i]) then
|
||
|
Exit;
|
||
|
Result := True;
|
||
|
end;
|
||
|
|
||
|
end.
|