xtool/contrib/fundamentals/Utils/flcX509Certificate.pas

2125 lines
78 KiB
ObjectPascal

{******************************************************************************}
{ }
{ Library: Fundamentals 5.00 }
{ File name: flcX509Certificate.pas }
{ File version: 5.07 }
{ Description: X.509 certificate }
{ }
{ Copyright: Copyright (c) 2010-2020, David J Butler }
{ All rights reserved. }
{ Redistribution and use in source and binary forms, with }
{ or without modification, are permitted provided that }
{ the following conditions are met: }
{ Redistributions of source code must retain the above }
{ copyright notice, this list of conditions and the }
{ following disclaimer. }
{ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND }
{ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED }
{ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED }
{ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A }
{ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL }
{ THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, }
{ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR }
{ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, }
{ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF }
{ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) }
{ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER }
{ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING }
{ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE }
{ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE }
{ POSSIBILITY OF SUCH DAMAGE. }
{ }
{ Github: https://github.com/fundamentalslib }
{ E-mail: fundamentals.library at gmail.com }
{ }
{ References: }
{ }
{ RFC 2459 Internet X.509 Public Key Infrastructure - Jan 1999 }
{ RFC 3280 Internet X.509 Public Key Infrastructure - Apr 2002 }
{ RFC 5280 Internet X.509 Public Key Infrastructure - May 2008 }
{ }
{ Revision history: }
{ }
{ 2010/11/21 0.01 Initial development: Encoding }
{ 2010/11/23 0.02 Initial development: Decoding }
{ 2010/11/24 0.03 Test case: Example D.1 from RFC 2459 }
{ 2010/12/15 0.04 RSAPrivateKey structure }
{ 2015/03/31 4.05 Revision for Fundamentals 4 }
{ 2018/07/17 5.06 Revised for Fundamentals 5 }
{ 2019/09/24 5.07 Compilable with FreePascal 3.04 }
{ }
{******************************************************************************}
{$INCLUDE ..\flcInclude.inc}
{$IFDEF DEBUG}
{$IFDEF TEST}
{$DEFINE X509_TEST}
{$ENDIF}
{$ENDIF}
unit flcX509Certificate;
interface
uses
{ System }
SysUtils,
{ Fundamentals }
flcStdTypes,
flcASN1;
{ }
{ X.509 errors }
{ }
type
EX509 = class(Exception);
{ }
{ X.509 structures }
{ }
{ Name }
type
TX509AttributeType = TASN1ObjectIdentifier;
TX509AttributeValue = RawByteString;
TX509AttributeTypeAndValue = record
AType : TX509AttributeType;
Value : TX509AttributeValue;
_Decoded : Boolean;
end;
PX509AttributeTypeAndValue = ^TX509AttributeTypeAndValue;
const
OID_X509At : array[0..2] of Integer = (2, 5, 4);
OID_X509At_Name : array[0..3] of Integer = (2, 5, 4, 41);
OID_X509At_Surname : array[0..3] of Integer = (2, 5, 4, 4);
OID_X509At_GivenName : array[0..3] of Integer = (2, 5, 4, 42);
OID_X509At_Initials : array[0..3] of Integer = (2, 5, 4, 43);
OID_X509At_GenerationQualifier : array[0..3] of Integer = (2, 5, 4, 41);
OID_X509At_CommonName : array[0..3] of Integer = (2, 5, 4, 3);
OID_X509At_LocalityName : array[0..3] of Integer = (2, 5, 4, 7);
OID_X509At_StateOrProvince : array[0..3] of Integer = (2, 5, 4, 8);
OID_X509At_OrganizationName : array[0..3] of Integer = (2, 5, 4, 10);
OID_X509At_OrganizationUnitName : array[0..3] of Integer = (2, 5, 4, 11);
OID_X509At_Title : array[0..3] of Integer = (2, 5, 4, 12);
OID_X509At_DnQualifier : array[0..3] of Integer = (2, 5, 4, 46);
OID_X509At_CountryName : array[0..3] of Integer = (2, 5, 4, 6);
OID_X509At_SerialNumber : array[0..3] of Integer = (2, 5, 4, 5);
OID_X509At_Pseudonym : array[0..3] of Integer = (2, 5, 4, 65);
OID_PKCS9 : array[0..5] of Integer = (1, 2, 840, 113549, 1, 9);
OID_PKCS9_EmailAddress : array[0..6] of Integer = (1, 2, 840, 113549, 1, 9, 1);
OID_AtDomainComponent : array[0..6] of Integer = (0, 9, 2342, 19200300, 100, 1, 25);
procedure InitX509AttributeTypeAndValue(var A: TX509AttributeTypeAndValue; const AType: TX509AttributeType; const Value: TX509AttributeValue);
procedure InitX509AtName(var A: TX509AttributeTypeAndValue; const B: RawByteString);
procedure InitX509AtSurname(var A: TX509AttributeTypeAndValue; const B: RawByteString);
procedure InitX509AtGivenName(var A: TX509AttributeTypeAndValue; const B: RawByteString);
procedure InitX509AtInitials(var A: TX509AttributeTypeAndValue; const B: RawByteString);
procedure InitX509AtGenerationQuailifier(var A: TX509AttributeTypeAndValue; const B: RawByteString);
procedure InitX509AtCommonName(var A: TX509AttributeTypeAndValue; const B: RawByteString);
procedure InitX509AtLocailityName(var A: TX509AttributeTypeAndValue; const B: RawByteString);
procedure InitX509AtStateOrProvince(var A: TX509AttributeTypeAndValue; const B: RawByteString);
procedure InitX509AtOriganizationName(var A: TX509AttributeTypeAndValue; const B: RawByteString);
procedure InitX509AtOriganizationUnitName(var A: TX509AttributeTypeAndValue; const B: RawByteString);
procedure InitX509AtTitle(var A: TX509AttributeTypeAndValue; const B: RawByteString);
procedure InitX509AtDnQualifier(var A: TX509AttributeTypeAndValue; const B: RawByteString);
procedure InitX509AtCountryName(var A: TX509AttributeTypeAndValue; const B: RawByteString);
procedure InitX509AtEmailAddress(var A: TX509AttributeTypeAndValue; const B: RawByteString);
function EncodeX509AttributeTypeAndValue(const A: TX509AttributeTypeAndValue): RawByteString;
function DecodeX509AttributeTypeAndValue(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509AttributeTypeAndValue): Integer;
type
TX509RelativeDistinguishedName = array of TX509AttributeTypeAndValue;
PX509RelativeDistinguishedName = ^TX509RelativeDistinguishedName;
procedure AppendX509RelativeDistinguishedName(var A: TX509RelativeDistinguishedName; const V: TX509AttributeTypeAndValue);
function EncodeX509RelativeDistinguishedName(const A: TX509RelativeDistinguishedName): RawByteString;
function DecodeX509RelativeDistinguishedName(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509RelativeDistinguishedName): Integer;
type
TX509RDNSequence = array of TX509RelativeDistinguishedName;
PX509RDNSequence = ^TX509RDNSequence;
TX509Name = TX509RDNSequence;
procedure AppendX509RDNSequence(var A: TX509RDNSequence; const B: TX509RelativeDistinguishedName);
function EncodeX509RDNSequence(const A: TX509RDNSequence): RawByteString;
function DecodeX509RDNSequence(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509RDNSequence): Integer;
function EncodeX509Name(const A: TX509Name): RawByteString;
function DecodeX509Name(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509Name): Integer;
{ Version }
type
TX509Version = (
X509v1 = 0,
X509v2 = 1,
X509v3 = 2,
X509vUndefined = $FF // implementation defined value
);
const
CurrentX509Version = X509v3;
procedure InitX509Version(var A: TX509Version);
function EncodeX509Version(const A: TX509Version): RawByteString;
function DecodeX509Version(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509Version): Integer;
{ Time }
type
TX509Time = TDateTime;
function EncodeX509Time(const A: TX509Time): RawByteString;
function DecodeX509Time(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509Time): Integer;
{ Validity }
type
TX509Validity = record
NotBefore : TX509Time;
NotAfter : TX509Time;
_Decoded : Boolean;
end;
PX509Validity = ^TX509Validity;
function EncodeX509Validity(const A: TX509Validity): RawByteString;
function DecodeX509Validity(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509Validity): Integer;
{ Diffie-Hellman DomainParameters }
type
TX509DHValidationParms = record
Seed : RawByteString;
PgenCounter : Integer;
end;
PX509DHValidationParms = ^TX509DHValidationParms;
TX509DHDomainParameters = record
P : RawByteString;
G : RawByteString;
Q : RawByteString;
J : RawByteString;
ValidationParms : TX509DHValidationParms;
end;
PX509DHDomainParameters = ^TX509DHDomainParameters;
function EncodeX509DHValidationParms(const A: TX509DHValidationParms): RawByteString;
function DecodeX509DHValidationParms(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509DHValidationParms): Integer;
function EncodeX509DHDomainParameters(const A: TX509DHDomainParameters): RawByteString;
function DecodeX509DHDomainParameters(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509DHDomainParameters): Integer;
{ DSS Parms }
type
TX509DSSParms = record
P : RawByteString;
Q : RawByteString;
G : RawByteString;
end;
PX509DSSParms = ^TX509DSSParms;
function EncodeX509DSSParms(const A: TX509DSSParms): RawByteString;
function DecodeX509DSSParms(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509DSSParms): Integer;
{ DSS Sig Value }
type
TX509DSSSigValue = record
R : RawByteString;
S : RawByteString;
end;
PX509DSSSigValue = ^TX509DSSSigValue;
function EncodeX509DSSSigValue(const A: TX509DSSSigValue): RawByteString;
function DecodeX509DSSSigValue(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509DSSSigValue): Integer;
{ AlgorithmIdentifier }
type
TX509AlgorithmIdentifier = record
Algorithm : TASN1ObjectIdentifier;
Parameters : RawByteString;
_Decoded : Boolean;
end;
PX509AlgorithmIdentifier = ^TX509AlgorithmIdentifier;
procedure InitX509AlgorithmIdentifier(var A: TX509AlgorithmIdentifier; const Algorithm: array of Integer; const Parameters: RawByteString);
procedure InitX509AlgorithmIdentifierDSA_SHA1(var A: TX509AlgorithmIdentifier; const Parameters: RawByteString);
function EncodeX509AlgorithmIdentifier(const A: TX509AlgorithmIdentifier): RawByteString;
function DecodeX509AlgorithmIdentifier(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509AlgorithmIdentifier): Integer;
{ PublicKeyInfo }
type
TX509RSAPublicKey = record
Modulus : RawByteString;
PublicExponent : RawByteString;
end;
PX509RSAPublicKey = ^TX509RSAPublicKey;
function EncodeX509RSAPublicKey(const A: TX509RSAPublicKey): RawByteString;
function DecodeX509RSAPublicKey(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509RSAPublicKey): Integer;
function ParseX509RSAPublicKey(const Buf; const Size: Integer; var A: TX509RSAPublicKey): Integer;
type
TX509DHPublicKey = RawByteString;
function EncodeX509DHPublicKey(const A: TX509DHPublicKey): RawByteString;
type
TX509DSAPublicKey = RawByteString;
function EncodeX509DSAPublicKey(const A: TX509DSAPublicKey): RawByteString;
type
TX509SubjectPublicKeyInfo = record
Algorithm : TX509AlgorithmIdentifier;
SubjectPublicKey : RawByteString;
_Decoded : Boolean;
end;
PX509SubjectPublicKeyInfo = ^TX509SubjectPublicKeyInfo;
procedure InitX509SubjectPublicKeyInfoDSA(var A: TX509SubjectPublicKeyInfo; const B: TX509DSSParms; const PublicKey: RawByteString);
function EncodeX509SubjectPublicKeyInfo(const A: TX509SubjectPublicKeyInfo): RawByteString;
function DecodeX509SubjectPublicKeyInfo(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509SubjectPublicKeyInfo): Integer;
function ParseX509SubjectPublicKeyInfo(const Buf; const Size: Integer; var A: TX509SubjectPublicKeyInfo): Integer;
{ GeneralName }
type
TX509GeneralNameType = (
gnOtherName = 0,
gnRFC822Name = 1,
gnDNSName = 2,
gnX400Address = 3,
gnDirectoryName = 4,
gnEDIPartyName = 5,
gnUniformResourceIdentifier = 6,
gnIPAddress = 7,
gnRegisteredID = 8);
function EncodeX509GeneralName(const A: TX509GeneralNameType; const EncodedName: RawByteString): RawByteString;
function DecodeX509GeneralName(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509GeneralNameType; var B: RawByteString): Integer;
type
TX509GeneralNames = array of record
NameType : TX509GeneralNameType;
Name : RawByteString;
end;
PX509GeneralNames = ^TX509GeneralNames;
function EncodeX509GeneralNames(const A: TX509GeneralNames): RawByteString;
function DecodeX509GeneralNames(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509GeneralNames): Integer;
{ Extensions }
type
TX509BasicConstraints = record
CA : Boolean;
PathLenConstraint : RawByteString;
_DecodedCA : Boolean;
end;
PX509BasicConstraints = ^TX509BasicConstraints;
function EncodeX509BasicConstraints(const A: TX509BasicConstraints): RawByteString;
function DecodeX509BasicConstraints(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509BasicConstraints): Integer;
type
TX509AuthorityKeyIdentifier = record
KeyIdentifier : RawByteString;
AuthorityCertIssuer : TX509GeneralNames;
AuthorityCertSerialNumber : Int64;
end;
PX509AuthorityKeyIdentifier = ^TX509AuthorityKeyIdentifier;
function EncodeX509AuthorityKeyIdentifier(const A: TX509AuthorityKeyIdentifier): RawByteString;
type
TX509SubjectKeyIdentifier = RawByteString;
type
TX509KeyUsage = RawByteString;
type
TX509Extension = record
ExtnID : TASN1ObjectIdentifier;
Critical : Boolean;
ExtnValue : RawByteString;
_DecodedCritical : Boolean;
_Decoded : Boolean;
end;
PX509Extension = ^TX509Extension;
const
OID_CE : array[0..2] of Integer = (2, 5, 29);
OID_CE_AuthorityKeyIdentifier : array[0..3] of Integer = (2, 5, 29, 35);
OID_CE_SubjectKeyIdentifier : array[0..3] of Integer = (2, 5, 29, 14);
OID_CE_KeyUsage : array[0..3] of Integer = (2, 5, 29, 15);
OID_CE_PrivateKeyUsagePeriod : array[0..3] of Integer = (2, 5, 29, 16);
OID_CE_CertificatePolicies : array[0..3] of Integer = (2, 5, 29, 32);
OID_CE_PolicyMappings : array[0..3] of Integer = (2, 5, 29, 33);
OID_CE_SubjectAltName : array[0..3] of Integer = (2, 5, 29, 17);
OID_CE_IssuerAltName : array[0..3] of Integer = (2, 5, 29, 18);
OID_CE_SubjectDirectoryAttributes : array[0..3] of Integer = (2, 5, 29, 9);
OID_CE_BasicConstraints : array[0..3] of Integer = (2, 5, 29, 19);
OID_CE_NameConstraints : array[0..3] of Integer = (2, 5, 29, 30);
OID_CE_PolicyConstraints : array[0..3] of Integer = (2, 5, 29, 36);
OID_CE_CRLDistributionPoints : array[0..3] of Integer = (2, 5, 29, 31);
procedure InitX509ExtAuthorityKeyIdentifier(var A: TX509Extension; const B: TX509AuthorityKeyIdentifier);
procedure InitX509ExtSubjectKeyIdentifier(var A: TX509Extension; const B: TX509SubjectKeyIdentifier);
procedure InitX509ExtBasicConstraints(var A: TX509Extension; const B: TX509BasicConstraints);
function EncodeX509Extension(const A: TX509Extension): RawByteString;
function DecodeX509Extension(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509Extension): Integer;
type
TX509Extensions = array of TX509Extension;
PX509Extensions = ^TX509Extensions;
procedure AppendX509Extensions(var A: TX509Extensions; const B: TX509Extension);
function EncodeX509Extensions(const A: TX509Extensions): RawByteString;
function DecodeX509Extensions(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509Extensions): Integer;
{ Key }
function NormaliseX509IntKeyBuf(var KeyBuf: RawByteString): Integer;
{ TBSCertificate }
type
TX509TBSCertificate = record
Version : TX509Version;
SerialNumber : RawByteString;
Signature : TX509AlgorithmIdentifier;
Issuer : TX509Name;
Validity : TX509Validity;
Subject : TX509Name;
SubjectPublicKeyInfo : TX509SubjectPublicKeyInfo;
IssuerUniqueID : RawByteString;
SubjectUniqueID : RawByteString;
Extensions : TX509Extensions;
_DecodedVersion : Boolean;
_Decoded : Boolean;
end;
PX509TBSCertificate = ^TX509TBSCertificate;
function EncodeX509TBSCertificate(const A: TX509TBSCertificate): RawByteString;
function DecodeX509TBSCertificate(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509TBSCertificate): Integer;
{ Certificate }
type
TX509Certificate = record
TBSCertificate : TX509TBSCertificate;
SignatureAlgorithm : TX509AlgorithmIdentifier;
SignatureValue : RawByteString;
_Decoded : Boolean;
end;
PX509Certificate = ^TX509Certificate;
TX509CertificateArray = array of TX509Certificate;
procedure InitX509Certificate(var A: TX509Certificate);
function EncodeX509Certificate(const A: TX509Certificate): RawByteString;
function DecodeX509Certificate(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509Certificate): Integer;
function ParseX509Certificate(const Buf; const Size: Integer; var A: TX509Certificate): Integer;
procedure ParseX509CertificateStr(const BufStr: RawByteString; var A: TX509Certificate);
procedure ParseX509CertificatePEM(const BufStr: RawByteString; var A: TX509Certificate);
{ RSAPrivateKey }
type
TX509RSAPrivateKey = record
Version : Integer;
Modulus : RawByteString;
PublicExponent : RawByteString;
PrivateExponent : RawByteString;
Prime1 : RawByteString;
Prime2 : RawByteString;
Exponent1 : RawByteString;
Exponent2 : RawByteString;
CRTCoefficient : RawByteString;
_Decoded : Boolean;
end;
PX509RSAPrivateKey = ^TX509RSAPrivateKey;
function EncodeX509RSAPrivateKey(const A: TX509RSAPrivateKey): RawByteString;
function DecodeX509RSAPrivateKey(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509RSAPrivateKey): Integer;
function ParseX509RSAPrivateKey(const Buf; const Size: Integer; var A: TX509RSAPrivateKey): Integer;
procedure ParseX509RSAPrivateKeyStr(const BufStr: RawByteString; var A: TX509RSAPrivateKey);
procedure ParseX509RSAPrivateKeyPEM(const BufStr: RawByteString; var A: TX509RSAPrivateKey);
{ }
{ Test }
{ }
{$IFDEF X509_TEST}
procedure Test;
{$ENDIF}
implementation
uses
{ Fundamentals }
flcBase64;
{ }
{ X.509 errors }
{ }
const
SErr_InvalidBuffer = 'Invalid buffer';
SErr_DecodeError = 'Decode error';
SErr_EncodeError = 'Encode error';
{ }
{ X.509 structures }
{ }
(*
AttributeType ::= OBJECT IDENTIFIER
AttributeValue ::= ANY DEFINED BY AttributeType
AttributeTypeAndValue ::= SEQUENCE {
type AttributeType,
value AttributeValue }
*)
procedure InitX509AttributeTypeAndValue(
var A: TX509AttributeTypeAndValue;
const AType: TX509AttributeType; const Value: TX509AttributeValue);
begin
A.AType := AType;
A.Value := Value;
end;
procedure InitX509AtName(var A: TX509AttributeTypeAndValue; const B: RawByteString);
begin
ASN1OIDInit(A.AType, OID_X509At_Name);
A.Value := B;
end;
procedure InitX509AtSurname(var A: TX509AttributeTypeAndValue; const B: RawByteString);
begin
ASN1OIDInit(A.AType, OID_X509At_Surname);
A.Value := B;
end;
procedure InitX509AtGivenName(var A: TX509AttributeTypeAndValue; const B: RawByteString);
begin
ASN1OIDInit(A.AType, OID_X509At_GivenName);
A.Value := B;
end;
procedure InitX509AtInitials(var A: TX509AttributeTypeAndValue; const B: RawByteString);
begin
ASN1OIDInit(A.AType, OID_X509At_Initials);
A.Value := B;
end;
procedure InitX509AtGenerationQuailifier(var A: TX509AttributeTypeAndValue; const B: RawByteString);
begin
ASN1OIDInit(A.AType, OID_X509At_GenerationQualifier);
A.Value := B;
end;
procedure InitX509AtCommonName(var A: TX509AttributeTypeAndValue; const B: RawByteString);
begin
ASN1OIDInit(A.AType, OID_X509At_CommonName);
A.Value := B;
end;
procedure InitX509AtLocailityName(var A: TX509AttributeTypeAndValue; const B: RawByteString);
begin
ASN1OIDInit(A.AType, OID_X509At_LocalityName);
A.Value := B;
end;
procedure InitX509AtStateOrProvince(var A: TX509AttributeTypeAndValue; const B: RawByteString);
begin
ASN1OIDInit(A.AType, OID_X509At_StateOrProvince);
A.Value := B;
end;
procedure InitX509AtOriganizationName(var A: TX509AttributeTypeAndValue; const B: RawByteString);
begin
ASN1OIDInit(A.AType, OID_X509At_OrganizationName);
A.Value := B;
end;
procedure InitX509AtOriganizationUnitName(var A: TX509AttributeTypeAndValue; const B: RawByteString);
begin
ASN1OIDInit(A.AType, OID_X509At_OrganizationUnitName);
A.Value := B;
end;
procedure InitX509AtTitle(var A: TX509AttributeTypeAndValue; const B: RawByteString);
begin
ASN1OIDInit(A.AType, OID_X509At_Title);
A.Value := B;
end;
procedure InitX509AtDnQualifier(var A: TX509AttributeTypeAndValue; const B: RawByteString);
begin
ASN1OIDInit(A.AType, OID_X509At_DnQualifier);
A.Value := B;
end;
procedure InitX509AtCountryName(var A: TX509AttributeTypeAndValue; const B: RawByteString);
begin
ASN1OIDInit(A.AType, OID_X509At_CountryName);
A.Value := B;
end;
procedure InitX509AtEmailAddress(var A: TX509AttributeTypeAndValue; const B: RawByteString);
begin
ASN1OIDInit(A.AType, OID_PKCS9_EmailAddress);
A.Value := B;
end;
function EncodeX509AttributeTypeAndValue(const A: TX509AttributeTypeAndValue): RawByteString;
begin
Result :=
ASN1EncodeSequence(
ASN1EncodeOID(A.AType) +
ASN1EncodePrintableString(A.Value)
);
end;
procedure X509AttributeTypeAndValueParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509AttributeTypeAndValue;
begin
D := Pointer(CallerData);
case ObjectIdx of
0 : ASN1DecodeOID(TypeID, DataBuf, DataSize, D^.AType);
1 : ASN1DecodeString(TypeID, DataBuf, DataSize, D^.Value);
end;
if ObjectIdx >= 1 then
D^._Decoded := True;
end;
function DecodeX509AttributeTypeAndValue(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509AttributeTypeAndValue): Integer;
begin
Assert(TypeID = ASN1_ID_SEQUENCE);
A._Decoded := False;
Result := ASN1Parse(Buf, Size, X509AttributeTypeAndValueParseProc, NativeInt(@A));
if not A._Decoded then
raise EX509.Create(SErr_DecodeError); // incomplete structure
end;
(*
RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
*)
procedure AppendX509RelativeDistinguishedName(var A: TX509RelativeDistinguishedName; const V: TX509AttributeTypeAndValue);
var L : Integer;
begin
L := Length(A);
SetLength(A, L + 1);
A[L] := V;
end;
function EncodeX509RelativeDistinguishedName(const A: TX509RelativeDistinguishedName): RawByteString;
var I : Integer;
S : RawByteString;
begin
S := '';
for I := 0 to Length(A) - 1 do
S := S + EncodeX509AttributeTypeAndValue(A[I]);
Result := ASN1EncodeSet(S);
end;
procedure X509RelativeDistinguishedNameParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509RelativeDistinguishedName;
L : Integer;
begin
D := Pointer(CallerData);
L := Length(D^);
SetLength(D^, L + 1);
DecodeX509AttributeTypeAndValue(TypeID, DataBuf, DataSize, D^[L]);
end;
function DecodeX509RelativeDistinguishedName(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509RelativeDistinguishedName): Integer;
begin
Assert(TypeID = ASN1_ID_SET);
Result := ASN1Parse(Buf, Size, X509RelativeDistinguishedNameParseProc, NativeInt(@A));
end;
(*
Name ::= CHOICE {
RDNSequence }
RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
*)
procedure AppendX509RDNSequence(var A: TX509RDNSequence; const B: TX509RelativeDistinguishedName);
var L : Integer;
begin
L := Length(A);
SetLength(A, L + 1);
A[L] := B;
end;
function EncodeX509RDNSequence(const A: TX509RDNSequence): RawByteString;
var I : Integer;
S : RawByteString;
begin
S := '';
for I := 0 to Length(A) - 1 do
S := S + EncodeX509RelativeDistinguishedName(A[I]);
Result := ASN1EncodeSequence(S);
end;
procedure X509RDNSequenceParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509RDNSequence;
L : Integer;
begin
D := Pointer(CallerData);
L := Length(D^);
SetLength(D^, L + 1);
DecodeX509RelativeDistinguishedName(TypeID, DataBuf, DataSize, D^[L]);
end;
function DecodeX509RDNSequence(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509RDNSequence): Integer;
begin
Assert(TypeID = ASN1_ID_SEQUENCE);
Result := ASN1Parse(Buf, Size, X509RDNSequenceParseProc, NativeInt(@A));
end;
function EncodeX509Name(const A: TX509Name): RawByteString;
begin
Result := EncodeX509RDNSequence(A);
end;
function DecodeX509Name(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509Name): Integer;
begin
Result := DecodeX509RDNSequence(TypeID, Buf, Size, A);
end;
(*
Version ::= INTEGER { v1(0), v2(1), v3(2) }
*)
procedure InitX509Version(var A: TX509Version);
begin
A := CurrentX509Version;
end;
function EncodeX509Version(const A: TX509Version): RawByteString;
begin
Result := ASN1EncodeInteger32(Ord(A));
end;
function DecodeX509Version(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509Version): Integer;
var I : Int32;
begin
Result := ASN1DecodeInteger32(TypeID, Buf, Size, I);
A := TX509Version(I);
end;
(*
Time ::= CHOICE {
utcTime UTCTime,
generalTime GeneralizedTime }
CAs conforming to this profile MUST always encode certificate
validity dates through the year 2049 as UTCTime; certificate validity
dates in 2050 or later MUST be encoded as GeneralizedTime.
For the purposes of this profile, UTCTime values MUST be expressed
Greenwich Mean Time (Zulu) and MUST include seconds (i.e., times are
YYMMDDHHMMSSZ), even where the number of seconds is zero. Conforming
systems MUST interpret the year field (YY) as follows:
Where YY is greater than or equal to 50, the year shall be
interpreted as 19YY; and
Where YY is less than 50, the year shall be interpreted as 20YY.
For the purposes of this profile, GeneralizedTime values MUST be
expressed Greenwich Mean Time (Zulu) and MUST include seconds (i.e.,
times are YYYYMMDDHHMMSSZ), even where the number of seconds is zero.
GeneralizedTime values MUST NOT include fractional seconds.
*)
function EncodeX509Time(const A: TX509Time): RawByteString;
var
Y, M, D : Word;
begin
DecodeDate(A, Y, M, D);
if Y >= 2050 then
Result := ASN1EncodeGeneralizedTime(A)
else
Result := ASN1EncodeUTCTime(A);
end;
function DecodeX509Time(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509Time): Integer;
var T : TDateTime;
begin
Result := ASN1DecodeTime(TypeID, Buf, Size, T);
A := T;
end;
(*
Validity ::= SEQUENCE {
notBefore Time,
notAfter Time }
*)
function EncodeX509Validity(const A: TX509Validity): RawByteString;
begin
Result :=
ASN1EncodeSequence(
EncodeX509Time(A.NotBefore) +
EncodeX509Time(A.NotAfter)
);
end;
procedure X509ValidityParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509Validity;
begin
D := Pointer(CallerData);
case ObjectIdx of
0 : ASN1DecodeTime(TypeID, DataBuf, DataSize, D^.NotBefore);
1 : ASN1DecodeTime(TypeID, DataBuf, DataSize, D^.NotAfter);
end;
if ObjectIdx >= 1 then
D^._Decoded := True;
end;
function DecodeX509Validity(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509Validity): Integer;
begin
Assert(TypeID = ASN1_ID_SEQUENCE);
A._Decoded := False;
Result := ASN1Parse(Buf, Size, X509ValidityParseProc, NativeInt(@A));
if not A._Decoded then
raise EX509.Create(SErr_DecodeError); // incomplete structure
end;
(*
Diffie-Hellman Key Exchange Key:
ValidationParms ::= SEQUENCE {
seed BIT STRING,
pgenCounter INTEGER }
*)
function EncodeX509DHValidationParms(const A: TX509DHValidationParms): RawByteString;
begin
Result :=
ASN1EncodeSequence(
ASN1EncodeBitString(A.Seed, 0) +
ASN1EncodeInteger32(A.PgenCounter)
);
end;
procedure X509DHValidationParmsParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509DHValidationParms;
K : Byte;
begin
D := Pointer(CallerData);
case ObjectIdx of
0 : ASN1DecodeBitString(TypeID, DataBuf, DataSize, D^.Seed, K);
1 : ASN1DecodeInteger32(TypeID, DataBuf, DataSize, D^.PgenCounter);
end;
end;
function DecodeX509DHValidationParms(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509DHValidationParms): Integer;
begin
Assert(TypeID = ASN1_ID_SEQUENCE);
Result := ASN1Parse(Buf, Size, X509DHValidationParmsParseProc, NativeInt(@A));
end;
(*
Diffie-Hellman Key Exchange Key:
DomainParameters ::= SEQUENCE {
p INTEGER, -- odd prime, p=jq +1
g INTEGER, -- generator, g
q INTEGER, -- factor of p-1
j INTEGER OPTIONAL, -- subgroup factor
validationParms ValidationParms OPTIONAL }
*)
function EncodeX509DHDomainParameters(const A: TX509DHDomainParameters): RawByteString;
var S : RawByteString;
begin
if (A.P = '') or (A.G = '') or (A.Q = '') then
raise EX509.Create(SErr_EncodeError);
S := ASN1EncodeIntegerBufStr(A.P) +
ASN1EncodeIntegerBufStr(A.G) +
ASN1EncodeIntegerBufStr(A.Q);
if A.J <> '' then
S := S + ASN1EncodeIntegerBufStr(A.J);
if A.ValidationParms.Seed <> '' then
S := S + EncodeX509DHValidationParms(A.ValidationParms);
Result := ASN1EncodeSequence(S);
end;
procedure X509DHDomainParametersParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509DHDomainParameters;
begin
D := Pointer(CallerData);
case ObjectIdx of
0 : ASN1DecodeIntegerBuf(TypeID, DataBuf, DataSize, D^.P);
1 : ASN1DecodeIntegerBuf(TypeID, DataBuf, DataSize, D^.G);
2 : ASN1DecodeIntegerBuf(TypeID, DataBuf, DataSize, D^.Q);
3 : ASN1DecodeIntegerBuf(TypeID, DataBuf, DataSize, D^.J);
4 : DecodeX509DHValidationParms(TypeID, DataBuf, DataSize, D^.ValidationParms);
end;
end;
function DecodeX509DHDomainParameters(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509DHDomainParameters): Integer;
begin
Assert(TypeID = ASN1_ID_SEQUENCE);
Result := ASN1Parse(Buf, Size, X509DHDomainParametersParseProc, NativeInt(@A));
end;
(*
Dss-Parms ::= SEQUENCE {
p INTEGER,
q INTEGER,
g INTEGER }
*)
function EncodeX509DSSParms(const A: TX509DSSParms): RawByteString;
begin
Result := ASN1EncodeSequence(
ASN1EncodeIntegerBufStr(A.P) +
ASN1EncodeIntegerBufStr(A.Q) +
ASN1EncodeIntegerBufStr(A.G)
);
end;
procedure X509DSSParmsParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509DSSParms;
begin
D := Pointer(CallerData);
case ObjectIdx of
0 : ASN1DecodeIntegerBuf(TypeID, DataBuf, DataSize, D^.P);
1 : ASN1DecodeIntegerBuf(TypeID, DataBuf, DataSize, D^.Q);
2 : ASN1DecodeIntegerBuf(TypeID, DataBuf, DataSize, D^.G);
end;
end;
function DecodeX509DSSParms(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509DSSParms): Integer;
begin
Assert(TypeID = ASN1_ID_SEQUENCE);
Result := ASN1Parse(Buf, Size, X509DSSParmsParseProc, NativeInt(@A));
end;
(*
Dss-Sig-Value ::= SEQUENCE {
r INTEGER,
s INTEGER }
*)
function EncodeX509DSSSigValue(const A: TX509DSSSigValue): RawByteString;
begin
Result := ASN1EncodeSequence(
ASN1EncodeIntegerBufStr(A.R) +
ASN1EncodeIntegerBufStr(A.S)
);
end;
procedure X509DSSSigValueParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509DSSSigValue;
begin
D := Pointer(CallerData);
case ObjectIdx of
0 : ASN1DecodeIntegerBuf(TypeID, DataBuf, DataSize, D^.R);
1 : ASN1DecodeIntegerBuf(TypeID, DataBuf, DataSize, D^.S);
end;
end;
function DecodeX509DSSSigValue(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509DSSSigValue): Integer;
begin
Assert(TypeID = ASN1_ID_SEQUENCE);
Result := ASN1Parse(Buf, Size, X509DSSSigValueParseProc, NativeInt(@A));
end;
(*
AlgorithmIdentifier ::= SEQUENCE {
algorithm OBJECT IDENTIFIER,
parameters ANY DEFINED BY algorithm OPTIONAL }
*)
procedure InitX509AlgorithmIdentifier(var A: TX509AlgorithmIdentifier; const Algorithm: array of Integer; const Parameters: RawByteString);
begin
ASN1OIDInit(A.Algorithm, Algorithm);
A.Parameters := Parameters;
end;
procedure InitX509AlgorithmIdentifierDSA_SHA1(var A: TX509AlgorithmIdentifier; const Parameters: RawByteString);
begin
InitX509AlgorithmIdentifier(A, OID_DSA_SHA1, Parameters);
end;
function EncodeX509AlgorithmIdentifier(const A: TX509AlgorithmIdentifier): RawByteString;
var S : RawByteString;
begin
S := ASN1EncodeOID(A.Algorithm);
if A.Parameters <> '' then
S := S + A.Parameters;
Result := ASN1EncodeSequence(S);
end;
procedure X509AlgorithmIdentifierParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509AlgorithmIdentifier;
begin
D := Pointer(CallerData);
case ObjectIdx of
0 : ASN1DecodeOID(TypeID, DataBuf, DataSize, D^.Algorithm);
1 : ASN1DecodeDataRawByteString(DataBuf, DataSize, D^.Parameters);
end;
if ObjectIdx >= 0 then
D^._Decoded := True;
end;
function DecodeX509AlgorithmIdentifier(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509AlgorithmIdentifier): Integer;
begin
Assert(TypeID = ASN1_ID_SEQUENCE);
A._Decoded := False;
Result := ASN1Parse(Buf, Size, X509AlgorithmIdentifierParseProc, NativeInt(@A));
if not A._Decoded then
raise EX509.Create(SErr_DecodeError); // incomplete structure
end;
(*
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e -- }
*)
function EncodeX509RSAPublicKey(const A: TX509RSAPublicKey): RawByteString;
begin
Result :=
ASN1EncodeSequence(
ASN1EncodeIntegerBufStr(A.Modulus) +
ASN1EncodeIntegerBufStr(A.PublicExponent)
);
end;
procedure X509RSAPublicKeyParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509RSAPublicKey;
begin
D := Pointer(CallerData);
case ObjectIdx of
0 : ASN1DecodeIntegerBuf(TypeID, DataBuf, DataSize, D^.Modulus);
1 : ASN1DecodeIntegerBuf(TypeID, DataBuf, DataSize, D^.PublicExponent);
end;
end;
function DecodeX509RSAPublicKey(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509RSAPublicKey): Integer;
begin
Assert(TypeID = ASN1_ID_SEQUENCE);
Result := ASN1Parse(Buf, Size, X509RSAPublicKeyParseProc, NativeInt(@A));
end;
procedure X509RSAPublicKeyTopParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509RSAPublicKey;
begin
D := Pointer(CallerData);
DecodeX509RSAPublicKey(TypeID, DataBuf, DataSize, D^);
end;
function ParseX509RSAPublicKey(const Buf; const Size: Integer; var A: TX509RSAPublicKey): Integer;
begin
Result := ASN1Parse(Buf, Size, X509RSAPublicKeyTopParseProc, NativeInt(@A));
end;
(*
DHPublicKey ::= INTEGER -- public key, y = g^x mod p
*)
function EncodeX509DHPublicKey(const A: TX509DHPublicKey): RawByteString;
begin
Result := ASN1EncodeIntegerBufStr(A);
end;
(*
DSAPublicKey ::= INTEGER -- public key, Y
*)
function EncodeX509DSAPublicKey(const A: TX509DSAPublicKey): RawByteString;
begin
Result := ASN1EncodeIntegerBufStr(A);
end;
(*
SubjectPublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
subjectPublicKey BIT STRING }
*)
procedure InitX509SubjectPublicKeyInfoDSA(var A: TX509SubjectPublicKeyInfo; const B: TX509DSSParms; const PublicKey: RawByteString);
begin
ASN1OIDInit(A.Algorithm.Algorithm, OID_DSA);
A.Algorithm.Parameters := EncodeX509DSSParms(B);
A.SubjectPublicKey := PublicKey;
end;
function EncodeX509SubjectPublicKeyInfo(const A: TX509SubjectPublicKeyInfo): RawByteString;
begin
Result :=
ASN1EncodeSequence(
EncodeX509AlgorithmIdentifier(A.Algorithm) +
ASN1EncodeBitString(A.SubjectPublicKey, 0)
);
end;
procedure X509SubjectPublicKeyInfoParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509SubjectPublicKeyInfo;
begin
D := Pointer(CallerData);
case ObjectIdx of
0 : DecodeX509AlgorithmIdentifier(TypeID, DataBuf, DataSize, D^.Algorithm);
1 : ASN1DecodeString(TypeID, DataBuf, DataSize, D^.SubjectPublicKey);
end;
if ObjectIdx >= 1 then
D^._Decoded := True;
end;
function DecodeX509SubjectPublicKeyInfo(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509SubjectPublicKeyInfo): Integer;
begin
Assert(TypeID = ASN1_ID_SEQUENCE);
A._Decoded := False;
Result := ASN1Parse(Buf, Size, X509SubjectPublicKeyInfoParseProc, NativeInt(@A));
if not A._Decoded then
raise EX509.Create(SErr_DecodeError); // incomplete structure
end;
procedure X509SubjectPublicKeyInfoTopParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509SubjectPublicKeyInfo;
begin
D := Pointer(CallerData);
case ObjectIdx of
0 : DecodeX509SubjectPublicKeyInfo(TypeID, DataBuf, DataSize, D^);
end;
end;
function ParseX509SubjectPublicKeyInfo(const Buf; const Size: Integer; var A: TX509SubjectPublicKeyInfo): Integer;
var P : Pointer;
begin
P := @Buf;
if not Assigned(P) or (Size <= 0) then
raise EX509.Create(SErr_InvalidBuffer);
Result := ASN1Parse(Buf, Size, X509SubjectPublicKeyInfoTopParseProc, NativeInt(@A));
if Result <> Size then
raise EX509.Create(SErr_DecodeError); // incomplete parsing
end;
(*
BasicConstraints ::= SEQUENCE {
cA BOOLEAN DEFAULT FALSE,
pathLenConstraint INTEGER (0..MAX) OPTIONAL }
*)
function EncodeX509BasicConstraints(const A: TX509BasicConstraints): RawByteString;
var S : RawByteString;
begin
S := ASN1EncodeBoolean(A.CA);
if A.PathLenConstraint <> '' then
S := S + ASN1EncodeIntegerBufStr(A.PathLenConstraint);
Result := ASN1EncodeSequence(S);
end;
procedure X509BasicConstraintsParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509BasicConstraints;
begin
D := Pointer(CallerData);
if (ObjectIdx = 0) and (TypeID = ASN1_ID_BOOLEAN) then
begin
ASN1DecodeBoolean(TypeID, DataBuf, DataSize, D^.CA);
D^._DecodedCA := True;
end
else
if ((ObjectIdx = 0) and (TypeID = ASN1_ID_INTEGER)) or
((ObjectIdx = 1) and (TypeID = ASN1_ID_INTEGER) and D^._DecodedCA) then
ASN1DecodeDataIntegerBuf(DataBuf, DataSize, D^.PathLenConstraint);
end;
function DecodeX509BasicConstraints(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509BasicConstraints): Integer;
begin
Assert(TypeID = ASN1_ID_SEQUENCE);
A.CA := False;
A._DecodedCA := False;
Result := ASN1Parse(Buf, Size, X509BasicConstraintsParseProc, NativeInt(@A));
end;
(*
GeneralName ::= CHOICE {
otherName [0] OtherName,
rfc822Name [1] IA5String,
dNSName [2] IA5String,
x400Address [3] ORAddress,
directoryName [4] Name,
ediPartyName [5] EDIPartyName,
uniformResourceIdentifier [6] IA5String,
iPAddress [7] OCTET STRING,
registeredID [8] OBJECT IDENTIFIER }
*)
function EncodeX509GeneralName(const A: TX509GeneralNameType; const EncodedName: RawByteString): RawByteString;
begin
Result := ASN1EncodeContextSpecific(Ord(A), EncodedName);
end;
function DecodeX509GeneralName(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509GeneralNameType; var B: RawByteString): Integer;
var I : Integer;
begin
if not ASN1TypeIsContextSpecific(TypeID, I) then
raise EX509.Create(SErr_DecodeError);
A := TX509GeneralNameType(I);
Result := ASN1DecodeString(TypeID, Buf, Size, B);
end;
(*
GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
*)
function EncodeX509GeneralNames(const A: TX509GeneralNames): RawByteString;
var I : Integer;
S : RawByteString;
begin
S := '';
for I := 0 to Length(A) - 1 do
S := S + EncodeX509GeneralName(A[I].NameType, A[I].Name);
Result := ASN1EncodeSequence(S);
end;
procedure X509GeneralNamesParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509GeneralNames;
L : Integer;
begin
D := Pointer(CallerData);
L := Length(D^);
SetLength(D^, L + 1);
DecodeX509GeneralName(TypeID, DataBuf, DataSize, D^[L].NameType, D^[L].Name);
end;
function DecodeX509GeneralNames(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509GeneralNames): Integer;
begin
Assert(TypeID = ASN1_ID_SEQUENCE);
Result := ASN1Parse(Buf, Size, X509GeneralNamesParseProc, NativeInt(@A));
end;
(*
AuthorityKeyIdentifier ::= SEQUENCE {
keyIdentifier [0] KeyIdentifier OPTIONAL,
authorityCertIssuer [1] GeneralNames OPTIONAL,
authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
*)
function EncodeX509AuthorityKeyIdentifier(const A: TX509AuthorityKeyIdentifier): RawByteString;
var S : RawByteString;
begin
S := '';
if A.KeyIdentifier <> '' then
S := S + ASN1EncodeContextSpecific(0, ASN1EncodeBitString(A.KeyIdentifier, 0));
if Length(A.AuthorityCertIssuer) > 0 then
S := S + ASN1EncodeContextSpecific(1, EncodeX509GeneralNames(A.AuthorityCertIssuer));
S := S + ASN1EncodeContextSpecific(2, ASN1EncodeInteger64(A.AuthorityCertSerialNumber));
Result := ASN1EncodeSequence(S);
end;
(*
KeyUsage ::= BIT STRING {
digitalSignature (0),
nonRepudiation (1),
keyEncipherment (2),
dataEncipherment (3),
keyAgreement (4),
keyCertSign (5),
cRLSign (6),
encipherOnly (7),
decipherOnly (8) }
*)
(*
KeyIdentifier ::= OCTET STRING
SubjectKeyIdentifier ::= KeyIdentifier
PrivateKeyUsagePeriod ::= SEQUENCE {
notBefore [0] GeneralizedTime OPTIONAL,
notAfter [1] GeneralizedTime OPTIONAL }
SubjectAltName ::= GeneralNames
OtherName ::= SEQUENCE {
type-id OBJECT IDENTIFIER,
value [0] EXPLICIT ANY DEFINED BY type-id }
EDIPartyName ::= SEQUENCE {
nameAssigner [0] DirectoryString OPTIONAL,
partyName [1] DirectoryString }
IssuerAltName ::= GeneralNames
NameConstraints ::= SEQUENCE {
permittedSubtrees [0] GeneralSubtrees OPTIONAL,
excludedSubtrees [1] GeneralSubtrees OPTIONAL }
GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
GeneralSubtree ::= SEQUENCE {
base GeneralName,
minimum [0] BaseDistance DEFAULT 0,
maximum [1] BaseDistance OPTIONAL }
BaseDistance ::= INTEGER (0..MAX)
ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
KeyPurposeId ::= OBJECT IDENTIFIER
*)
procedure InitX509ExtAuthorityKeyIdentifier(var A: TX509Extension; const B: TX509AuthorityKeyIdentifier);
begin
ASN1OIDInit(A.ExtnID, OID_CE_AuthorityKeyIdentifier);
A.Critical := False;
A.ExtnValue := EncodeX509AuthorityKeyIdentifier(B);
end;
procedure InitX509ExtSubjectKeyIdentifier(var A: TX509Extension; const B: TX509SubjectKeyIdentifier);
begin
ASN1OIDInit(A.ExtnID, OID_CE_SubjectKeyIdentifier);
A.Critical := False;
A.ExtnValue := B;
end;
procedure InitX509ExtBasicConstraints(var A: TX509Extension; const B: TX509BasicConstraints);
begin
ASN1OIDInit(A.ExtnID, OID_CE_BasicConstraints);
A.Critical := True;
A.ExtnValue := EncodeX509BasicConstraints(B);
end;
(*
Extension ::= SEQUENCE {
extnID OBJECT IDENTIFIER,
critical BOOLEAN DEFAULT FALSE,
extnValue OCTET STRING
}
*)
function EncodeX509Extension(const A: TX509Extension): RawByteString;
var S : RawByteString;
begin
S := ASN1EncodeOID(A.ExtnID);
if A.Critical then
S := S + ASN1EncodeBoolean(A.Critical);
S := S + ASN1EncodeOctetString(A.ExtnValue);
Result := ASN1EncodeSequence(S);
end;
procedure X509ExtensionParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509Extension;
begin
D := Pointer(CallerData);
if ObjectIdx = 0 then
ASN1DecodeOID(TypeID, DataBuf, DataSize, D^.ExtnID)
else
if (ObjectIdx = 1) and (TypeID = ASN1_ID_BOOLEAN) then
begin
ASN1DecodeBoolean(TypeID, DataBuf, DataSize, D^.Critical);
D^._DecodedCritical := True;
end
else
if ((ObjectIdx = 1) and (TypeID = ASN1_ID_OCTET_STRING)) or
((ObjectIdx = 2) and (TypeID = ASN1_ID_OCTET_STRING) and D^._DecodedCritical) then
begin
ASN1DecodeString(TypeID, DataBuf, DataSize, D^.ExtnValue);
D^._Decoded := True;
end;
end;
function DecodeX509Extension(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509Extension): Integer;
begin
Assert(TypeID = ASN1_ID_SEQUENCE);
A.Critical := False;
A._DecodedCritical := False;
A._Decoded := False;
Result := ASN1Parse(Buf, Size, X509ExtensionParseProc, NativeInt(@A));
if not A._Decoded then
raise EX509.Create(SErr_DecodeError); // incomplete structure
end;
(*
Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
*)
procedure AppendX509Extensions(var A: TX509Extensions; const B: TX509Extension);
var L : Integer;
begin
L := Length(A);
SetLength(A, L + 1);
A[L] := B;
end;
function EncodeX509Extensions(const A: TX509Extensions): RawByteString;
var I : Integer;
S : RawByteString;
begin
S := '';
for I := 0 to Length(A) - 1 do
S := S + EncodeX509Extension(A[I]);
Result := ASN1EncodeSequence(S);
end;
procedure X509ExtensionsParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509Extensions;
L : Integer;
begin
D := Pointer(CallerData);
L := Length(D^);
SetLength(D^, L + 1);
DecodeX509Extension(TypeID, DataBuf, DataSize, D^[L]);
end;
function DecodeX509Extensions(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509Extensions): Integer;
begin
Assert(TypeID = ASN1_ID_SEQUENCE);
Result := ASN1Parse(Buf, Size, X509ExtensionsParseProc, NativeInt(@A));
end;
{ Key }
function NormaliseX509IntKeyBuf(var KeyBuf: RawByteString): Integer;
var L : Integer;
begin
L := Length(KeyBuf);
// some integer key values have an extra #0 padding prefixed.
// remove the extra character if found.
if (L mod 8 = 1) and (KeyBuf[1] = #0) then
begin
Delete(KeyBuf, 1, 1);
Dec(L);
end;
Result := L;
end;
(*
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,
-- If present, version shall be v2 or v3
subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
-- If present, version shall be v2 or v3
extensions [3] EXPLICIT Extensions OPTIONAL
-- If present, version shall be v3
}
CertificateSerialNumber ::= INTEGER
Certificate users MUST be able to handle serialNumber values up to 20 octets.
UniqueIdentifier ::= BIT STRING
*)
function EncodeX509TBSCertificate(const A: TX509TBSCertificate): RawByteString;
var S : RawByteString;
begin
if A.Version = X509vUndefined then
raise EX509.Create(SErr_EncodeError); // version not defined
if A.SerialNumber = '' then
raise EX509.Create(SErr_EncodeError); // serial number not defined
S := '';
if A.Version <> X509v1 then
S := S + ASN1EncodeContextSpecific(0, EncodeX509Version(A.Version));
S := S +
ASN1EncodeIntegerBufStr(A.SerialNumber) +
EncodeX509AlgorithmIdentifier(A.Signature) +
EncodeX509Name(A.Issuer) +
EncodeX509Validity(A.Validity) +
EncodeX509Name(A.Subject) +
EncodeX509SubjectPublicKeyInfo(A.SubjectPublicKeyInfo);
if A.IssuerUniqueID <> '' then
S := S +
ASN1EncodeContextSpecific(1, ASN1EncodeBitString(A.IssuerUniqueID, 0));
if A.SubjectUniqueID <> '' then
S := S +
ASN1EncodeContextSpecific(2, ASN1EncodeBitString(A.SubjectUniqueID, 0));
if Length(A.Extensions) > 0 then
S := S +
ASN1EncodeContextSpecific(3, EncodeX509Extensions(A.Extensions));
Result :=
ASN1EncodeSequence(S);
end;
procedure X509TBSCertificateCS0ParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509TBSCertificate;
begin
D := Pointer(CallerData);
DecodeX509Version(TypeID, DataBuf, DataSize, D^.Version);
D^._DecodedVersion := True;
end;
procedure X509TBSCertificateCS1ParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509TBSCertificate;
begin
D := Pointer(CallerData);
ASN1DecodeString(TypeID, DataBuf, DataSize, D^.IssuerUniqueID);
if D^.Version in [X509vUndefined, X509v1] then
raise EX509.Create(SErr_DecodeError); // invalid version specified
end;
procedure X509TBSCertificateCS2ParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509TBSCertificate;
begin
D := Pointer(CallerData);
ASN1DecodeString(TypeID, DataBuf, DataSize, D^.SubjectUniqueID);
if D^.Version in [X509vUndefined, X509v1] then
raise EX509.Create(SErr_DecodeError); // invalid version specified
end;
procedure X509TBSCertificateCS3ParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509TBSCertificate;
begin
D := Pointer(CallerData);
DecodeX509Extensions(TypeID, DataBuf, DataSize, D^.Extensions);
if D^.Version in [X509vUndefined, X509v1, X509v2] then
raise EX509.Create(SErr_DecodeError); // invalid version specified
end;
procedure X509TBSCertificateParseProc(
const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509TBSCertificate;
I : Integer;
begin
D := Pointer(CallerData);
if ASN1TypeIsContextSpecific(TypeID, I) then
case I of
0 : ASN1Parse(DataBuf, DataSize, X509TBSCertificateCS0ParseProc, CallerData);
1 : ASN1Parse(DataBuf, DataSize, X509TBSCertificateCS1ParseProc, CallerData);
2 : ASN1Parse(DataBuf, DataSize, X509TBSCertificateCS2ParseProc, CallerData);
3 : ASN1Parse(DataBuf, DataSize, X509TBSCertificateCS3ParseProc, CallerData);
end
else
begin
if D^._DecodedVersion then
I := 0
else
I := 1;
case ObjectIdx + I of
1 : ASN1DecodeIntegerBuf(TypeID, DataBuf, DataSize, D^.SerialNumber);
2 : DecodeX509AlgorithmIdentifier(TypeID, DataBuf, DataSize, D^.Signature);
3 : DecodeX509Name(TypeID, DataBuf, DataSize, D^.Issuer);
4 : DecodeX509Validity(TypeID, DataBuf, DataSize, D^.Validity);
5 : DecodeX509Name(TypeID, DataBuf, DataSize, D^.Subject);
6 : begin
DecodeX509SubjectPublicKeyInfo(TypeID, DataBuf, DataSize, D^.SubjectPublicKeyInfo);
D^._Decoded := True;
end;
end;
end;
end;
function DecodeX509TBSCertificate(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509TBSCertificate): Integer;
begin
Assert(TypeID = ASN1_ID_SEQUENCE);
A.Version := X509vUndefined;
A._DecodedVersion := False;
A._Decoded := False;
Result := ASN1Parse(Buf, Size, X509TBSCertificateParseProc, NativeInt(@A));
if not A._Decoded then
raise EX509.Create(SErr_DecodeError); // incomplete structure
if not A._DecodedVersion then
A.Version := X509v1;
end;
(*
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signature BIT STRING }
Signature is based on ASN.1 DER encoded tbsCertificate
*)
procedure InitX509Certificate(var A: TX509Certificate);
begin
FillChar(A, SizeOf(A), 0);
end;
function EncodeX509Certificate(const A: TX509Certificate): RawByteString;
begin
Result := ASN1EncodeSequence(
EncodeX509TBSCertificate(A.TBSCertificate) +
EncodeX509AlgorithmIdentifier(A.SignatureAlgorithm) +
ASN1EncodeBitString(A.SignatureValue, 0)
);
end;
procedure X509CertificateParseProc(const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509Certificate;
K : Byte;
begin
D := Pointer(CallerData);
case ObjectIdx of
0 : DecodeX509TBSCertificate(TypeID, DataBuf, DataSize, D^.TBSCertificate);
1 : DecodeX509AlgorithmIdentifier(TypeID, DataBuf, DataSize, D^.SignatureAlgorithm);
2 : ASN1DecodeBitString(TypeID, DataBuf, DataSize, D^.SignatureValue, K);
end;
if ObjectIdx >= 2 then
D^._Decoded := True;
end;
function DecodeX509Certificate(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509Certificate): Integer;
begin
A._Decoded := False;
Result := ASN1Parse(Buf, Size, X509CertificateParseProc, NativeInt(@A));
if not A._Decoded then
raise EX509.Create(SErr_DecodeError); // incomplete structure
if not ASN1OIDEqual(A.SignatureAlgorithm.Algorithm, A.TBSCertificate.Signature.Algorithm) then
raise EX509.Create(SErr_DecodeError); // signature algorithm specifications not the same
end;
procedure X509CertificateTopParseProc(const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509Certificate;
begin
D := Pointer(CallerData);
case ObjectIdx of
0 : DecodeX509Certificate(TypeID, DataBuf, DataSize, D^);
end;
end;
function ParseX509Certificate(const Buf; const Size: Integer; var A: TX509Certificate): Integer;
var P : Pointer;
begin
P := @Buf;
if not Assigned(P) or (Size <= 0) then
raise EX509.Create(SErr_InvalidBuffer);
Result := ASN1Parse(Buf, Size, X509CertificateTopParseProc, NativeInt(@A));
if Result <> Size then
raise EX509.Create(SErr_DecodeError); // incomplete parsing
end;
procedure ParseX509CertificateStr(const BufStr: RawByteString; var A: TX509Certificate);
var L : Integer;
begin
L := Length(BufStr);
if L = 0 then
raise EX509.Create(SErr_InvalidBuffer);
ParseX509Certificate(BufStr[1], L, A);
end;
procedure ParseX509CertificatePEM(const BufStr: RawByteString; var A: TX509Certificate);
var S : RawByteString;
begin
S := MIMEBase64Decode(BufStr);
ParseX509CertificateStr(S, A);
end;
(*
RSAPrivateKey ::= SEQUENCE {
version Version, -- a INTEGER version number; 0 for this standard
modulus INTEGER, -- n
publicExponent INTEGER, -- e
privateExponent INTEGER, -- d
prime1 INTEGER, -- primeP (p) (first prime factor of n)
prime2 INTEGER, -- primeQ (q) (second prime factor of n)
exponent1 INTEGER, -- primeExponentP: d mod (p - 1)
exponent2 INTEGER, -- primeExponentQ: d mod (q - 1)
crtCoefficient INTEGER -- Chinese Remainder Theorem ((inverse of q) mod p) }
*)
function EncodeX509RSAPrivateKey(const A: TX509RSAPrivateKey): RawByteString;
begin
Result := ASN1EncodeSequence(
ASN1EncodeInteger32(A.Version) +
ASN1EncodeIntegerBufStr(A.Modulus) +
ASN1EncodeIntegerBufStr(A.PublicExponent) +
ASN1EncodeIntegerBufStr(A.PrivateExponent) +
ASN1EncodeIntegerBufStr(A.Prime1) +
ASN1EncodeIntegerBufStr(A.Prime2) +
ASN1EncodeIntegerBufStr(A.Exponent1) +
ASN1EncodeIntegerBufStr(A.Exponent2) +
ASN1EncodeIntegerBufStr(A.CRTCoefficient)
);
end;
procedure X509RSAPrivateKeyParseProc(const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509RSAPrivateKey;
begin
D := Pointer(CallerData);
case ObjectIdx of
0 : ASN1DecodeInteger32(TypeID, DataBuf, DataSize, D^.Version);
1 : ASN1DecodeIntegerBuf(TypeID, DataBuf, DataSize, D^.Modulus);
2 : ASN1DecodeIntegerBuf(TypeID, DataBuf, DataSize, D^.PublicExponent);
3 : ASN1DecodeIntegerBuf(TypeID, DataBuf, DataSize, D^.PrivateExponent);
4 : ASN1DecodeIntegerBuf(TypeID, DataBuf, DataSize, D^.Prime1);
5 : ASN1DecodeIntegerBuf(TypeID, DataBuf, DataSize, D^.Prime2);
6 : ASN1DecodeIntegerBuf(TypeID, DataBuf, DataSize, D^.Exponent1);
7 : ASN1DecodeIntegerBuf(TypeID, DataBuf, DataSize, D^.Exponent2);
8 : begin
ASN1DecodeIntegerBuf(TypeID, DataBuf, DataSize, D^.CRTCoefficient);
D^._Decoded := True;
end;
end;
end;
function DecodeX509RSAPrivateKey(const TypeID: Byte; const Buf; const Size: Integer; var A: TX509RSAPrivateKey): Integer;
begin
A._Decoded := False;
Result := ASN1Parse(Buf, Size, X509RSAPrivateKeyParseProc, NativeInt(@A));
if not A._Decoded then
raise EX509.Create(SErr_DecodeError); // incomplete structure
end;
procedure X509RSAPrivateKeyTopParseProc(const TypeID: Byte; const DataBuf; const DataSize: Integer;
const ObjectIdx: Integer; const CallerData: NativeInt);
var D : PX509RSAPrivateKey;
begin
D := Pointer(CallerData);
case ObjectIdx of
0 : DecodeX509RSAPrivateKey(TypeID, DataBuf, DataSize, D^);
end;
end;
function ParseX509RSAPrivateKey(const Buf; const Size: Integer; var A: TX509RSAPrivateKey): Integer;
var P : Pointer;
begin
P := @Buf;
if not Assigned(P) or (Size <= 0) then
raise EX509.Create(SErr_InvalidBuffer);
Result := ASN1Parse(Buf, Size, X509RSAPrivateKeyTopParseProc, NativeInt(@A));
if Result <> Size then
raise EX509.Create(SErr_DecodeError); // incomplete parsing
end;
procedure ParseX509RSAPrivateKeyStr(const BufStr: RawByteString; var A: TX509RSAPrivateKey);
var L : Integer;
begin
L := Length(BufStr);
if L = 0 then
raise EX509.Create(SErr_InvalidBuffer);
ParseX509RSAPrivateKey(BufStr[1], L, A);
end;
procedure ParseX509RSAPrivateKeyPEM(const BufStr: RawByteString; var A: TX509RSAPrivateKey);
var S : RawByteString;
begin
S := MIMEBase64Decode(BufStr);
ParseX509RSAPrivateKeyStr(S, A);
end;
{ }
{ Test }
{ }
{$IFDEF X509_TEST}
{$ASSERTIONS ON}
procedure TestRFC2459;
var A, B : TX509Certificate;
C : RawByteString;
F : TX509AttributeTypeAndValue;
N : TX509RelativeDistinguishedName;
M : TX509RDNSequence;
EXT : TX509Extension;
EXTS : TX509Extensions;
EBC : TX509BasicConstraints;
ESK : TX509SubjectKeyIdentifier;
PKD : TX509DSSParms;
L : Integer;
begin
InitX509Certificate(A);
InitX509Certificate(B);
// //
// Example D.1 from RFC 2459 //
// //
A.TBSCertificate.Version := X509v3;
A.TBSCertificate.SerialNumber := #17;
InitX509AlgorithmIdentifierDSA_SHA1(A.TBSCertificate.Signature, '');
A.TBSCertificate.Validity.NotBefore := EncodeDate(1997, 6, 30);
A.TBSCertificate.Validity.NotAfter := EncodeDate(1997, 12, 31);
PKD.P := RawByteString(
#$d4#$38#$02#$c5#$35#$7b#$d5#$0b#$a1#$7e#$5d#$72#$59#$63#$55#$d3 +
#$45#$56#$ea#$e2#$25#$1a#$6b#$c5#$a4#$ab#$aa#$0b#$d4#$62#$b4#$d2 +
#$21#$b1#$95#$a2#$c6#$01#$c9#$c3#$fa#$01#$6f#$79#$86#$83#$3d#$03 +
#$61#$e1#$f1#$92#$ac#$bc#$03#$4e#$89#$a3#$c9#$53#$4a#$f7#$e2#$a6 +
#$48#$cf#$42#$1e#$21#$b1#$5c#$2b#$3a#$7f#$ba#$be#$6b#$5a#$f7#$0a +
#$26#$d8#$8e#$1b#$eb#$ec#$bf#$1e#$5a#$3f#$45#$c0#$bd#$31#$23#$be +
#$69#$71#$a7#$c2#$90#$fe#$a5#$d6#$80#$b5#$24#$dc#$44#$9c#$eb#$4d +
#$f9#$da#$f0#$c8#$e8#$a2#$4c#$99#$07#$5c#$8e#$35#$2b#$7d#$57#$8d);
PKD.Q := RawByteString(
#$a7#$83#$9b#$f3#$bd#$2c#$20#$07#$fc#$4c#$e7#$e8#$9f#$f3#$39#$83 +
#$51#$0d#$dc#$dd);
PKD.G := RawByteString(
#$0e#$3b#$46#$31#$8a#$0a#$58#$86#$40#$84#$e3#$a1#$22#$0d#$88#$ca +
#$90#$88#$57#$64#$9f#$01#$21#$e0#$15#$05#$94#$24#$82#$e2#$10#$90 +
#$d9#$e1#$4e#$10#$5c#$e7#$54#$6b#$d4#$0c#$2b#$1b#$59#$0a#$a0#$b5 +
#$a1#$7d#$b5#$07#$e3#$65#$7c#$ea#$90#$d8#$8e#$30#$42#$e4#$85#$bb +
#$ac#$fa#$4e#$76#$4b#$78#$0e#$df#$6c#$e5#$a6#$e1#$bd#$59#$77#$7d +
#$a6#$97#$59#$c5#$29#$a7#$b3#$3f#$95#$3e#$9d#$f1#$59#$2d#$f7#$42 +
#$87#$62#$3f#$f1#$b8#$6f#$c7#$3d#$4b#$b8#$8d#$74#$c4#$ca#$44#$90 +
#$cf#$67#$db#$de#$14#$60#$97#$4a#$d1#$f7#$6d#$9e#$09#$94#$c4#$0d);
InitX509SubjectPublicKeyInfoDSA(A.TBSCertificate.SubjectPublicKeyInfo, PKD,
RawByteString(
#$02#$81#$80#$aa#$98#$ea#$13#$94#$a2#$db#$f1#$5b#$7f#$98#$2f#$78 +
#$e7#$d8#$e3#$b9#$71#$86#$f6#$80#$2f#$40#$39#$c3#$da#$3b#$4b#$13 +
#$46#$26#$ee#$0d#$56#$c5#$a3#$3a#$39#$b7#$7d#$33#$c2#$6b#$5c#$77 +
#$92#$f2#$55#$65#$90#$39#$cd#$1a#$3c#$86#$e1#$32#$eb#$25#$bc#$91 +
#$c4#$ff#$80#$4f#$36#$61#$bd#$cc#$e2#$61#$04#$e0#$7e#$60#$13#$ca +
#$c0#$9c#$dd#$e0#$ea#$41#$de#$33#$c1#$f1#$44#$a9#$bc#$71#$de#$cf +
#$59#$d4#$6e#$da#$44#$99#$3c#$21#$64#$e4#$78#$54#$9d#$d0#$7b#$ba +
#$4e#$f5#$18#$4d#$5e#$39#$30#$bf#$e0#$d1#$f6#$f4#$83#$25#$4f#$14 +
#$aa#$71#$e1));
M := nil;
N := nil;
InitX509AtCountryName(F, 'US');
AppendX509RelativeDistinguishedName(N, F);
AppendX509RDNSequence(M, N);
N := nil;
InitX509AtOriganizationName(F, 'gov');
AppendX509RelativeDistinguishedName(N, F);
AppendX509RDNSequence(M, N);
N := nil;
InitX509AtOriganizationUnitName(F, 'nist');
AppendX509RelativeDistinguishedName(N, F);
AppendX509RDNSequence(M, N);
A.TBSCertificate.Issuer := M;
M := nil;
N := nil;
InitX509AtCountryName(F, 'US');
AppendX509RelativeDistinguishedName(N, F);
AppendX509RDNSequence(M, N);
N := nil;
InitX509AtOriganizationName(F, 'gov');
AppendX509RelativeDistinguishedName(N, F);
AppendX509RDNSequence(M, N);
N := nil;
InitX509AtOriganizationUnitName(F, 'nist');
AppendX509RelativeDistinguishedName(N, F);
AppendX509RDNSequence(M, N);
A.TBSCertificate.Subject := M;
EXTS := nil;
EBC.CA := True;
EBC.PathLenConstraint := '';
InitX509ExtBasicConstraints(EXT, EBC);
AppendX509Extensions(EXTS, EXT);
ESK := RawByteString(
#$04#$14#$e7#$26#$c5#$54#$cd#$5b#$a3#$6f#$35#$68#$95#$aa#$d5#$ff#$1c#$21#$e4#$22#$75#$d6);
InitX509ExtSubjectKeyIdentifier(EXT, ESK);
AppendX509Extensions(EXTS, EXT);
A.TBSCertificate.Extensions := EXTS;
InitX509AlgorithmIdentifierDSA_SHA1(A.SignatureAlgorithm, '');
A.SignatureValue := RawByteString(
#$30#$2c#$02#$14#$a0#$66#$c1#$76#$33#$99#$13#$51#$8d#$93#$64#$2f +
#$ca#$13#$73#$de#$79#$1a#$7d#$33#$02#$14#$5d#$90#$f6#$ce#$92#$4a +
#$bf#$29#$11#$24#$80#$28#$a6#$5a#$8e#$73#$b6#$76#$02#$68);
C := EncodeX509Certificate(A);
Assert(C =
#$30#$82#$02#$b7 + // 695: SEQUENCE
#$30#$82#$02#$77 + // 631: . SEQUENCE tbscertificate
#$a0#$03 + // 3: . . [0]
#$02#$01 + // 1: . . . INTEGER 2
#$02 +
#$02#$01 + // 1: . . INTEGER 17
#$11 +
#$30#$09 + // 9: . . SEQUENCE
#$06#$07 + // 7: . . . OID 1.2.840.10040.4.3: dsa-with-sha
#$2a#$86#$48#$ce#$38#$04#$03 +
#$30#$2a + // 42: . . SEQUENCE
#$31#$0b + // 11: . . . SET
#$30#$09 + // 9: . . . . SEQUENCE
#$06#$03 + // 3: . . . . . OID 2.5.4.6: C
#$55#$04#$06 +
#$13#$02 + // 2: . . . . . PrintableString 'US'
#$55#$53 +
#$31#$0c + // 12: . . . SET
#$30#$0a + // 10: . . . . SEQUENCE
#$06#$03 + // 3: . . . . . OID 2.5.4.10: O
#$55#$04#$0a +
#$13#$03 + // 3: . . . . . PrintableString 'gov'
#$67#$6f#$76 +
#$31#$0d + // 13: . . . SET
#$30#$0b + // 11: . . . . SEQUENCE
#$06#$03 + // 3: . . . . . OID 2.5.4.11: OU
#$55#$04#$0b +
#$13#$04 + // 4: . . . . . PrintableString 'nist'
#$6e#$69#$73#$74 +
#$30#$1e + // 30: . . SEQUENCE
#$17#$0d + // 13: . . . UTCTime '970630000000Z'
#$39#$37#$30#$36#$33#$30#$30#$30#$30#$30#$30#$30#$5a +
#$17#$0d + // 13: . . . UTCTime '971231000000Z'
#$39#$37#$31#$32#$33#$31#$30#$30#$30#$30#$30#$30#$5a +
#$30#$2a + // 42: . . SEQUENCE
#$31#$0b + // 11: . . . SET
#$30#$09 + // 9: . . . . SEQUENCE
#$06#$03 + // 3: . . . . . OID 2.5.4.6: C
#$55#$04#$06 +
#$13#$02 + // 2: . . . . . PrintableString 'US'
#$55#$53 +
#$31#$0c + // 12: . . . SET
#$30#$0a + // 10: . . . . SEQUENCE
#$06#$03 + // 3: . . . . . OID 2.5.4.10: O
#$55#$04#$0a +
#$13#$03 + // 3: . . . . . PrintableString 'gov'
#$67#$6f#$76 +
#$31#$0d + // 13: . . . SET
#$30#$0b + // 11: . . . . SEQUENCE
#$06#$03 + // 3: . . . . . OID 2.5.4.11: OU
#$55#$04#$0b +
#$13#$04 + // 4: . . . . . PrintableString 'nist'
#$6e#$69#$73#$74 +
#$30#$82#$01#$b4 + // 436: . . SEQUENCE
#$30#$82#$01#$29 + // 297: . . . SEQUENCE
#$06#$07 + // 7: . . . . OID 1.2.840.10040.4.1: dsa
#$2a#$86#$48#$ce#$38#$04#$01 +
#$30#$82#$01#$1c + // 284: . . . . SEQUENCE
#$02#$81#$80 + // 128: . . . . . INTEGER
#$d4#$38#$02#$c5#$35#$7b#$d5#$0b#$a1#$7e#$5d#$72#$59#$63#$55#$d3 +
#$45#$56#$ea#$e2#$25#$1a#$6b#$c5#$a4#$ab#$aa#$0b#$d4#$62#$b4#$d2 +
#$21#$b1#$95#$a2#$c6#$01#$c9#$c3#$fa#$01#$6f#$79#$86#$83#$3d#$03 +
#$61#$e1#$f1#$92#$ac#$bc#$03#$4e#$89#$a3#$c9#$53#$4a#$f7#$e2#$a6 +
#$48#$cf#$42#$1e#$21#$b1#$5c#$2b#$3a#$7f#$ba#$be#$6b#$5a#$f7#$0a +
#$26#$d8#$8e#$1b#$eb#$ec#$bf#$1e#$5a#$3f#$45#$c0#$bd#$31#$23#$be +
#$69#$71#$a7#$c2#$90#$fe#$a5#$d6#$80#$b5#$24#$dc#$44#$9c#$eb#$4d +
#$f9#$da#$f0#$c8#$e8#$a2#$4c#$99#$07#$5c#$8e#$35#$2b#$7d#$57#$8d +
#$02#$14 + // 20: . . . . . INTEGER
#$a7#$83#$9b#$f3#$bd#$2c#$20#$07#$fc#$4c#$e7#$e8#$9f#$f3#$39#$83 +
#$51#$0d#$dc#$dd +
#$02#$81#$80 + // 128: . . . . . INTEGER
#$0e#$3b#$46#$31#$8a#$0a#$58#$86#$40#$84#$e3#$a1#$22#$0d#$88#$ca +
#$90#$88#$57#$64#$9f#$01#$21#$e0#$15#$05#$94#$24#$82#$e2#$10#$90 +
#$d9#$e1#$4e#$10#$5c#$e7#$54#$6b#$d4#$0c#$2b#$1b#$59#$0a#$a0#$b5 +
#$a1#$7d#$b5#$07#$e3#$65#$7c#$ea#$90#$d8#$8e#$30#$42#$e4#$85#$bb +
#$ac#$fa#$4e#$76#$4b#$78#$0e#$df#$6c#$e5#$a6#$e1#$bd#$59#$77#$7d +
#$a6#$97#$59#$c5#$29#$a7#$b3#$3f#$95#$3e#$9d#$f1#$59#$2d#$f7#$42 +
#$87#$62#$3f#$f1#$b8#$6f#$c7#$3d#$4b#$b8#$8d#$74#$c4#$ca#$44#$90 +
#$cf#$67#$db#$de#$14#$60#$97#$4a#$d1#$f7#$6d#$9e#$09#$94#$c4#$0d +
#$03#$81#$84#$00 + // 132: . . . BIT STRING (0 unused bits)
#$02#$81#$80#$aa#$98#$ea#$13#$94#$a2#$db#$f1#$5b#$7f#$98#$2f#$78 +
#$e7#$d8#$e3#$b9#$71#$86#$f6#$80#$2f#$40#$39#$c3#$da#$3b#$4b#$13 +
#$46#$26#$ee#$0d#$56#$c5#$a3#$3a#$39#$b7#$7d#$33#$c2#$6b#$5c#$77 +
#$92#$f2#$55#$65#$90#$39#$cd#$1a#$3c#$86#$e1#$32#$eb#$25#$bc#$91 +
#$c4#$ff#$80#$4f#$36#$61#$bd#$cc#$e2#$61#$04#$e0#$7e#$60#$13#$ca +
#$c0#$9c#$dd#$e0#$ea#$41#$de#$33#$c1#$f1#$44#$a9#$bc#$71#$de#$cf +
#$59#$d4#$6e#$da#$44#$99#$3c#$21#$64#$e4#$78#$54#$9d#$d0#$7b#$ba +
#$4e#$f5#$18#$4d#$5e#$39#$30#$bf#$e0#$d1#$f6#$f4#$83#$25#$4f#$14 +
#$aa#$71#$e1 +
#$a3#$32 + // 50: . . [3]
#$30#$30 + // 48: . . . SEQUENCE
#$30#$0f + // 9: . . . . SEQUENCE
#$06#$03 + // 3: . . . . . OID 2.5.29.19: basicConstraints
#$55#$1d#$13 +
#$01#$01 + // 1: . . . . . TRUE
#$ff +
#$04#$05 + // 5: . . . . . OCTET STRING
#$30#$03#$01#$01#$ff +
#$30#$1d + // 29: . SEQUENCE
#$06#$03 + // 3: . . . . . OID 2.5.29.14: subjectKeyIdentifier
#$55#$1d#$0e +
#$04#$16 + // 22: . . . . . OCTET STRING
#$04#$14#$e7#$26#$c5#$54#$cd#$5b#$a3#$6f#$35#$68#$95#$aa#$d5#$ff +
#$1c#$21#$e4#$22#$75#$d6 +
#$30#$09 + // 9: . SEQUENCE
#$06#$07 + // 7: . . OID 1.2.840.10040.4.3: dsa-with-sha
#$2a#$86#$48#$ce#$38#$04#$03 +
#$03#$2f#$00 + // 47: . BIT STRING (0 unused bits)
#$30#$2c#$02#$14#$a0#$66#$c1#$76#$33#$99#$13#$51#$8d#$93#$64#$2f +
#$ca#$13#$73#$de#$79#$1a#$7d#$33#$02#$14#$5d#$90#$f6#$ce#$92#$4a +
#$bf#$29#$11#$24#$80#$28#$a6#$5a#$8e#$73#$b6#$76#$02#$68);
L := ParseX509Certificate(C[1], Length(C), B);
Assert(L = Length(C));
Assert(B.TBSCertificate.Version = X509v3);
Assert(B.TBSCertificate.SerialNumber = #17);
Assert(B.TBSCertificate.Validity.NotBefore = EncodeDate(1997, 6, 30));
Assert(B.TBSCertificate.Validity.NotAfter = EncodeDate(1997, 12, 31));
Assert(B.SignatureValue =
#$30#$2c#$02#$14#$a0#$66#$c1#$76#$33#$99#$13#$51#$8d#$93#$64#$2f +
#$ca#$13#$73#$de#$79#$1a#$7d#$33#$02#$14#$5d#$90#$f6#$ce#$92#$4a +
#$bf#$29#$11#$24#$80#$28#$a6#$5a#$8e#$73#$b6#$76#$02#$68);
end;
procedure TestSTunnelOrg;
var A, B : TX509Certificate;
C, D : RawByteString;
L : Integer;
S : RawByteString;
PKR : TX509RSAPublicKey;
PRK : TX509RSAPrivateKey;
begin
InitX509Certificate(A);
InitX509Certificate(B);
// //
// stunnel.org public certificate //
// //
C := RawByteString(
#$30#$82#$02#$0F#$30#$82#$01#$78#$A0#$03#$02#$01#$02#$02#$01#$00 +
#$30#$0D#$06#$09#$2A#$86#$48#$86#$F7#$0D#$01#$01#$04#$05#$00#$30 +
#$42#$31#$0B#$30#$09#$06#$03#$55#$04#$06#$13#$02#$50#$4C#$31#$1F +
#$30#$1D#$06#$03#$55#$04#$0A#$13#$16#$53#$74#$75#$6E#$6E#$65#$6C +
#$20#$44#$65#$76#$65#$6C#$6F#$70#$65#$72#$73#$20#$4C#$74#$64#$31 +
#$12#$30#$10#$06#$03#$55#$04#$03#$13#$09#$6C#$6F#$63#$61#$6C#$68 +
#$6F#$73#$74#$30#$1E#$17#$0D#$39#$39#$30#$34#$30#$38#$31#$35#$30 +
#$39#$30#$38#$5A#$17#$0D#$30#$30#$30#$34#$30#$37#$31#$35#$30#$39 +
#$30#$38#$5A#$30#$42#$31#$0B#$30#$09#$06#$03#$55#$04#$06#$13#$02 +
#$50#$4C#$31#$1F#$30#$1D#$06#$03#$55#$04#$0A#$13#$16#$53#$74#$75 +
#$6E#$6E#$65#$6C#$20#$44#$65#$76#$65#$6C#$6F#$70#$65#$72#$73#$20 +
#$4C#$74#$64#$31#$12#$30#$10#$06#$03#$55#$04#$03#$13#$09#$6C#$6F +
#$63#$61#$6C#$68#$6F#$73#$74#$30#$81#$9F#$30#$0D#$06#$09#$2A#$86 +
#$48#$86#$F7#$0D#$01#$01#$01#$05#$00#$03#$81#$8D#$00#$30#$81#$89 +
#$02#$81#$81#$00#$B1#$50#$53#$2E#$A8#$92#$5B#$23#$D2#$A7#$07#$C5 +
#$6D#$C1#$26#$DC#$BF#$03#$4E#$96#$D5#$81#$B5#$6C#$9A#$4A#$6A#$7B +
#$C8#$49#$EA#$C1#$67#$A5#$E5#$31#$5F#$70#$E3#$9B#$0A#$E2#$D9#$EB +
#$DB#$05#$8B#$51#$0B#$8B#$90#$BD#$D6#$A4#$5A#$0C#$CA#$30#$EE#$4E +
#$46#$8F#$4E#$43#$66#$D2#$C3#$15#$F2#$02#$0F#$45#$B5#$4B#$E9#$F6 +
#$2A#$A9#$76#$A3#$C7#$F6#$45#$2B#$D9#$A8#$3D#$96#$F6#$5F#$22#$E7 +
#$D5#$DB#$0B#$3D#$68#$4B#$89#$7D#$4B#$4F#$4B#$FB#$74#$53#$65#$5F +
#$68#$7A#$BF#$D4#$9D#$BC#$BF#$42#$68#$9C#$14#$B3#$4C#$D1#$68#$2B +
#$54#$D8#$8A#$CB#$02#$03#$01#$00#$01#$A3#$15#$30#$13#$30#$11#$06 +
#$09#$60#$86#$48#$01#$86#$F8#$42#$01#$01#$04#$04#$03#$02#$06#$40 +
#$30#$0D#$06#$09#$2A#$86#$48#$86#$F7#$0D#$01#$01#$04#$05#$00#$03 +
#$81#$81#$00#$08#$58#$15#$39#$E0#$59#$CD#$ED#$B8#$C8#$D5#$16#$14 +
#$B8#$1D#$B7#$C5#$17#$FB#$E5#$3A#$04#$EE#$E3#$8F#$EB#$BF#$61#$7E +
#$C9#$AD#$66#$10#$1F#$77#$86#$D7#$CD#$C7#$1D#$E8#$7D#$1C#$5C#$8C +
#$27#$68#$AE#$A3#$8D#$64#$5C#$04#$6D#$AE#$B1#$67#$CF#$D4#$BA#$36 +
#$1F#$56#$62#$06#$08#$1C#$B8#$5F#$CF#$9A#$5D#$DF#$37#$4C#$9F#$40 +
#$79#$28#$D3#$CD#$5F#$54#$6B#$23#$A8#$1A#$D9#$A0#$F0#$3B#$F6#$6A +
#$3F#$F9#$89#$A6#$CD#$78#$AD#$50#$7F#$7E#$68#$BA#$F9#$5C#$4D#$EC +
#$D5#$66#$DD#$99#$78#$94#$61#$A9#$41#$DC#$19#$A1#$3E#$6A#$90#$8D +
#$4C#$2F#$58);
L := ParseX509Certificate(C[1], Length(C), A);
Assert(L = Length(C));
Assert(ASN1OIDEqual(A.TBSCertificate.SubjectPublicKeyInfo.Algorithm.Algorithm, OID_RSA));
S := A.TBSCertificate.SubjectPublicKeyInfo.SubjectPublicKey;
L := Length(S);
Assert(L > 0);
Assert(ParseX509RSAPublicKey(S[1], L, PKR) = L);
Assert(Length(PKR.Modulus) = 129);
Assert((PKR.Modulus[1] = #0) and (PKR.Modulus[2] <> #0));
Assert(Length(PKR.PublicExponent) = 3);
D := EncodeX509Certificate(A);
Assert(D <> '');
// //
// stunnel.org PEM file //
// //
ParseX509RSAPrivateKeyPEM(
'MIICXAIBAAKBgQCxUFMuqJJbI9KnB8VtwSbcvwNOltWBtWyaSmp7yEnqwWel5TFf' +
'cOObCuLZ69sFi1ELi5C91qRaDMow7k5Gj05DZtLDFfICD0W1S+n2Kql2o8f2RSvZ' +
'qD2W9l8i59XbCz1oS4l9S09L+3RTZV9oer/Unby/QmicFLNM0WgrVNiKywIDAQAB' +
'AoGAKX4KeRipZvpzCPMgmBZi6bUpKPLS849o4pIXaO/tnCm1/3QqoZLhMB7UBvrS' +
'PfHj/Tejn0jjHM9xYRHi71AJmAgzI+gcN1XQpHiW6kATNDz1r3yftpjwvLhuOcp9' +
'tAOblojtImV8KrAlVH/21rTYQI+Q0m9qnWKKCoUsX9Yu8UECQQDlbHL38rqBvIMk' +
'zK2wWJAbRvVf4Fs47qUSef9pOo+p7jrrtaTqd99irNbVRe8EWKbSnAod/B04d+cQ' +
'ci8W+nVtAkEAxdqPOnCISW4MeS+qHSVtaGv2kwvfxqfsQw+zkwwHYqa+ueg4wHtG' +
'/9+UgxcXyCXrj0ciYCqURkYhQoPbWP82FwJAWWkjgTgqsYcLQRs3kaNiPg8wb7Yb' +
'NxviX0oGXTdCaAJ9GgGHjQ08lNMxQprnpLT8BtZjJv5rUOeBuKoXagggHQJAaUAF' +
'91GLvnwzWHg5p32UgPsF1V14siX8MgR1Q6EfgKQxS5Y0Mnih4VXfnAi51vgNIk/2' +
'AnBEJkoCQW8BTYueCwJBALvz2JkaUfCJc18E7jCP7qLY4+6qqsq+wr0t18+ogOM9' +
'JIY9r6e1qwNxQ/j1Mud6gn6cRrObpRtEad5z2FtcnwY=', PRK);
Assert(PRK.Version = 0);
Assert(Length(PRK.PrivateExponent) = 128);
ParseX509CertificatePEM(
'MIICDzCCAXigAwIBAgIBADANBgkqhkiG9w0BAQQFADBCMQswCQYDVQQGEwJQTDEf' +
'MB0GA1UEChMWU3R1bm5lbCBEZXZlbG9wZXJzIEx0ZDESMBAGA1UEAxMJbG9jYWxo' +
'b3N0MB4XDTk5MDQwODE1MDkwOFoXDTAwMDQwNzE1MDkwOFowQjELMAkGA1UEBhMC' +
'UEwxHzAdBgNVBAoTFlN0dW5uZWwgRGV2ZWxvcGVycyBMdGQxEjAQBgNVBAMTCWxv' +
'Y2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsVBTLqiSWyPSpwfF' +
'bcEm3L8DTpbVgbVsmkpqe8hJ6sFnpeUxX3Djmwri2evbBYtRC4uQvdakWgzKMO5O' +
'Ro9OQ2bSwxXyAg9FtUvp9iqpdqPH9kUr2ag9lvZfIufV2ws9aEuJfUtPS/t0U2Vf' +
'aHq/1J28v0JonBSzTNFoK1TYissCAwEAAaMVMBMwEQYJYIZIAYb4QgEBBAQDAgZA' +
'MA0GCSqGSIb3DQEBBAUAA4GBAAhYFTngWc3tuMjVFhS4HbfFF/vlOgTu44/rv2F+' +
'ya1mEB93htfNxx3ofRxcjCdorqONZFwEba6xZ8/UujYfVmIGCBy4X8+aXd83TJ9A' +
'eSjTzV9UayOoGtmg8Dv2aj/5iabNeK1Qf35ouvlcTezVZt2ZeJRhqUHcGaE+apCN' +
'TC9Y', A);
Assert(A.TBSCertificate.Version = X509v3);
end;
procedure Test;
begin
TestRFC2459;
TestSTunnelOrg;
end;
{$ENDIF}
end.