FastReport_2022_VCL/LibD28/frxPDFPFX.pas
2024-01-01 16:13:08 +01:00

766 lines
22 KiB
ObjectPascal

unit frxPDFPFX;
{$I frx.inc}
interface
uses
{$IFNDEF Linux}
Windows,
{$ENDIF}
SysUtils, Classes, Math,
frxHelpers,
frxASN1, frxPDFX509Certificate;
type
TfrxPKCS12Document = class
private
FContentInfo: AnsiString;
FMacSalt: AnsiString;
FMacDigest: AnsiString;
FMacAlgorithmName: TOIDName;
FMacIterations: Integer;
FValidPassword: UTF8String;
FPrivateKeys: TOwnObjList;
FCertificates: TOwnObjList;
FChain: TfrxX509Certificate;
FChainLen: Integer;
function GetCertificates(i: Integer): TfrxX509Certificate;
function GetPrivateKeys(i: Integer): TfrxRSAPrivateKey;
protected
FDebugLog: TLogList;
procedure Clear;
procedure ClearMacData;
procedure LoadMacData(MacData: TfrxASN1Container);
procedure ProcessSafeBag(SafeBag: TfrxASN1Container);
class procedure PartCopy(Source: AnsiString; var Dest: AnsiString; APos: Integer);
class procedure PartCopy2(Source: AnsiString; var Dest: AnsiString; APos: Integer);
class function NormalizePassword(Password: UTF8String): AnsiString;
procedure ReadPrivateKey(BagId: TOIDName; BagValue, BagAttributes: TfrxASN1Container);
procedure ReadCertificate(BagValue, BagAttributes: TfrxASN1Container);
property Certificates[i: Integer]: TfrxX509Certificate read GetCertificates;
property PrivateKeys[i: Integer]: TfrxRSAPrivateKey read GetPrivateKeys;
public
constructor Create(ADebugLog: TLogList = nil);
destructor Destroy; override;
procedure LoadFromFile(AFileName: string);
procedure LoadFromStream(AStream: TStream);
function IsCheckPassword(Password: UTF8String): Boolean;
procedure Parse;
class function DerivingKey(Password: UTF8String; Salt: AnsiString; ID: Byte;
Algorithm: TOIDName; Iteration: Integer; Len: Cardinal): AnsiString;
property Chain: TfrxX509Certificate read FChain;
property ChainLen: Integer read FChainLen;
end;
implementation
uses
frxHash, frxCipher;
const
UnknownStructure = 'Unknown structure.';
function PKCS7ProcessData(Data: TfrxASN1Container): AnsiString;
begin
// Fix for using OCTET STRING as a Container like
// [0] (1 elem)
// OCTET STRING (1 elem)
// OCTET STRING (1 elem)
while (Data[0] is TfrxASN1Container) and (Data[0].IsTag(ASN1_TAG_OCTET_STRING)) do
begin
Data := TfrxASN1Container(Data[0]);
RaiseIf(Data.IsEmpty, UnknownStructure);
end;
RaiseIf(Data[0].IsNot(ASN1_TAG_OCTET_STRING), UnknownStructure);
Result := TfrxASN1Data(Data[0]).Data;
end;
function PKCS7ProcessEncryptedData(Content: TfrxASN1Container; Password: UTF8String): AnsiString;
const
InvalidEncryptedData = 'Invalid Encrypted Data.';
var
Version: TfrxASN1Base;
EncryptedData, EncryptedContentInfo: TfrxASN1Container;
begin
RaiseIf(Password = '', 'Empty Password');
// [0]
RaiseIf(Content[0].IsNot(ASN1_TAG_SEQUENCE), InvalidEncryptedData);
EncryptedData := Content[0] as TfrxASN1Container;
// EncryptedData ::= SEQUENCE {
// version Version,
// encryptedContentInfo EncryptedContentInfo }
RaiseIf(EncryptedData.Count < 2, InvalidEncryptedData);
Version := EncryptedData[0];
RaiseIf(not (Version is TfrxASN1Integer), InvalidEncryptedData);
RaiseIf(TfrxASN1Integer(Version).Value <> 0, 'Invalid Encryption Algorithm Version.');
// EncryptedContentInfo ::= SEQUENCE {
// contentType ContentType,
// contentEncryptionAlgorithm
// ContentEncryptionAlgorithmIdentifier,
// encryptedContent
// [0] IMPLICIT EncryptedContent OPTIONAL }
// EncryptedContent ::= OCTET STRING
RaiseIf(not (EncryptedData[1] is TfrxASN1Container) or
(TfrxASN1Container(EncryptedData[1]).Count < 3), InvalidEncryptedData);
EncryptedContentInfo := EncryptedData[1] as TfrxASN1Container;
Result := EncriptData(EncryptedContentInfo[1], EncryptedContentInfo[2], Password);
end;
function ExtractPKCS7Info(ContentInfo: TfrxASN1Container; Password: UTF8String; DebugLog: TLogList = nil): AnsiString;
var
ContentType: TfrxASN1Base;
Content: TfrxASN1Container;
begin
// ContentInfo ::= SEQUENCE {
// contentType ContentType,
// content
// [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
RaiseIf(ContentInfo.IsEmpty, 'ContentInfo.IsEmpty: ' + UnknownStructure, DebugLog);
// ContentType ::= OBJECT IDENTIFIER
ContentType := ContentInfo[0];
RaiseIf(ContentType.IsNot(ASN1_TAG_OBJECT_ID), 'ContentType.IsNot(ASN1_TAG_OBJECT_ID): ' + UnknownStructure, DebugLog);
if ContentInfo.Count >= 2 then
if ContentInfo[1].IsTag(ASN1_TAG_EOC) and ContentInfo[1].IsClass(ASN1_CLASS_CONTEXT) and
(ContentInfo[1] is TfrxASN1Container) and not TfrxASN1Container(ContentInfo[1]).IsEmpty then
begin
Content := ContentInfo[1] as TfrxASN1Container;
case TfrxASN1ObjectID(ContentType).OIDName of
OID_pkcs7_data:
begin
DebugLog.Add('OID_pkcs7_data');
Result := PKCS7ProcessData(Content);
end;
// OID_pkcs7_signedData:
// OID_pkcs7_envelopedData:
// OID_pkcs7_signedAndEnvelopedData:
// OID_pkcs7_digestedData:
OID_pkcs7_encryptedData: // The encrypted-data content type shall have ASN.1 type EncryptedData:
begin
DebugLog.Add('OID_pkcs7_encryptedData');
Result := PKCS7ProcessEncryptedData(Content, Password);
end;
else
RaiseIf(True, 'TfrxASN1ObjectID(ContentType).OIDName: Unsupported document.', DebugLog);
end
end
else
RaiseIf(True, 'ExtractPKCS7Info: ' + UnknownStructure, DebugLog);
end;
function HMAC(const Key, Text: AnsiString; HashAlgorithm: TOIDName; DebugLog: TLogList = nil): AnsiString;
var
HC: TfrxHashClass;
H: TfrxHash;
PAD, WrkPAD: array [0 .. 63] of Byte;
HashDigest: TfrxHashDigest;
i: Integer;
begin
HC := OIDtoHashClass(HashAlgorithm);
RaiseIf(HC = nil, 'Algorithm not supported', DebugLog);
H := HC.Create;
try
FillChar(PAD, SizeOf(PAD), 0);
if Length(Key) <= 64 then
Move(Key[1], PAD, Length(Key))
else
begin
H.Initialize;
H.UpdateByAnsi(Key);
H.Finalize;
H.MoveDigestTo(PAD);
end;
for i := 0 to 63 do
WrkPAD[i] := PAD[i] xor $36;
H.Initialize;
H.Update(WrkPAD, 64);
H.UpdateByAnsi(Text);
H.Finalize;
H.MoveDigestTo(HashDigest);
for i := 0 to 63 do
WrkPAD[i] := PAD[i] xor $5C;
H.Initialize;
H.Update(WrkPAD, 64);
H.Update(HashDigest, H.DigestSize);
H.Finalize;
Result := H.DigestToAnsi;
finally
H.Free;
end;
end;
{ TPKCS12Document }
procedure TfrxPKCS12Document.Clear;
begin
FValidPassword := '';
FContentInfo := '';
ClearMacData;
FCertificates.Clear;
FPrivateKeys.Clear;
end;
procedure TfrxPKCS12Document.ClearMacData;
begin
FMacSalt := '';
FMacAlgorithmName := OID_Undef;
FMacDigest := '';
FMacIterations := 1; // Default
end;
constructor TfrxPKCS12Document.Create(ADebugLog: TLogList = nil);
begin
FDebugLog := ADebugLog;
FCertificates := TOwnObjList.Create;
FPrivateKeys := TOwnObjList.Create;
end;
class function TfrxPKCS12Document.DerivingKey(Password: UTF8String;
Salt: AnsiString; ID: Byte; Algorithm: TOIDName; Iteration: Integer;
Len: Cardinal): AnsiString;
procedure FillData(out OutData: AnsiString; out OutLen: Integer; InData: AnsiString);
var
InLen: Integer;
i: Integer;
begin
InLen := Length(InData);
if InLen and $3F = 0 then
begin
OutLen := InLen;
OutData := InData;
end
else
begin
OutLen := ((InLen shr 6) + 1) shl 6;
SetLength(OutData, OutLen);
for i := 0 to OutLen - 1 do
OutData[i + 1] := InData[(i mod InLen) + 1];
end;
end;
var
NormalizedPassword: AnsiString;
i : Cardinal;
j, SLen, PLen: Integer;
V: Integer;
D, S, P, A, B: AnsiString;
H: TfrxHash;
HC: TfrxHashClass;
begin
Result := '';
HC := OIDtoHashClass(Algorithm);
if (Len = 0) or (HC = nil) then
Exit;
NormalizedPassword := NormalizePassword(PassWord);
V := 64;
SetLength(D, V);
FillChar(D[1], V, ID);
FillData(S, SLen, Salt);
FillData(P, PLen, NormalizedPassword);
SetLength(B, V);
SetLength(Result, Len);
H := HC.Create;
try
i := 0;
while True do
begin
H.Initialize;
H.Iterate(D + S + P, Iteration);
A := H.DigestToAnsi;
PartCopy(A, Result, i);
Inc(i, H.DigestSize);
if i >= Len then
Break;
j := 0;
while j < V do
begin
PartCopy(A, B, j);
Inc(j, H.DigestSize);
end;
j := 0;
while j < SLen do
begin
PartCopy2(B, S, j);
Inc(j, V);
end;
j := 0;
while j < PLen do
begin
PartCopy2(B, P, j);
Inc(j, V);
end;
end;
finally
H.Free;
end;
end;
destructor TfrxPKCS12Document.Destroy;
begin
FPrivateKeys.Free;
FCertificates.Free;
inherited;
end;
function TfrxPKCS12Document.GetCertificates(i: Integer): TfrxX509Certificate;
begin
Result := TfrxX509Certificate(FCertificates[i]);
end;
function TfrxPKCS12Document.GetPrivateKeys(i: Integer): TfrxRSAPrivateKey;
begin
Result := TfrxRSAPrivateKey(FPrivateKeys[i]);
end;
function TfrxPKCS12Document.IsCheckPassword(Password: UTF8String): Boolean;
var
Key: AnsiString;
Digest: AnsiString;
HSize: Cardinal;
begin
HSize := OIDtoHashClass(FMacAlgorithmName).DigestSize;
Key := DerivingKey(Password, FMacSalt, 3, FMacAlgorithmName, FMacIterations, HSize);
try
Digest := HMAC(Key, FContentInfo, FMacAlgorithmName, FDebugLog);
except
Result := False;
Exit;
end;
Result := FMacDigest = Digest;
if Result then
FValidPassword := Password;
end;
procedure TfrxPKCS12Document.LoadFromFile(AFileName: string);
var
FS: TFileStream;
begin
FS := TFileStream.Create(AFileName, fmOpenRead);
try
LoadFromStream(FS);
finally
FS.Free;
end;
end;
procedure TfrxPKCS12Document.LoadFromStream(AStream: TStream);
var
PFX: TfrxASN1Container;
Version, AuthSafe, MacData: TfrxASN1Base;
FASN1Doc: TfrxASN1Document;
begin
Clear;
FASN1Doc := TfrxASN1Document.Create;
try
FASN1Doc.LoadFromStream(AStream);
// PFX ::= SEQUENCE {
// version INTEGER {v3(3)}(v3,...),
// authSafe ContentInfo,
// macData MacData OPTIONAL }
RaiseIf((FASN1Doc.Count = 0) or FASN1Doc[0].IsNot(ASN1_TAG_SEQUENCE), 'ASN1 sequence not found.', FDebugLog);
PFX := FASN1Doc[0] as TfrxASN1Container;
RaiseIf(PFX.Count < 2, UnknownStructure);
Version := PFX[0]; // version INTEGER {v3(3)}(v3,...)
RaiseIf(Version.IsNot(ASN1_TAG_INTEGER) or (TfrxASN1Integer(Version).Value <> 3), 'Invalid version of document.', FDebugLog);
AuthSafe := PFX[1]; // authSafe ContentInfo
RaiseIf(AuthSafe.IsNot(ASN1_TAG_SEQUENCE), 'PKCS#7 information not found.', FDebugLog);
FContentInfo := ExtractPKCS7Info(AuthSafe as TfrxASN1Container, '', FDebugLog);
if PFX.Count > 2 then // macData MacData OPTIONAL
begin
MacData := PFX[2];
RaiseIf(MacData.IsNot(ASN1_TAG_SEQUENCE), 'MacData information not found.', FDebugLog);
LoadMacData(MacData as TfrxASN1Container);
end;
finally
FASN1Doc.Free;
end;
end;
procedure TfrxPKCS12Document.LoadMacData(MacData: TfrxASN1Container);
const
UnknownMacDataStructure = 'Unknown MacData structure.';
UnknownDigestStructure = 'Unknown digest structure.';
var
Mac, DigestAlgorithm: TfrxASN1Container;
Digest, MacSalt, Iterations: TfrxASN1Base;
begin
RaiseIf(MacData.Count < 2, 'MacData.Count < 2: ' + UnknownMacDataStructure, FDebugLog);
// MacData ::= SEQUENCE {
// mac DigestInfo,
// macSalt OCTET STRING,
// iterations INTEGER DEFAULT 1 }
ClearMacData;
// MacData.Mac
// DigestInfo ::= SEQUENCE {
// digestAlgorithm DigestAlgorithmIdentifier,
// digest Digest }
RaiseIf(MacData[0].IsNot(ASN1_TAG_SEQUENCE), 'MacData[0].IsNot(ASN1_TAG_SEQUENCE): ' + UnknownMacDataStructure, FDebugLog);
Mac := TfrxASN1Container(MacData[0]);
RaiseIf(Mac.Count <> 2, 'Mac.Count <> 2: ' + UnknownDigestStructure, FDebugLog);
// MacData.Mac.DigestAlgorithm
RaiseIf(Mac[0].IsNot(ASN1_TAG_SEQUENCE), 'Mac[0].IsNot(ASN1_TAG_SEQUENCE)' + UnknownDigestStructure, FDebugLog);
DigestAlgorithm := TfrxASN1Container(Mac[0]);
RaiseIf((DigestAlgorithm.IsEmpty) or DigestAlgorithm[0].IsNot(ASN1_TAG_Object_ID), UnknownDigestStructure, FDebugLog);
FMacAlgorithmName := TfrxASN1ObjectID(DigestAlgorithm[0]).OIDName;
RaiseIf(FMacAlgorithmName <> OID_sha1, 'Unsupported digest algorithm.', FDebugLog);
// MacData.Mac.Digest
// Digest ::= OCTET STRING
Digest := Mac[1];
RaiseIf(Digest.IsNot(ASN1_TAG_OCTET_STRING), 'Digest.IsNot(ASN1_TAG_OCTET_STRING): ' + UnknownDigestStructure, FDebugLog);
FMacDigest := TfrxASN1Data(Digest).Data;
// MacData.MacSalt
MacSalt := MacData[1];
RaiseIf(MacSalt.IsNot(ASN1_TAG_OCTET_STRING), 'MacSalt.IsNot(ASN1_TAG_OCTET_STRING):' + UnknownMacDataStructure, FDebugLog);
FMacSalt := TfrxASN1Data(MacSalt).Data;
// MacData.Iterations
if MacData.Count > 2 then
begin
Iterations := MacData[2];
RaiseIf(Iterations.IsNot(ASN1_TAG_INTEGER) or (TfrxASN1Integer(Iterations).IsOver64Bit), UnknownMacDataStructure, FDebugLog);
FMacIterations := TfrxASN1Integer(Iterations).Value;
end
else // DEFAULT 1
FMacIterations := 1;
end;
class function TfrxPKCS12Document.NormalizePassword(Password: UTF8String): AnsiString;
var
Pass: WideString;
i: Integer;
WCh: Word;
begin
Pass := {$IFDEF Delphi12} UTF8ToWideString(Password);
{$ELSE} UTF8Decode(Password);
{$ENDIF}
SetLength(Result, (Length(Pass) + 1) * 2);
for i := 1 to Length(Pass) do
begin
WCh := Word(Pass[i]);
Result[i shl 1 - 1] := AnsiChar(Byte(WCh shr 8));
Result[i shl 1] := AnsiChar(Byte(WCh and $FF));
end;
FillChar(Result[Length(Result) - 1], 2, 0);
end;
procedure TfrxPKCS12Document.Parse;
var
AuthSafe, SafeContents: TfrxASN1Container;
i, j: Integer;
Item: TfrxASN1Base;
Bag: AnsiString;
Certificate: TfrxX509Certificate;
begin
Item := nil; // Remove warning
try
Item := ReadASN1Object(FContentInfo);
except
on ESignException do
RaiseIf(True, 'Parse - ReadASN1Object: AuthenticatedSafe cannot loaded', FDebugLog);
end;
AuthSafe := TfrxASN1Container(Item);
FChain := nil;
FChainLen := 0;
FDebugLog.Add('AuthSafe.Count: ' + IntToStr(AuthSafe.Count));
try
for i := 0 to AuthSafe.Count - 1 do
if AuthSafe[i].IsTag(ASN1_TAG_SEQUENCE) then
begin
FDebugLog.Add('AuthSafe[' + IntToStr(i) + '].IsTag(ASN1_TAG_SEQUENCE)');
Bag := ExtractPKCS7Info(TfrxASN1Container(AuthSafe[i]), FValidPassword, FDebugLog);
Item := ReadASN1Object(Bag);
try
if Item.IsTag(ASN1_TAG_SEQUENCE) then
begin
// SafeContents ::= SEQUENCE OF SafeBag
SafeContents := TfrxASN1Container(Item);
FDebugLog.Add('SafeContents.Count: ' + IntToStr(SafeContents.Count));
for j := 0 to SafeContents.Count - 1 do
if SafeContents[j].IsTag(ASN1_TAG_SEQUENCE) then
begin
FDebugLog.Add('SafeContents[' + IntToStr(j) + '].IsTag(ASN1_TAG_SEQUENCE)');
ProcessSafeBag(SafeContents[j] as TfrxASN1Container);
end;
end;
finally
Item.Free;
end;
end;
RaiseIf(FCertificates.Count = 0,
'Certificates not found in pfx document', FDebugLog);
RaiseIf(FPrivateKeys.Count = 0,
'Private key not found in pfx document', FDebugLog);
for i := 0 to FPrivateKeys.Count - 1 do
begin
for j := 0 to FCertificates.Count - 1 do
if Certificates[j].CheckPrivateKey(PrivateKeys[i]) then
begin
FChain := Certificates[j];
Break;
end;
if FChain <> nil then
Break;
end;
RaiseIf(FChain = nil, 'Not found pair certificate and private key', FDebugLog);
for i := 0 to FCertificates.Count - 1 do
for j := 0 to FCertificates.Count - 1 do
if (i <> j) and Certificates[i].CheckOwner(Certificates[j]) then
Break;
Certificate := FChain;
while Certificate <> nil do
begin
Certificate := Certificate.Owner;
Inc(FChainLen);
end;
finally
AuthSafe.Free;
end;
end;
class procedure TfrxPKCS12Document.PartCopy(Source: AnsiString; var Dest: AnsiString; APos: Integer);
var
MoveCount: Integer;
begin
MoveCount := Min(Length(Source), Length(Dest) - APos);
if MoveCount > 0 then
Move(Source[1], Dest[1 + APos], MoveCount);
end;
class procedure TfrxPKCS12Document.PartCopy2(Source: AnsiString; var Dest: AnsiString; APos: Integer);
var
i: Integer;
W: Word;
D, S: PByteArray;
begin
if Source = '' then
Exit;
if (APos < 0) or (Length(Dest) < Length(Source) + APos) then
raise Exception.Create('Invalid parameters');
D := @Dest[APos + 1];
S := @Source[1];
W := 1;
for i := Length(Source) - 1 downto 0 do
begin
W := D^[i] + S^[i] + W;
D^[i] := Lo(W);
W := Hi(W);
end;
end;
procedure TfrxPKCS12Document.ProcessSafeBag(SafeBag: TfrxASN1Container);
var
BagId: TOIDName;
BagValue, BagAttributes: TfrxASN1Container;
i: Integer;
begin
// SafeBag ::= SEQUENCE {
// bagId BAG-TYPE.&id ({PKCS12BagSet})
// bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
// bagAttributes SET OF PKCS12Attribute OPTIONAL }
if (SafeBag.Count < 3) or
SafeBag[0].IsNot(ASN1_TAG_OBJECT_ID) or
not (SafeBag[1] is TfrxASN1Container) then
Exit;
BagId := TfrxASN1ObjectID(SafeBag[0]).OIDName;
BagValue := TfrxASN1Container(SafeBag[1]);
BagAttributes := nil;
if SafeBag[2] is TfrxASN1Container then
BagAttributes := SafeBag[2] as TfrxASN1Container;
// PKCS12BagSet BAG-TYPE ::= { keyBag | pkcs8ShroudedKeyBag | certBag | crlBag | secretBag | safeContentsBag }
case BagId of
OID_keyBag, OID_pkcs_8ShroudedKeyBag:
begin
FDebugLog.Add('BagId: OID_keyBag, OID_pkcs_8ShroudedKeyBag');
ReadPrivateKey(BagId, BagValue, BagAttributes);
end;
OID_certBag:
begin
FDebugLog.Add('BagId: OID_certBag');
ReadCertificate(BagValue, BagAttributes);
end;
OID_crlBag: ;
OID_secretBag: ;
OID_safeContentsBag:
// This recursive structure allows for arbitrary nesting of multiple KeyBags,
// PKCS8ShroudedKeyBags, CertBags, CRLBags, and SecretBags within the top-level SafeContents.
begin
FDebugLog.Add('BagId: OID_safeContentsBag; BagValue.Count: ' + IntToStr(BagValue.Count));
for i := 0 to BagValue.Count - 1 do
if BagValue[i] is TfrxASN1Container then
begin
FDebugLog.Add('BagValue[' + IntToStr(i) + '] is TfrxASN1Container');
ProcessSafeBag(BagValue[i] as TfrxASN1Container);
end;
end;
end;
end;
procedure TfrxPKCS12Document.ReadCertificate(BagValue, BagAttributes: TfrxASN1Container);
var
CertBag, CertValue: TfrxASN1Container;
Data: AnsiString;
CertObj: TfrxASN1Base;
Certificate: TfrxX509Certificate;
begin
// CertBag ::= SEQUENCE {
// certId BAG-TYPE.&id ({CertTypes}),
// certValue [0] EXPLICIT BAG-TYPE.&Type ({CertTypes}{@certId}) }
//
// x509Certificate BAG-TYPE ::=
// {OCTET STRING IDENTIFIED BY {certTypes 1}}
// -- DER-encoded X.509 certificate stored in OCTET STRING
// sdsiCertificate BAG-TYPE ::=
// {IA5String IDENTIFIED BY {certTypes 2}}
// -- Base64-encoded SDSI certificate stored in IA5String
//
// CertTypes BAG-TYPE ::= { x509Certificate | sdsiCertificate }
if BagValue.IsEmpty then
begin
FDebugLog.Add('BagValue.IsEmpty');
Exit;
end;
if not (BagValue[0] is TfrxASN1Container) then
begin
FDebugLog.Add('not (BagValue[0] is TfrxASN1Container)');
Exit;
end;
CertBag := TfrxASN1Container(BagValue[0]); // [0]
if (CertBag.Count < 2) or
not (CertBag[0] is TfrxASN1ObjectID) or
(TfrxASN1ObjectID(CertBag[0]).OIDName <> OID_x509Certificate) or
not (CertBag[1] is TfrxASN1Container) or
(TfrxASN1Container(CertBag[1]).IsEmpty) then
begin
FDebugLog.Add('CertBag: Exit');
Exit;
end;
CertValue := TfrxASN1Container(CertBag[1]);
if CertValue[0].IsNot(ASN1_TAG_OCTET_STRING) then
begin
FDebugLog.Add('CertValue[0].IsNot(ASN1_TAG_OCTET_STRING)');
Exit;
end;
Data := TfrxASN1Data(CertValue[0]).Data;
if Data = '' then
begin
FDebugLog.Add('Data = ""');
Exit;
end;
try
CertObj := ReadASN1Object(Data);
except
on Exception do
begin
CertObj := nil;
FDebugLog.Add('CertObj: raise ?');
end;
end;
if CertObj = nil then
Exit;
try
if not (CertObj is TfrxASN1Container) or
((CertObj as TfrxASN1Container).IsEmpty) or
not ((CertObj as TfrxASN1Container)[0] is TfrxASN1Container) then
begin
FDebugLog.Add('CertObj: Exit');
Exit;
end;
Certificate := TfrxX509Certificate.Create;
try
Certificate.Load(CertObj as TfrxASN1Container);
except
on Exception do
begin
FDebugLog.Add('Certificate: raise ?');
FreeAndNil(Certificate);
end;
end;
if Certificate <> nil then
FCertificates.Add(Certificate);
finally
CertObj.Free;
end;
end;
procedure TfrxPKCS12Document.ReadPrivateKey(BagId: TOIDName; BagValue, BagAttributes: TfrxASN1Container);
var
PK: TfrxRSAPrivateKey;
begin
PK := TfrxRSAPrivateKey.Create;
try
case BagId of
OID_keyBag: // KeyBag ::= PrivateKeyInfo (PKCS #8)
begin
FDebugLog.Add('BagId: OID_keyBag');
PK.Read(BagValue, BagAttributes);
end;
OID_pkcs_8ShroudedKeyBag: // PKCS8ShroudedKeyBag ::= EncryptedPrivateKeyInfo (PKCS #8)
begin
FDebugLog.Add('BagId: OID_pkcs_8ShroudedKeyBag');
PK.ReadCrypted(BagValue, BagAttributes, FValidPassword);
end;
end;
except
on ESignException do
begin
FreeAndNil(PK);
FDebugLog.Add('raise ?');
end;
end;
if PK <> nil then
FPrivateKeys.Add(PK);
end;
end.