sgcSign 2026.9.0 is een grote release. Het grootste deel ervan komt voort uit verzoeken van klanten en het raakt drie gebieden: weten met welk certificaat u gaat ondertekenen, een handtekening bouwen die een validator over tien jaar nog steeds accepteert, en een handtekening verifiëren tegen iets anders dan zichzelf.
Dit bericht loopt de nieuwe functies door, met voor elke functie de Delphi-code. Aan het einde staat ook een korte sectie over handtekeningen van eerdere versies die opnieuw gemaakt zouden moeten worden.
Certificaatlijsten waaruit u kunt kiezen
Het opsommen van certificaten leverde vroeger een lijst met weergavenamen op, genoeg om een keuzelijst te vullen en niet genoeg om een beslissing te nemen. Twee kaarten van dezelfde instantie, uitgegeven aan dezelfde persoon, zien er in die lijst identiek uit.
De opsomming bevat nu de SHA-1-vingerafdruk, de fiscale identificatie, het serienummer, de uitgever en de geldigheidsdata, en werkt op dezelfde manier voor het Windows-certificaatarchief, een PKCS#11-token en een PFX-bestand. Verlopen certificaten en certificaten zonder privésleutel kunnen worden uitgefilterd. De vingerafdruk gaat rechtstreeks in SelectCertificateByThumbprint, zodat het certificaat dat de gebruiker heeft gekozen ook het certificaat is dat ondertekent.
uses
sgcSign_KeyProvider_WinCertStore, sgcSign_X509, sgcSign_Types;
var
oProvider: TsgcWindowsCertStoreProvider;
oList: TsgcX509CertificateList;
i: Integer;
begin
oProvider := TsgcWindowsCertStoreProvider.Create(nil);
Try
// only certificates that are still valid and hold a private key
oList := oProvider.EnumerateCertificateList([cfNotExpired, cfPrivateKey]);
Try
for i := 0 to oList.Count - 1 do
Memo1.Lines.Add(Format('%s | %s | %s | %s .. %s | %s',
[oList[i].Subject, oList[i].NIF, oList[i].SerialNumber,
DateToStr(oList[i].NotBefore), DateToStr(oList[i].NotAfter),
oList[i].Thumbprint]));
// and sign with exactly the one that was chosen
oProvider.SelectCertificateByThumbprint(oList[0].Thumbprint);
Finally
oList.Free;
End;
Finally
oProvider.Free;
End;
end;
De aanroep zonder parameters is ongewijzigd, dus bestaande code blijft werken.
Kaarten met meerdere slots inventariseren zonder pincode
Een kaart voor gekwalificeerde handtekeningen bevat vaak meer dan één certificaat, elk achter een eigen pincode. Poolse kaarten zijn het gangbare geval, een Certum-kaart met twee profielen of een PWPW Sigillum-kaart met drie containers. De gebruiker om drie pincodes vragen alleen om hem een lijst te tonen, is geen werkbare interface.
Een PKCS#11-token kan nu volledig zonder aanmelden worden geïnventariseerd. TokenSlotCount meldt hoeveel slots daadwerkelijk een token bevatten, wat het bereik is dat de moeite waard is om aan te spreken, en elk item legt vast uit welk slot en van welk tokenlabel het komt, zodat pas om de juiste pincode wordt gevraagd wanneer dat certificaat het gekozen certificaat is.
uses
sgcSign_KeyProvider_PKCS11, sgcSign_X509, sgcSign_Types;
var
oPKCS11: TsgcPKCS11Provider;
oList: TsgcX509CertificateList;
i: Integer;
begin
oPKCS11 := TsgcPKCS11Provider.Create(nil);
Try
oPKCS11.LibraryPath := 'C:\Windows\System32\cryptoCertum3PKCS.dll';
ShowMessage(Format('%d slots hold a token', [oPKCS11.TokenSlotCount]));
// walks every slot that holds a token, never logs in, never needs a PIN
oList := oPKCS11.EnumerateCertificateListAllSlots([cfNotExpired]);
Try
for i := 0 to oList.Count - 1 do
Memo1.Lines.Add(Format('slot %d (%s): %s',
[oList[i].SlotIndex, oList[i].TokenLabel, oList[i].Subject]));
Finally
oList.Free;
End;
Finally
oPKCS11.Free;
End;
end;
Het certificaat vinden dat het uwe heeft uitgegeven
Langetermijnprofielen voor handtekeningen hebben het uitgevende certificaat nodig, en de meeste kaarten voor gekwalificeerde handtekeningen bevatten alleen dat van uzelf. Twee nieuwe aanroepen op elke sleutelprovider vinden het: GetIssuerCertificate geeft het certificaat terug dat het certificaat heeft uitgegeven waarmee u ondertekent, en GetCertificateChain geeft het hele pad erboven terug. De overeenkomst wordt cryptografisch gecontroleerd in plaats van op naam, zodat een instantie die haar ondertekeningssleutel heeft vernieuwd niet wordt verward met haar voorganger.
Waar gezocht wordt is een beslissing, dus is het een eigenschap. Standaard wordt het Windows-certificaatarchief doorzocht, iluLocalStore doorzoekt PEM- of DER-bestanden die u met uw applicatie meelevert, en iluAIA downloadt het certificaat van het adres dat in uw eigen certificaat staat, wat standaard uit staat omdat het het netwerk op gaat.
uses
sgcSign_Classes, sgcSign_X509;
var
vIssuerDER: TBytes;
oChain: TsgcCertificateChain;
begin
oProvider.IssuerLookup := [iluSystemStore, iluLocalStore];
oProvider.IssuerFiles.Add('certs\ca-intermediate.pem');
oProvider.IssuerFiles.Add('certs\ca-root.pem');
vIssuerDER := oProvider.GetIssuerCertificate;
oChain := oProvider.GetCertificateChain;
end;
Twee nieuwe PAdES-profielen
spPAdESBasicT ondertekent met een ingebedde tijdstempel en zonder intrekkingsgegevens, wat u wilt wanneer de handtekening alleen hoeft te bewijzen wanneer die is gemaakt. spPAdESDocumentArchive gaat de andere kant op en voegt bovenop het langetermijnprofiel een archieftijdstempel toe die het hele document dekt, inclusief de intrekkingsgegevens, zodat het bestand controleerbaar blijft nadat het geldigheidsvenster van de eerste tijdstempel zelf is verstreken.
uses
sgcSign_PAdES, sgcSign_Types;
var
oPAdES: TsgcPAdESSigner;
begin
oPAdES := TsgcPAdESSigner.Create(nil);
Try
oPAdES.KeyProvider := oProvider;
oPAdES.Profile.Profile := spPAdESDocumentArchive;
oPAdES.TSAClient := TSAClient1;
oPAdES.OCSPClient := OCSPClient1;
// revocation lists you supply yourself, for an authority whose CRL is
// issued by its own root. IssuerCertificate is resolved automatically
// when it is left empty and OCSPClient is assigned
oPAdES.CRLFiles.Add('crl\ca-intermediate.crl');
oPAdES.SignPDFFile('contract.pdf', 'contract-signed.pdf');
Finally
oPAdES.Free;
End;
end;
Certificaten melden alles wat ze bevatten
Het onderwerp en de uitgever meldden vroeger de zeven attributen die de parser herkende, en lieten de rest vallen. Ze melden nu elk attribuut in het certificaat, het postadres wordt gedecodeerd naar leesbare regels, en elk attribuut kan via zijn OID worden gelezen.
uses
sgcSign_X509;
var
i: Integer;
begin
// by OID: organizationIdentifier
ShowMessage(oCert.GetSubjectAttribute('2.5.4.97'));
// or the full list, in the order the certificate declares it
for i := 0 to oCert.SubjectAttributeCount - 1 do
Memo1.Lines.Add(oCert.SubjectAttributeOID[i] + ' = ' +
oCert.SubjectAttributeValue[i]);
end;
Ondertekende tijdstempelverzoeken
Sommige gekwalificeerde tijdstempelinstanties, de Poolse in het bijzonder, beantwoorden een gewoon RFC 3161-verzoek niet. Zij willen dat het verzoek zelf in een CMS SignedData wordt verpakt en ondertekend. Dat is nu een eigenschap in plaats van iets dat u met de hand bouwt.
uses
sgcSign_TSA;
begin
oTSA.URL := 'https://tsa.example.com';
oTSA.RequestFormat := trfCMS; // plain RFC 3161 is still the default
oTSA.KeyProvider := oProvider; // trfCMS needs one
// authorities differ in what they expect inside the wrapper
oTSA.SignOptions.IncludeSignedAttributes := True;
// and the exact bytes are available when an authority needs checking
oTSA.OnBeforeSendRequest := DoBeforeSendRequest;
oTSA.OnAfterReceiveResponse := DoAfterReceiveResponse;
end;
De standaardvorm komt overeen met een verzoek dat de PWPW Sigillum-tijdstempelinstantie accepteert. Bestaande code blijft een gewoon verzoek verzenden, er verandert niets tenzij u RequestFormat instelt.
Authenticode-crosscertificaten
Een handtekening van een kernelmodusstuurprogramma moet via een crosscertificaat ketenen naar de Microsoft Code Verification Root, en dat is wat signtool /ac insluit. sgcSign kan nu op dezelfde manier extra certificaten insluiten.
uses
sgcSign_Authenticode;
var
oSigner: TsgcAuthenticodeSigner;
begin
oSigner := TsgcAuthenticodeSigner.Create(nil);
Try
oSigner.KeyProvider := oProvider;
oSigner.AddCertificateFromFile('MSCV-VSClass3.cer');
// or from bytes you already hold
// oSigner.AddCertificate(vCrossCertDER);
oSigner.SignFile('driver.sys', 'driver-signed.sys');
Finally
oSigner.Free;
End;
end;
Ze stromen door in elke geneste handtekening, en niets toevoegen laat de handtekening byte voor byte zoals die was. De ondertekeningsserver accepteert een add_certs-veld en de CLI een herhaalbare --add-cert-optie.
Verificatie met vertrouwensankers
Dit is de belangrijkste wijziging in de release. Tot nu toe haalde de verificateur het ondertekeningscertificaat uit het document dat hij controleerde en bevestigde hij dat die sleutel dat document had ondertekend. Dat bewijst dat wie het document schreef ook de handtekening erin schreef, en niets meer. Iedereen kan een document maken dat slaagt.
Verificatie kan nu vertrouwensankers meekrijgen, en bouwt en controleert de certificaatketen daartegen. Een anker wordt gematcht op SHA-256-vingerafdruk of door verificatie onder zijn eigen sleutel, nooit op naam.
uses
sgcSign_Verifier, sgcSign_Types;
var
oVerifier: TsgcSignatureVerifier;
begin
oVerifier := TsgcSignatureVerifier.Create(nil);
Try
oVerifier.TrustedCertificates.Add('certs\qualified-root.pem');
// or a Windows certificate store by name
oVerifier.TrustedCertificateStore := 'ROOT';
oVerifier.RequireTrustedChain := True;
oVerifier.CheckKeyUsage := True;
oVerifier.RequireCompleteRevocationCheck := True;
if oVerifier.Verify(vSignedXML) = vsValid then
ShowMessage('signed, and chained to a root you trust');
Finally
oVerifier.Free;
End;
end;
Een verificateur zonder anker geeft hetzelfde oordeel terug als voorheen, dus er gaat niets kapot bij het upgraden. Eén ding verandert wel: het ETSI TS 119 102-2-rapport zegt niet langer total-passed voor een handtekening die nooit aan een anker is geketend, het zegt indeterminate met NO_CERTIFICATE_CHAIN_FOUND. Rapporten die met eerdere versies zijn opgeslagen, moeten opnieuw worden gegenereerd.
Eén HTTP-transport, met proxy's
De bibliotheek doet vanuit verschillende plekken netwerkverzoeken: de tijdstempelclient, de OCSP- en intrekkingslijstclients, de download van de EU-vertrouwenslijst en de cloudsleutelproviders. Elk daarvan had een eigen idee over hoe je zo'n verzoek maakt. Ze delen nu één transport met één HTTPOptions-eigenschap.
uses
sgcSign_WinHTTP;
begin
oTSA.HTTPOptions.Proxy.Mode := pxCustom; // pxSystem is the default
oTSA.HTTPOptions.Proxy.URL := 'proxy.corp.local:8080';
oTSA.HTTPOptions.Proxy.Username := 'user';
oTSA.HTTPOptions.Proxy.Password := 'secret';
// the certificate presented when the gateway asks for client authentication
oTSA.HTTPOptions.ClientCertificate.StoreName := 'MY';
oTSA.HTTPOptions.ClientCertificate.Thumbprint := 'a1b2c3...';
oTSA.HTTPOptions.MinTLSVersion := tlsTLS1_2;
end;
De proxy kan de machinebrede proxy zijn, helemaal geen proxy, een expliciet adres, of de instelling per gebruiker die via WPAD of een PAC-script wordt opgelost, wat ook de browser doet. Elke instelling heeft als standaardwaarde wat die verzoeken eerder deden. Voor een gateway die deze instellingen niet kunnen beschrijven, vervangt een nieuwe OnHTTPRequest-gebeurtenis het transport volledig.
Kleinere zaken die het weten waard zijn
OCSP-nonces. Het intrekkingsverzoek bevat nu een willekeurige nonce van de cryptografische generator van het systeem, en het antwoord wordt daartegen gecontroleerd. Een antwoord dat geen nonce weerkaatst wordt nog steeds geaccepteerd, omdat RFC 6960 vooraf geproduceerde antwoorden toestaat, maar een antwoord dat een andere nonce weerkaatst wordt geweigerd. NonceEnabled schakelt de extensie uit voor een responder die haar niet accepteert.
Pinning van EU-vertrouwenslijstpivots. Een nieuwe RequirePinnedPivot-eigenschap bepaalt of de lijst van vertrouwde lijsten moet ketenen naar een van de vastgezette pivot-vingerafdrukken uit het Publicatieblad, waarbij LOTLPivotPinned en LastPivotFingerprint de uitkomst melden. De controle bestond al en werd vanuit niets aangeroepen. De vastgezette constanten die worden meegeleverd zijn nog steeds de gedocumenteerde plaatshouders, dus er wordt een misser gemeld totdat u echte vingerafdrukken invult en de eigenschap inschakelt.
Een digest die u kiest. CAdES en PKCS#11 krijgen allebei een HashAlgorithm-eigenschap, standaard SHA-256 zodat bestaande code dezelfde bytes produceert. CAdES schreef SHA-256 vroeger als letterlijke waarde in elk digest-algoritme, en PKCS#11 koos zijn DigestInfo-header op basis van de lengte van wat het kreeg aangereikt, zodat geen andere digest kon worden uitgedrukt. Een kaart kan nu op verzoek ondertekenen met SHA-1, SHA-256, SHA-384 of SHA-512.
ASiC met een ondertekeningscallback. Een nieuwe overload van BuildCAdES neemt een callback in plaats van voltooide handtekeningbytes. Hij bouwt eerst META-INF/ASiCManifest.xml, geeft precies die bytes door aan uw callback en slaat op wat terugkomt als META-INF/signature.p7s, wat de enige volgorde is waarin de handtekening het manifest kan dekken. Voor ASiC-S, dat geen manifest bevat, ontvangt de callback het gegevensdocument zelf. GetCAdESSignedData geeft dezelfde bytes terug voor aanroepers die twee expliciete stappen verkiezen.
Cloud-KMS-certificaten. AWS KMS en Google Cloud KMS krijgen SetCertificate en SetCertificateFromFile, overeenkomend met het paar dat HashiCorp Vault al had. Beide diensten geven een kale publieke sleutel uit, en er was voorheen geen manier om aan een van beide providers door te geven welk X.509-certificaat daarbij hoort.
Een tijdstempel van de machine die de CLI draait. De opdrachtregel sgcsign krijgt --tsa-direct, waarmee de tijdstempelinstantie rechtstreeks wordt bevraagd in plaats van via de sgcSign Server te gaan. Geef die samen met --tsa mee.
De ondertekeningsserver
De serverkant kreeg zijn eigen lijst. Een Authenticode-handtekening kan nu meer dan twee geneste handtekeningen bevatten, met voor elk daarvan een ander certificaat, via een geordende hash_algorithms-lijst zoals sha1,sha256,sha384 of een geordende providers-lijst zoals certA:sha256,certB:sha1, in beide gevallen tot vier items. Dat is bedoeld om één bestand uit te leveren dat is ondertekend door een verlopend certificaat en zijn vervanger. Elk certificaat wordt getoetst aan de rechten van de API-sleutel voordat er ook maar iets wordt ondertekend.
Windows-catalogusbestanden kunnen worden ondertekend: het upload-eindpunt accepteert catalog als formaat en ondertekent een bestaand .cat-bestand van het soort dat makecat produceert, zodat een stuurprogrammapakket op dezelfde manier wordt ondertekend als een programma.
Een nieuw /api/v1/sign/raw-eindpunt ondertekent een digest die u al hebt berekend en geeft alleen de handtekeningwaarde terug, zonder PKCS#7-omhulsel, zonder ondertekende attributen en zonder tijdstempel. Dat is precies wat signtool vraagt via zijn /dlib-callback. Omdat het elke aangereikte digest ondertekent, staat het standaard uit en wordt het per provider ingeschakeld met allow_raw_sign.
API-sleutels en de gebruikers die ze aanmaken zijn nu per project geïsoleerd, een projectbeheerder beheert de sleutels binnen zijn eigen project, en sleutels kunnen worden in- en uitgeschakeld in plaats van alleen eenmalig te worden ingetrokken. De rate limit en het dagquotum per sleutel kunnen na het aanmaken van de sleutel worden bewerkt. Een nieuwe SessionAbsoluteMaxMin-instelling begrenst de totale levensduur van een beheerderssessie standaard op twaalf uur, omdat elk geauthenticeerd verzoek de vervaltijd vroeger zonder plafond vooruitschoof. Het auditlogboek kan worden gefilterd op clientadres, in de console en in de CSV-export, waarbij een gedeeltelijk adres vanaf links matcht. En nieuwe instellingen voor doorgestuurde headers, standaard uit, herstellen het echte clientadres wanneer de server achter een reverse proxy draait, alleen geloofd wanneer de verbinding binnenkomt vanaf een vermelde vertrouwde proxy.
Handtekeningen die u opnieuw moet maken
Drie defecten in eerdere versies produceerden bestanden die structureel onjuist zijn, en upgraden herstelt geen bestand dat al is weggeschreven. Als een van deze punten beschrijft wat u hebt ondertekend, onderteken het dan opnieuw met 2026.9.0.
- Alles wat met een EC-sleutel is ondertekend. Elke PAdES-, CAdES-, Authenticode-, NuGet- en RFC 3161-handtekening die met een EC-sleutel is gemaakt, was ongeldig. De standaarden vereisen dat de handtekeningwaarde binnen een CMS-structuur een DER ECDSA-Sig-Value is, de sleutelproviders produceren de kale aaneenschakeling van r en s, en niets converteerde die onderweg. P-256, P-384 en P-521 werken nu allemaal. De XAdES- en XML-DSig-paden waren correct en zijn bewust ongemoeid gelaten.
- Elke CAdES-handtekening, en alles wat met een kaart, een USB-token of een cloudsleutel is ondertekend.
SignDatahad geen vastgelegd contract en de providers waren het oneens over de vraag of het argument de gegevens is of een digest daarvan, waardoor een CAdES-handtekening RSA over een hash van een hash was. Het argument is nu de kale bytes en de provider hasht ze met zijn geconfigureerde digest. - PAdES-bestanden die zijn ondertekend nadat een andere ondertekenaar dezelfde sleutelprovider had gebruikt.
Profile.HashAlgorithmwerd één keer in de constructor geschreven en daarna nooit meer gelezen, zodat een PDF die na een Facturae- of SAF-T-ondertekenaar werd ondertekend RSA over een SHA-1-digest kon zijn binnen een handtekening die SHA-256 declareerde.
Verificatie is in dezelfde richting veranderd. Authenticode-verificatie controleerde nooit een handtekening, ze berekende de bestandshash opnieuw en vergeleek die met de hash in de handtekening, dus voor het vervalsen van een bestand dat sgcSign geldig ondertekend noemde was geen privésleutel nodig. Intrekkingsantwoorden en tijdstempeltokens werden ingebed zonder te worden geverifieerd. De EU-vertrouwenslijst werd gedownload en gebruikt zonder ook maar iets te verifiëren. Al die onderdelen doen nu de controle die hun naam impliceert, en het volledige verslag van elk ervan staat in de changelog.
Verkrijgbaarheid
sgcSign 2026.9.0 is nu beschikbaar, met volledige broncode en één jaar updates, voor Delphi 7 tot en met Delphi 13 Florence, de bijbehorende C++ Builder-versies, en .NET.
Productpagina · Download de proefversie · Changelog
Vragen of feedback? Neem contact op, u krijgt antwoord van de mensen die de code hebben geschreven.
