İki birim, aynı X.509 biçiminin zıt yönlerini kapsar: sgcCrypto_X509, başkasının size verdiği bir sertifikayı okur ve doğrular; sgcCrypto_X509_Gen, kendinize ait bir sertifika veya istek yazar. Üçüncü bir birim, sgcCrypto_ASN1, ikisinin de üzerine oturduğu DER/PEM altyapısını sağlar.
Bir sertifikayı okuma ve doğrulama
sgcCrypto_X509, DER kodlu bir sertifikayı sade bir kayda ayrıştırır ve buna karşı bir zinciri veya bir iptal listesini kontrol eder. Uygulamanız hat üzerinden bir sertifika aldığında ve buna güvenip güvenmeyeceğine karar vermesi gerektiğinde API'nin bu tarafını kullanın.
function sgcX509_Parse(const aDER: TBytes; out aCert: TsgcX509Cert): Boolean;
function sgcX509_VerifySignedBy(const aCert, aIssuer: TsgcX509Cert): Boolean;
function sgcX509_VerifyChain(const aChainDER: array of TBytes;
const aTrustRootsDER: array of TBytes; aNowUnix: Int64): Boolean;
function sgcX509_SubjectOneLine(const aCert: TsgcX509Cert): string;
function sgcX509_IssuerRFC2253(const aCert: TsgcX509Cert): string;
function sgcX509_CRL_Parse(const aDER: TBytes; out aCRL: TsgcX509CRL): Boolean;
function sgcX509_IsRevoked(const aCRL: TsgcX509CRL; const aSerial: TBytes): Boolean;
function sgcX509_CSR_Parse(const aDER: TBytes; out aCSR: TsgcX509CSR): Boolean;
function sgcX509_CSR_Verify(const aCSR: TsgcX509CSR): Boolean;
sgcX509_VerifyChain, zinciri ve güven çıpalarını ayrıştırılmış kayıtlar olarak değil DER blokları olarak alır, ve geçerlilik penceresinin karşılaştırılacağı bir Unix zaman damgası alır. Zincirin 0. öğesi yapraktır ve her sertifika bir sonraki tarafından imzalanmış olmalıdır; en üstteki, güven köklerinden biri tarafından imzalanmış veya biriyle aynı olmalıdır. Bir sertifika aNowUnix anında geçerlilik penceresinin dışındaysa, bir veren CA değilse, basicConstraints pathLenConstraint değerini ihlal ediyorsa (kendi kendine verilen sertifikalar sınıra sayılmaz, RFC 5280 bölüm 6.1.4) veya güven çıpası dahil yoldaki herhangi bir sertifikadaki bir nameConstraints uzantısını ihlal ediyorsa yol reddedilir. Denetlenen kısıtlama seçenekleri dNSName, iPAddress, rfc822Name, uniformResourceIdentifier ve directoryName'dir; yaprak kendi DNS adını taşımadığında DNS alt ağaçları, bir ana bilgisayar adı gibi okunan bir yaprak ortak adına da uygulanır, ve başka herhangi bir seçeneği adlandıran kritik bir nameConstraints yolu reddeder. Sertifika politikaları denetlenmez.
TsgcX509Cert, ayrıştırmadan sonra bir çağıranın gerçekten ihtiyaç duyduğu alanları sunar: Version, SerialNumber, ham Issuer/Subject RDN dizileri, NotBeforeUnix/NotAfterUnix, açık anahtar (PubKeyType'ın söylediğine göre x509pkRSA, x509pkEC, x509pkEd25519, x509pkMLDSA, x509pkSLHDSA veya x509pkMLKEM) ve IsCA. Kuantum sonrası bir anahtar PQCPublicKey içinde gelir, ve nesne tanımlayıcısının adlandırdığı parametre kümesi, MLDSAParams, SLHDSAParams veya MLKEMParams alanlarından türle eşleşenin içindedir. TsgcX509CSR, bir PKCS#10 isteği için aynı alanları yansıtır.
Bir sertifika veya bir istek üretme
sgcCrypto_X509_Gen, bir RSA anahtar çiftinden (bkz. Açık Anahtar Kriptografisi) kendi kendine imzalı bir sertifika, veya bir CA'ya gönderilecek bir PKCS#10 sertifika imzalama isteği oluşturur. İkisi de aynı seçenekler kaydını alır.
function sgcX509_CreateSelfSigned(const aKey: TsgcRSAPrivateKey; const aOptions: TsgcX509Options): TBytes;
function sgcX509_CreateCSR(const aKey: TsgcRSAPrivateKey; const aOptions: TsgcX509Options): TBytes;
function sgcX509_ToPEM(const aDER: TBytes; const aLabel: string): string;
var
oKey: TsgcRSAPrivateKey;
oOptions: TsgcX509Options;
oDER: TBytes;
begin
oKey := sgcRSA_GenerateKey(2048);
oOptions := sgcX509_DefaultOptions;
oOptions.Subject.CommonName := 'example.com';
oOptions.NotBefore := Now;
oOptions.NotAfter := Now + 365;
SetLength(oOptions.DNSNames, 2);
oOptions.DNSNames[0] := 'example.com';
oOptions.DNSNames[1] := 'www.example.com';
oOptions.KeyUsage := [kuDigitalSignature, kuKeyEncipherment];
SetLength(oOptions.ExtKeyUsage, 1);
oOptions.ExtKeyUsage[0] := '1.3.6.1.5.5.7.3.1'; { TLS server authentication }
oDER := sgcX509_CreateSelfSigned(oKey, oOptions);
Memo1.Text := sgcX509_ToPEM(oDER, 'CERTIFICATE');
end;
Geçerlilik penceresini UTC'den oluşturun.
Sertifika geçerliliği, Greenwich saati anlamına gelen bir Z son ekiyle UTCTime olarak kodlanır, bu yüzden NotBefore ve NotAfter'ı yerel bir Now'dan doldurmak, sertifikayı UTC'den olan farkınız kadar yanlış tarihlendirir. Greenwich'in doğusunda, yeni verilmiş bir sertifika bu fark geçene kadar henüz geçerli olmaz; batısında ise sertifika erken sona erer. sgcX509_UTCNow doğru değeri döndürür, ve sgcX509_DefaultOptions zaten bunu, gerçek CA'ların uyguladığı aynı marj olan saat kayması için beş dakikalık bir geri tarihlemeyle kullanır.
function sgcX509_UTCNow: TDateTime;
var
oOptions: TsgcX509Options;
begin
oOptions := sgcX509_DefaultOptions;
oOptions.NotBefore := sgcX509_UTCNow;
oOptions.NotAfter := sgcX509_UTCNow + 365;
end;
Konu alternatif adlarını (subject alternative names) doldurun.
Modern tarayıcılar ve TLS kütüphaneleri ortak adı tamamen görmezden gelir ve yalnızca DNSNames uzantısına bakar; mükemmel bir ortak ada sahip ama SubjectAltName içinde hiçbir DNS adı olmayan bir sertifika, geri kalanı ne kadar doğru olursa olsun reddedilir.
DNSNames, IPAddresses ve Emails'in yanı sıra, TsgcX509Options, uniformResourceIdentifier türündeki konu alternatif adları için bir URIs dizisi taşır. Bir ana bilgisayar adı yerine bir URI olan bir hizmet kimliği buraya gider; somut örnek, bir iş yükü ağının (workload mesh) birebir eşleştirdiği ve hiçbir DNS adının ifade edemeyeceği bir SPIFFE kimliğidir. Değer, IA5String içeriği olarak olduğu gibi girer, bu yüzden tam bir URI geçirin.
SetLength(oOptions.URIs, 1);
oOptions.URIs[0] := 'spiffe://example.com/ns/default/sa/payments';
Bir yaprak sertifika yerine bir CA sertifikası üretmek için IsCA := True ve negatif olmayan bir PathLength ayarlayın; bir yaprak üzerinde kısıtlamayı atlamak için PathLength'i -1'de bırakın. Bir CSR, aynı istenen uzantıları extensionRequest özniteliğinde taşır; bu, bir CA'nın son sertifikayı verirken bunları aradığı yerdir.
IPv6 konu alternatif adları.
IPAddresses, IPv4'ü noktalı biçimde ve IPv6'yı RFC 4291'in metin biçimlerinden herhangi biriyle alır: tam sekiz gruplu biçim, :: ile sıkıştırılmış bir biçim, ve son 32 bitte gömülü bir IPv4 adresi taşıyan bir biçim, herhangi bir harf durumuyla. Köşeli parantez eklemeyin ve bir bölge dizini (zone index) eklemeyin. Bir IPv4 girdisi 4 sekizli, bir IPv6 girdisi 16 sekizli olarak yazılır; RFC 5280'in gerektirdiği ve bir TLS istemcisinin eş adresiyle karşılaştırdığı şey budur.
SetLength(oOptions.IPAddresses, 3);
oOptions.IPAddresses[0] := '192.0.2.10';
oOptions.IPAddresses[1] := '2001:db8::1';
oOptions.IPAddresses[2] := '::ffff:192.0.2.10';
Bir CA sertifikası üzerinde ad kısıtlamaları.
Bir CA sertifikası, altındaki sertifikaların neyi adlandırmasına izin verildiğini sınırlayabilir, RFC 5280 bölüm 4.2.1.10. PermittedDNSNames, ExcludedDNSNames, PermittedIPRanges veya ExcludedIPRanges alanlarından herhangi birini doldurun, uzantı kritik olarak işaretlenmiş biçimde yazılır. Bir DNS girdisi bir alt ağaçtır, bu yüzden example.com hem o adı hem de altındaki her adı kapsar. Bir IP girdisi, bir önek uzunluğuyla birlikte bir adrestir. sgcX509_VerifyChain, burada yazdığınızı uygular.
oOptions.IsCA := True;
oOptions.PathLength := 0;
oOptions.KeyUsage := [kuKeyCertSign, kuCRLSign];
SetLength(oOptions.PermittedDNSNames, 1);
oOptions.PermittedDNSNames[0] := 'internal.example.com';
SetLength(oOptions.PermittedIPRanges, 2);
oOptions.PermittedIPRanges[0] := '192.0.2.0/24';
oOptions.PermittedIPRanges[1] := '2001:db8::/32';
Bir CA'dan sertifika verme
Kendi kendine imzalı bir sertifika, üçüncü bir tarafın denetleyebileceği hiçbir şey öne sürmez. sgcX509_CreateSigned, bir CA anahtarından ve CA'nın kendi sertifikasından bir sertifika verir, ve sgcX509_CreateSignedFromCSR aynısını bir PKCS#10 isteğinden başlayarak yapar.
function sgcX509_CreateSigned(const aIssuerKey: TsgcX509SignKey;
const aIssuerCertDER, aSubjectPublicKeyInfo: TBytes; const aOptions: TsgcX509Options): TBytes;
function sgcX509_CreateSignedFromCSR(const aIssuerKey: TsgcX509SignKey;
const aIssuerCertDER, aCSRDER: TBytes; const aOptions: TsgcX509Options): TBytes;
Veren adı, veren sertifikanın konusundan bayt bayt kopyalanır, böylece zincir tam olarak bağlanır, ve authorityKeyIdentifier o sertifikanın subjectKeyIdentifier değeridir. TsgcX509Options.Issuer hiçbir oluşturucu tarafından hiçbir zaman okunmaz. Her iki fonksiyon da, veren sertifika bozuk olduğunda, bir CA olmadığında veya keyCertSign içermeyen bir anahtar kullanımı taşıdığında, geçirdiğiniz anahtar o sertifikanın anahtarı olmadığında, ve konu anahtarı bozuk olduğunda ya da birimin bilmediği bir algoritmaya ait olduğunda, hiçbir şey imzalanmadan önce hata verir.
Bir istekten sertifika verilirken önce isteğin üzerindeki imza doğrulanır ve başarısız olan bir istek hata verir. Konu adı ve açık anahtar istekten gelir, bu yüzden aOptions.Subject göz ardı edilir, ve uzantılar yalnızca aOptions içinden gelir. İsteğin talep ettiği şey kopyalanmaz: sertifikanın neyi öne süreceğine CA karar verir.
Bir EC anahtarıyla imzalama
sgcX509_CreateSelfSigned ve sgcX509_CreateCSR, RSA ile imzalar. Ex çifti bunun yerine, her iki anahtar türünü de saran bir TsgcX509SignKey alır; bu yüzden aynı seçenekler kaydı, ona ne verdiğinize bağlı olarak bir RSA veya bir EC sertifikası üretir. Sarmalayıcıyı kaydı elle doldurmak yerine sgcX509_RSAKey veya sgcX509_ECKey ile oluşturun; kullanılmayan yarı kasıtlı olarak tanımsız bırakılır. İki özgün fonksiyon değişmeden kalır ve şimdi dahili olarak yalnızca sgcX509_RSAKey'i sarar.
Bir P-256 sertifikası, 2048 bitlik bir RSA sertifikasının kabaca üçte biri boyutundadır ve çok daha hızlı doğrulanır; modern dağıtımların varsayılan olarak EC'yi seçmesinin nedeni budur. TsgcX509Options.Hash, hâlâ yalnızca SHA'yı adlandırır; imza algoritması anahtar türünü izler, bu yüzden bir EC anahtarıyla rhSHA256, ecdsa-with-SHA256 anlamına gelir, ve bir RSA anahtarıyla sha256WithRSAEncryption anlamına gelir.
type
TsgcX509KeyKind = (xkRSA, xkEC, xkMLDSA, xkSLHDSA);
function sgcX509_RSAKey(const aKey: TsgcRSAPrivateKey): TsgcX509SignKey;
function sgcX509_ECKey(aCurve: TsgcECCurve; const aPrivateKey: TBytes): TsgcX509SignKey;
function sgcX509_CreateSelfSignedEx(const aKey: TsgcX509SignKey; const aOptions: TsgcX509Options): TBytes;
function sgcX509_CreateCSREx(const aKey: TsgcX509SignKey; const aOptions: TsgcX509Options): TBytes;
var
oPrivateKey, oPublicKey, oDER: TBytes;
oOptions: TsgcX509Options;
begin
sgcEC_GenerateKeyPair(eccP256, oPrivateKey, oPublicKey);
oOptions := sgcX509_DefaultOptions;
oOptions.Subject.CommonName := 'example.com';
SetLength(oOptions.DNSNames, 1);
oOptions.DNSNames[0] := 'example.com';
oDER := sgcX509_CreateSelfSignedEx(sgcX509_ECKey(eccP256, oPrivateKey), oOptions);
Memo1.Text := sgcX509_ToPEM(oDER, 'CERTIFICATE');
end;
sgcX509_ECKey, açık noktayı skalerden türetir, bu yüzden onu asla kendiniz geçirmek zorunda kalmazsınız. Eğri sgcCrypto_ECCurves'ten gelir ve uyguladığı yedi eğriden herhangi biri olabilir, gerçi genel bir CA yalnızca P-256 veya P-384'ü kabul eder.
Kuantum sonrası sertifikalar
Aynı Ex oluşturucuları kuantum sonrası bir anahtarla imzalar. sgcX509_MLDSAKey bir ML-DSA anahtar çiftini sarar ve sgcX509_SLHDSAKey bir SLH-DSA özel anahtarını sarar; oradan sonra başka hiçbir şey değişmez: aynı seçenekler kaydı, ML-DSA veya SLH-DSA ile imzalanmış kendi kendine imzalı bir sertifika veya bir sertifika imzalama isteği üretir. Şemaların kendisi ve sertifikanın taşıdığı anahtar kapsayıcıları için bkz. Kuantum Sonrası Kriptografi.
function sgcX509_MLDSAKey(aParams: TsgcMLDSAParams; const aPrivateKey, aPublicKey: TBytes): TsgcX509SignKey;
function sgcX509_SLHDSAKey(aParams: TsgcSLHDSAParams; const aPrivateKey: TBytes): TsgcX509SignKey;
var
oSeed, oPub, oPriv, oDER: TBytes;
oOptions: TsgcX509Options;
begin
sgcMLDSA_GenerateKeyPairAndSeed(mldsa65, oSeed, oPub, oPriv);
oOptions := sgcX509_DefaultOptions;
oOptions.Subject.CommonName := 'example.com';
SetLength(oOptions.DNSNames, 1);
oOptions.DNSNames[0] := 'example.com';
{ the default options ask for kuKeyEncipherment, which ML-DSA does not allow }
oOptions.KeyUsage := [kuDigitalSignature];
oDER := sgcX509_CreateSelfSignedEx(sgcX509_MLDSAKey(mldsa65, oPriv, oPub), oOptions);
Memo1.Text := sgcX509_ToPEM(oDER, 'CERTIFICATE');
end;
Her iki şema da boş bir bağlam dizesiyle saf kipte imzalar, ve imza algoritması, parametreler yokken anahtarın kendi nesne tanımlayıcısıdır; ML-DSA için RFC 9881 ve SLH-DSA için RFC 9909 izlenir. TsgcX509Options.Hash, bu anahtarlar için göz ardı edilir, çünkü ikisi de gövdeyi ayrıca hash'lemez. sgcX509_MLDSAKey, her iki anahtar uzunluğunu da denetler ve çifte sabit bir mesajı imzalatıp doğrulatır, böylece yanlış açık anahtarla verilmiş bir özel anahtar, hiçbir şeyin doğrulayamayacağı bir sertifika üretmek yerine orada hata verir.
Anahtar kullanımı kısıtlıdır.
Bir ML-DSA veya SLH-DSA anahtarıyla yalnızca kuDigitalSignature, kuNonRepudiation, kuKeyCertSign ve kuCRLSign kabul edilir, ve kural hiçbir şey imzalanmadan önce uygulanır. sgcX509_DefaultOptions, kuKeyEncipherment ister, bu yüzden KeyUsage değerini değiştirene kadar bu anahtarlarla hata verir. Boş bir KeyUsage, uzantıyı dışarıda bırakır; her iki RFC de buna izin verir.
Bir ML-KEM anahtarını sertifikalandırma.
Bir ML-KEM anahtarı kapsülleme yapar, asla imzalamaz, bu yüzden bir sertifikanın konusu olabilir ama bir sertifika istemek için hiçbir zaman bir isteği imzalayamaz. Sahipliği başka yollarla kanıtladıktan sonra SubjectPublicKeyInfo değerini sgcX509_CreateSigned fonksiyonuna geçirin; RFC 9935'in uygulamaya bıraktığı şey budur. Böyle bir sertifikada izin verilen tek anahtar kullanımı bayrağı kuKeyEncipherment değeridir. Aynı nedenle sgcX509_CSR_Verify bir ML-KEM isteğinde hiçbir zaman geçemez, ve sgcX509_VerifySignedBy bir ML-KEM veren anahtarıyla hiçbir zaman hiçbir şeyi doğrulamaz.
Birini geri okuma.
sgcX509_Parse ve sgcX509_CSR_Parse, kuantum sonrası bir sertifikayı, SubjectPublicKeyInfo değeri ya da ML-DSA veya SLH-DSA imza algoritması parametre taşıdığında ve anahtar uzunluğu nesne tanımlayıcısının adlandırdığı parametre kümesiyle eşleşmediğinde bozuk sayar. Bir ML-KEM anahtarının ayrıca FIPS 203 modül denetiminden geçmesi gerekir. sgcX509_VerifySignedBy, bir ML-DSA veya SLH-DSA imzasını boş bir bağlamla saf kipte doğrular, ve imza algoritmasının adlandırdığı parametre kümesi veren anahtarın kendi kümesi değilse False döndürür.
DER, ASN.1 ve PEM altyapısı
sgcCrypto_ASN1, DER okur, PEM ve Base64'ü çözer ve DER'den RSA/EC özel ve açık anahtarlarını ayrıştırır. sgcCrypto_DER, sgcCrypto_X509_Gen tarafından ve Açık Anahtar Kriptografisi sayfasındaki RSA anahtarı dışa aktarma fonksiyonları tarafından dahili olarak kullanılan yazıcıdır; çoğu uygulama bunu doğrudan çağırmayacaktır.
function sgcPEM_Decode(const aPEM: string; out aLabel: string): TBytes;
function sgcPEM_Encode(const aDER: TBytes; const aLabel: string): string;
function sgcBase64Encode(const aData: TBytes): string;
function sgcBase64Decode(const aText: string): TBytes;