2125 lines
78 KiB
ObjectPascal
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.
|
|
|