Chilkat v11.7.0
Chilkat2 Java · Chilkat2 PHP · Chilkat2 Perl · Chilkat for Lua · Chilkat C++ over CBridge · PDF text extraction, text replacement, watermarks, encryption and optimization · SSH strict key exchange and post-quantum key exchange · TLS 1.3 on the server side · Authenticode signing of MSI, PowerShell and VBScript files
This release adds four new language APIs that follow the conventions of their languages. Three sit alongside the classic libraries, which are unchanged: Chilkat2 Java (exceptions, try-with-resources, and lambdas), Chilkat2 PHP (namespaced classes, real properties, exceptions, and closures), and Chilkat2 Perl (accessors, character strings, closures for events, and an optional DBI-style RaiseError). The fourth is new: Chilkat for Lua, a C module for Lua 5.1/LuaJIT, 5.4 and 5.5 (snake_case names, nil, err on failure, and closures for events). A new download for C++, Chilkat C++ over CBridge, is the existing Chilkat C++ API — the same classes and the same header files — as source code you compile with your own compiler, over the Chilkat C bridge shared library, so one library per platform works with every C++ compiler. The Pdf class gains five JSON-driven methods: ExtractText (plain text, Markdown, search and tables, with a quality report), TextReplace (fill placeholders and label values in a PDF template), Watermark (add, detect, list and remove), Protection (passwords, certificates and permissions, RC4 to AES-256-GCM) and Optimize (lossless size reduction), plus WriteFile/WriteBd, AddEmbeddedFiles2, SignPdf2, AddVerificationInfo2 and UpdateMetadata2 (embed files, sign, add LTV information and update XMP metadata without writing a file) and the PreserveSignatures property. For every SSH connection (Ssh, Scp, SFtp and SshTunnel), Chilkat now uses strict key exchange (the Terrapin countermeasure) and post-quantum hybrid key exchange when the server supports them, and offers Encrypt-then-MAC MACs by default again. The Socket class now supports TLS 1.3 on the server side, and a Chilkat TLS server gains SNI certificate selection, ALPN, and session resumption. CodeSign now signs, verifies and removes Authenticode signatures on Windows Installer packages (.msi), PowerShell files (.ps1, .psm1, .psd1) and VBScript files (.vbs), as well as EXE and DLL files.
✨ Release Highlights
| Area | What’s new |
|---|---|
| ☕ New API | Chilkat2 Java (com.chilkatsoft.chilkat2): failures throw ChilkatException, objects are AutoCloseable, progress events are lambdas; Java 8 and later on Windows, Linux, Alpine Linux, and macOS |
| 🐘 New API | Chilkat2 PHP (the chilkat2 extension, namespace Chilkat2\): real properties, failures throw Chilkat2\Exception, progress events are closures; PHP 8.1 to 8.5 on Windows, Linux, Alpine Linux, and macOS, installed with Composer or from the download page |
| 🐪 New API | Chilkat2 Perl (use Chilkat2;, Chilkat2::Http->new): one accessor per property, Perl character strings in and out, progress events are closures, optional RaiseError that dies with a Chilkat2::Error, perldoc for every class; Perl 5.26 to 5.44 on Windows (Strawberry Perl), Linux, Alpine Linux, and macOS, installed with one command, with cpanm, or from the download page |
| 🌙 New API | Chilkat for Lua (local chilkat = require("chilkat"), chilkat.Http.new()): properties are fields, members in snake_case, failures return nil, err, progress events are closures, LuaLS annotations for editors; Lua 5.1 (and LuaJIT), 5.4 and 5.5 on Windows, Linux, Alpine Linux, and macOS, installed with one command, with LuaRocks, or from the download page |
| 🧩 New download | Chilkat C++ over CBridge (chilkat-cpp-bridge.zip): the Chilkat C++ API with unchanged headers, compiled from source by your own compiler and linked with one C bridge library per platform — any Visual C++ version, MinGW-w64, C++Builder, and any GCC or Clang; Windows (64-bit, 32-bit, ARM64), Linux, Alpine Linux, and macOS in one download |
| 📄 PDF text | Pdf.ExtractText: plain text (flow, lines or grid), Markdown, search and tables from a PDF, scoped by page or by a label such as "Invoice Number:", with a per-page quality report that flags pages needing OCR. Pdf.TextReplace: fill {{placeholders}} and label values in a PDF template, with a font-rule check before an all-or-nothing apply |
| 💧 PDF watermarks | Pdf.Watermark: text, image and PDF-page watermarks stored as artifacts, annotations, layers or page content; detect, list and remove Chilkat's, Acrobat's and other producers' watermarks |
| 🔒 PDF encryption | Pdf.Protection: encrypt with passwords or certificates (RC4-40 to AES-256-GCM), permissions, scope, change passwords or permissions, remove encryption; PreLoadCheck inspects a file's security before loading |
| 🔐 SSH security | For all SSH connections (Ssh, Scp, SFtp, SshTunnel): strict key exchange (kex-strict-c-v00@openssh.com, the Terrapin countermeasure) and post-quantum hybrid key exchange (mlkem768x25519-sha256, sntrup761x25519-sha512), used automatically when the server supports them; Encrypt-then-MAC MACs offered by default again, so EtM-only servers such as AWS Transfer Family’s newer security policies connect with no special settings |
| 📦 PDF optimization | Pdf.Optimize: audit where a PDF's bytes go, then shrink it losslessly — recompression, deduplication, font subsetting, unused-object removal, object streams, old revisions dropped |
| 🖥️ TLS 1.3 server | A Chilkat TLS server (Socket.InitSslServer) now speaks TLS 1.3, negotiated automatically with no code change, including client certificate authentication. New server-side API: SNI certificate selection (AddSslServerCert), ALPN (AlpnProtocols) and session resumption (TlsTicketLifetimeSeconds, SetTlsTicketKey, TlsSessionResumed) for both TLS 1.2 and TLS 1.3 |
| ✍️ Code signing | CodeSign: Authenticode signatures for Windows Installer packages (.msi), PowerShell scripts, modules and data files (.ps1, .psm1, .psd1) and VBScript files (.vbs), with the same AddSignature, VerifySignature and RemoveSignature methods used for EXE and DLL files |
| 🔗 HttpCurl | HttpCurl runs more of the curl command line: -I, -G, --json, -b/-c cookies and cookie files, --digest/--anyauth, --oauth2-bearer, -m/--connect-timeout, --max-redirs, -z, -D/-i, combined flags such as -sSL; new ResponseHeader property and GetResponseHeaderField method; an unrecognized option is now an error instead of being silently dropped |
📑 Contents
- Chilkat2 Java — a new, idiomatic Java API
- Chilkat2 PHP — a new, idiomatic PHP extension
- Chilkat2 Perl — a new, idiomatic Perl module
- Chilkat for Lua — a new Lua C module
- Chilkat C++ over CBridge — one download for every C++ compiler
- Pdf.ExtractText — text, Markdown, search and tables
- Pdf.TextReplace — fill a PDF template
- Pdf.Watermark — add, detect, list, remove
- Pdf.Protection — encryption and permissions
- Pdf.Optimize — lossless size reduction
- Pdf.WriteFile, WriteBd, the in-memory "2" methods and PreserveSignatures
- SSH (Ssh, Scp, SFtp, SshTunnel) — strict key exchange, post-quantum key exchange, EtM MACs
- Socket TLS server — TLS 1.3, SNI certificate selection, ALPN, session resumption
- CodeSign — Authenticode signatures for MSI, PowerShell and VBScript files
- HttpCurl — variables in an AddOutput2 record spec and in the output file path
- HttpCurl — more curl options, response headers, and a stricter command parser
- StringBuilder.HtmlToMarkdown — convert HTML to Markdown
- .NET Core, .NET Standard and Mono — the DateTime members of the .NET Framework assembly
- Http DPoP — RSA and Ed25519 keys, and private_key_jwt client authentication
- PHP on Windows — the extension loads again into PHP 7.x and 8.0–8.3
- Hashtable — faster, leaner Swiss Table internals
- Http — cookies set without a Domain attribute are sent back again
- CodeSign — re-signing replaces the signature, stricter verification, and other fixes
- TLS — server certificate validation: cross-signed CA certificates, and the root must be trusted
- TLS — hostname verification: stricter matching of wildcard certificate names
- Cert — the certificate chain reported for a connection, and a cached VerifySignature result
- Socket TLS server — ECDSA server certificates work with TLS 1.2
- Crypt2, S/MIME and PDF signing — ECDSA signatures are always valid DER
- PKCS#11 smart cards and tokens — PIN-pad readers and tokens that only hash-and-sign
- Compression — the “gzip” algorithm now produces and reads gzip data
- Http DPoP — the htu and iat claims, and full-length EC coordinates in JWKs
✨ New Features & Additions
☕ Chilkat2 Java — a new, idiomatic Java API
Chilkat2 Java is new in v11.7.0: the com.chilkatsoft.chilkat2 package brings the full Chilkat API — all 99 classes — to Java with an API designed for Java. It relates to the classic Chilkat Java library (com.chilkatsoft: CkHttp, CkZip, …) the way Chilkat2-Python relates to CkPython: the same classes, members, and behavior, presented the way Java programmers expect. The classic library is unchanged and remains fully supported.
Installation
Download the archive for your operating system and the architecture of your JVM from the Chilkat2 Java page. Each archive contains chilkat2.jar, the native library (chilkat2java.dll, libchilkat2java.so, or libchilkat2java.dylib), and source and Javadoc jars for your IDE. Put the jar on the classpath and name the directory that holds the native library:
javac -cp chilkat2.jar MyApp.java
java -Dchilkat2.library.path=/opt/chilkat2 -cp .:chilkat2.jar MyApp
There is no System.loadLibrary call to make: the first Chilkat2 object you create loads the library, from -Dchilkat2.library.path, from inside your own jar (so an application can ship as one self-contained jar with the library for each platform it supports), or from java.library.path. Like the classic library, each download works with every JDK from Java 8 up: the jar is Java 8 bytecode and the native library uses only JNI 1.6. Chilkat2 Java is not published to a Maven repository; the download page shows how to install the jar into a local or organization repository for Maven and Gradle builds.
Platforms
- Windows: x64, x86 (32-bit), and ARM64 —
chilkat2-java-x64,chilkat2-java-win32,chilkat2-java-arm64. - Linux (glibc): x86_64, aarch64, and armv7l —
chilkat2-java-<arch>-linux. - Alpine Linux (musl): x86_64 and aarch64 —
chilkat2-java-<arch>-alpine. - macOS: Apple silicon and Intel —
chilkat2-java-arm64-macosx,chilkat2-java-x86_64-macosx.
A Java API
import com.chilkatsoft.chilkat2.ChilkatException;
import com.chilkatsoft.chilkat2.Global;
import com.chilkatsoft.chilkat2.Http;
import com.chilkatsoft.chilkat2.JsonObject;
public class Stars {
public static void main(String[] args) {
try (Global glob = new Global()) {
glob.unlockBundle("Anything for 30-day trial");
}
try (Http http = new Http(); JsonObject json = new JsonObject()) {
http.onPercentDone(pct -> { System.out.println(pct + "%"); return false; });
json.load(http.quickGetStr("https://api.github.com/repos/openjdk/jdk"));
System.out.println(json.intOf("stargazers_count") + " stars");
} catch (ChilkatException e) {
System.out.println(e.getMessage()); // "Http.quickGetStr failed: <reason>"
System.out.println(e.getLastErrorText()); // the full log of the failed call
}
}
}
- Failures are exceptions. A method that other languages report through a success/failure
bool, or through a string or object that is null on failure, throwsChilkatExceptioninstead — an unchecked exception whose message names the class, the method, and the reason, and whosegetLastErrorText()returns the full log of the failed call. The object stays usable. Lookups that simply find nothing (xml.findChild,json.objectOf, …) returnnullwithout throwing, and methods that answer a question (hasMember,tagEquals,hasPrivateKey, …) return a plainboolean. - Java naming. The class names are those of the other Chilkat APIs without the
Ckprefix (Http,Zip,JsonObject). Methods are camelCase (QuickGetStrisquickGetStr), and a propertyConnectTimeoutis the pairgetConnectTimeout()/setConnectTimeout(..), which Kotlin reads as the propertyconnectTimeout. - Lifetime. Every object is
AutoCloseable: try-with-resources (orclose()) frees the native object deterministically, and the garbage collector frees any object that is never closed. - Plain Java types. Strings are
Stringand binary data isbyte[]; there is noCkStringorCkByteData. - Events.
AbortCheck,PercentDone, andProgressInfoare delivered to lambdas installed withonAbortCheck,onPercentDone, andonProgressInfo, or to aProgressListener, on the thread that called the method. ReturningtruefromabortCheckorpercentDoneaborts the running method. - No async. The
*Asyncmethods and theTaskandTaskChainclasses are not included; use your own threads or executors. An object may be used from one thread at a time.
📝 Scope Chilkat2 Java is a new product; no other Chilkat product is affected. The classic Chilkat Java library (Chilkat Java) continues unchanged, and the two use different packages and native libraries, so both can be used in the same application. See the Chilkat2 Java page for the downloads and getting-started instructions, and the Chilkat2 Java reference documentation for the Java signature of every class member.
🐘 Chilkat2 PHP — a new, idiomatic PHP extension
Chilkat2 PHP is new in v11.7.0: the chilkat2 extension brings the full Chilkat API — all 99 classes — to PHP 8.1 and later, with an API designed for modern PHP. It relates to the classic Chilkat PHP extension (extension=chilkat, include "chilkat.php", CkHttp, …) the way Chilkat2-Python relates to CkPython: the same classes, members, and behavior, presented the way PHP programmers expect. The classic extension is unchanged and remains fully supported.
Installation
With Composer:
composer require chilkat/chilkat2
vendor/bin/chilkat2-install
chilkat2-install asks the PHP that runs it for its version, architecture, and thread safety, downloads the matching build of the extension, verifies its SHA-256, copies it into PHP's extension_dir, and enables it (phpenmod on Debian and Ubuntu; a chilkat2.ini in the conf.d directory on Alpine, RHEL/Fedora, Homebrew, and the official Docker images; an extension= line in php.ini on Windows). The package also brings stubs/chilkat2.stub.php, which declares every class, method, and property with types and PHPDoc for PhpStorm, Intelephense, PHPStan, and Psalm.
Or download the archive for your PHP from the Chilkat2 PHP page. Each contains the extension (chilkat2.so or php_chilkat2.dll), the stub, and a test.php; copy the extension into extension_dir and enable it in php.ini:
extension=chilkat2
Platforms
Each build is for one PHP version (8.1, 8.2, 8.3, 8.4, or 8.5) — 8.4 stands for the PHP version in the names below.
- Windows: x64 and x86 (32-bit), thread-safe and NTS —
chilkat2-php-8.4-x64,chilkat2-php-8.4-nts-x64,chilkat2-php-8.4-win32,chilkat2-php-8.4-nts-win32. No Visual C++ redistributable is needed. - Linux (glibc): x86_64 and x86 (PHP 8.1 and later), aarch64 and armv7l (PHP 8.2 and later), NTS and ZTS —
chilkat2-php-8.4-<arch>-linux,chilkat2-php-zts-8.4-<arch>-linux. - Alpine Linux (musl): x86_64 and aarch64, NTS —
chilkat2-php-8.4-<arch>-alpine. - macOS: Apple silicon and Intel, NTS (and ZTS for PHP 8.2 and later) —
chilkat2-php-8.4-arm64-macosx,chilkat2-php-8.4-x86_64-macosx.
A PHP API
<?php
use Chilkat2\GlobalSettings;
use Chilkat2\Http;
use Chilkat2\JsonObject;
(new GlobalSettings())->UnlockBundle('Anything for 30-day trial');
try {
$http = new Http(['ConnectTimeout' => 10]);
$http->onPercentDone(function (int $pct): bool { echo "$pct%\n"; return false; });
$json = new JsonObject();
$json->Load($http->QuickGetStr('https://api.github.com/repos/php/php-src'));
echo $json->IntOf('stargazers_count'), " stars\n";
} catch (\Chilkat2\Exception $e) {
echo $e->getClass(), '::', $e->getMethod(), " failed\n"; // "Chilkat2\Http::QuickGetStr failed"
echo $e->getMessage(), "\n"; // the full log of the failed call
}
- Failures are exceptions. A method that other languages report through a success/failure
bool, or through a string or object that is null on failure, throwsChilkat2\Exception(aRuntimeException) instead, the way PDO and mysqli do in PHP 8. ItsgetMessage()is the object'sLastErrorText, andgetClass()andgetMethod()name the call that failed. The object stays usable. Lookups that simply find nothing ($xml->FindChild(),$json->StringOf(), …) returnnullwithout throwing, and methods that answer a question (HasMember,TagEquals,HasPrivateKey, …) return a plainbool. Prefer return values? Create the object with['RaiseError' => false]and failures returnfalseornull, with the reason in$obj->LastErrorText; objects a method returns inherit the setting. - PHP naming and types. Classes live in the
Chilkat2namespace and drop theCkprefix (Chilkat2\Http,Chilkat2\Zip,Chilkat2\DateTime;GlobalisGlobalSettings). Properties are real, typed PHP properties ($http->ConnectTimeout = 10;), read-only where Chilkat's are, and can be set in the constructor. Methods keep their Chilkat names and declare their parameter and return types, sostrict_types, named arguments, and reflection work. Wrong argument types, unknown properties, and other misuse throw PHP's ownTypeError,ValueError, orError. - Plain PHP strings. Strings are PHP strings holding UTF-8, with no
put_Utf8(true)to call; there is noCkStringorCkByteData, and nochilkat.phpto include. - Lifetime. An object is freed when its last reference goes, like any PHP object; there is nothing to close.
- Events.
AbortCheck,PercentDone, andProgressInfoare delivered to closures (or any callable) installed withonAbortCheck,onPercentDone, andonProgressInfo; passingnullremoves one. Returningtruefrom anAbortCheckorPercentDonehandler aborts the running method, and an exception thrown in a handler aborts it and comes out of the method call. - No async. The
*Asyncmethods and theTaskandTaskChainclasses are not included.
📝 Scope Chilkat2 PHP is a new product; no other Chilkat product is affected. The classic Chilkat PHP extension (Chilkat PHP, Composer package chilkat/chilkat) continues unchanged, and the two can be installed side by side. See the Chilkat2 PHP page for the downloads and getting-started instructions, and the Chilkat2 PHP reference documentation for the PHP signature of every class member.
🐪 Chilkat2 Perl — a new, idiomatic Perl module
Chilkat2 Perl is new in v11.7.0: the Chilkat2 module brings the full Chilkat API — all 99 classes — to Perl 5.26 and later, with an API designed for Perl. It relates to the classic Chilkat Perl module (use chilkat();, chilkat::CkHttp->new(), get_/put_ property methods) the way Chilkat2-Python relates to CkPython: the same classes, members, and behavior, presented the way Perl programmers expect. The classic module is unchanged and remains fully supported.
Installation
With one command, into the perl that runs it:
curl -fsSL https://chilkatdownload.com/perl/install-perl2 | perl - # Linux, Alpine Linux, macOS
curl.exe -fsSL https://chilkatdownload.com/perl/install-perl2 | perl - # Windows (Strawberry Perl)
The installer reads the Perl version, CPU architecture, threading, and C library from that perl, downloads the matching build, installs it, and checks that it loads; nothing is compiled. perl - --dry-run shows what it would do, and perl - --local installs into ~/perl5 without root. With cpanm, the Chilkat2 distribution comes from Chilkat's own repository and installs the same prebuilt module through MakeMaker:
cpanm --mirror https://chilkatdownload.com/cpan --mirror-only Chilkat2
Or download the archive for your perl from the Chilkat2 Perl page. Each contains the module (Chilkat2.pm and Chilkat2.so, Chilkat2.bundle, or Chilkat2.dll), the POD for every class, and a test.pl; install it with perl Makefile.PL && make install (gmake on Windows), or run from the extracted folder with perl -Ilib.
Platforms
Each build is for one Perl version and, except on Windows, for non-threaded or thread-multi perls — 5.40 stands for the Perl version in the names below. A build for another perl refuses to load and says why.
- Windows (Strawberry Perl): 64-bit for Perl 5.30, 5.32 and 5.36 to 5.42, and 32-bit for 5.30 and 5.32 —
chilkat2-perl-5.40-x86_64-mingw32,chilkat2-perl-5.32-x86-mingw32. TheChilkat2.dllis code-signed and needs no runtime redistributable. - Linux (glibc): x86_64 and aarch64, Perl 5.26 to 5.44, thread-multi (the distributions' system perl) and non-threaded (perlbrew, plenv) —
chilkat2-perl-thread-multi-5.40-<arch>-linux,chilkat2-perl-5.40-<arch>-linux. - Alpine Linux (musl): x86_64 and aarch64, thread-multi and non-threaded —
chilkat2-perl-thread-multi-5.40-<arch>-alpine,chilkat2-perl-5.40-<arch>-alpine. - macOS: Apple silicon (Perl 5.34 and later) and Intel, thread-multi (Homebrew) and non-threaded —
chilkat2-perl-thread-multi-5.40-arm64-macosx,chilkat2-perl-5.40-x86_64-macosx.
A Perl API
use strict;
use warnings;
use Chilkat2;
Chilkat2::Global->new->UnlockBundle('Anything for 30-day trial');
my $http = Chilkat2::Http->new(ConnectTimeout => 10);
$http->on(PercentDone => sub { my ($pct) = @_; print "$pct%\n"; return 0 });
my $text = $http->QuickGetStr('https://api.github.com/repos/Perl/perl5');
if (!defined $text) {
print $http->LastErrorText, "\n"; # why it failed
exit 1;
}
my $json = Chilkat2::JsonObject->new(RaiseError => 1);
eval { $json->Load($text); 1 } or die "failed in ", $@->method, ": $@";
print $json->IntOf('stargazers_count'), " stars\n";
- Perl naming. Classes live in the
Chilkat2::namespace and drop theCkprefix (Chilkat2::Http,Chilkat2::Zip,Chilkat2::DateTime,Chilkat2::Global). Each property is one accessor:$http->ConnectTimeoutreads it and$http->ConnectTimeout(10)sets it (and returns the object, so setters chain); properties can also be set innew. Methods keep their Chilkat names. - Failures return false, or die if you ask. As in DBI, a failed call returns false (or
undef) and leaves the reason inLastErrorText. Create the object withRaiseError => 1(or set$obj->RaiseError(1)) and failures die instead with aChilkat2::Error, which stringifies to theLastErrorTextand hasclass,method, andobjectaccessors; objects a method returns inherit the setting. Lookups that simply find nothing ($xml->FindChild,$json->StringOf, …) returnundefand never die. - Character strings. Strings go in and out as Perl character strings, so
length, regular expressions, and:encodinglayers work withoutdecode; binary data is byte strings (Chilkat2::BinData). There is noCkStringorCkByteData. - Misuse croaks. A wrong argument count, a reference where a string or number is expected, a wide character where bytes are expected, a misspelled property in
new, or setting a read-only property croaks with the caller's file and line instead of being silently ignored. - Events.
AbortCheck,PercentDone, andProgressInfoare delivered to closures installed with$obj->on(Event => sub {...}, ...);undefremoves one. Returning a true value from anAbortCheckorPercentDonehandler aborts the running method, and adiein a handler aborts it and is re-thrown from the method call. - Documentation and lifetime. Every class has POD (
perldoc Chilkat2::Http). An object is freed when its last reference goes; there is nothing to close. - No async. The
*Asyncmethods and theTaskandTaskChainclasses are not included.
📝 Scope Chilkat2 Perl is a new product; no other Chilkat product is affected. The classic Chilkat Perl module (Chilkat Perl, use chilkat();) continues unchanged, and the two are separate modules that can be installed side by side. See the Chilkat2 Perl page for the downloads and getting-started instructions, and the Chilkat2 Perl reference documentation for the Perl signature of every class member.
🌙 Chilkat for Lua — a new Lua C module
Chilkat for Lua is new in v11.7.0: the chilkat module brings the full Chilkat API — all 99 classes — to Lua 5.1 (and LuaJIT 2.x), Lua 5.4 and Lua 5.5, with an API designed for Lua. It is a native C module (chilkat.so, or chilkat.dll on Windows) loaded with require; nothing is compiled when you install it.
Installation
With one command, into the Lua you choose:
curl -fsSL https://chilkatdownload.com/lua/install.sh | sh # Linux, Alpine Linux, macOS
irm https://chilkatdownload.com/lua/install.ps1 | iex # Windows PowerShell
The installer asks the interpreter for its version and package.cpath, detects the platform (and, on Windows, the interpreter's architecture), downloads the matching build, copies the module into place, and checks that require "chilkat" works. --lua=PATH (-Lua) picks the interpreter, --dest=DIR (-Dest) the directory, and --dry-run (-DryRun) shows what it would do. With LuaRocks, the binary rocks come from Chilkat's own rocks server, one per Lua version:
luarocks install chilkat --server=https://chilkatdownload.com/luarocks/lua5.4
(lua5.1, lua5.4 or lua5.5; on Alpine Linux lua5.4-musl and so on.) Or download the archive for your Lua and platform from the Chilkat for Lua page. Each contains the module, types/chilkat.lua (LuaLS annotations: completion and type checking in editors that use lua-language-server) and a test.lua.
Platforms
A Lua C module is built for one Lua version line, so each build is for one Lua ABI — 5.1 (also LuaJIT and OpenResty), 5.4 or 5.5 — and one platform. Lua 5.2 and 5.3 are not built.
- Windows: 32-bit, 64-bit and ARM64 —
chilkat-lua5.4-win32,chilkat-lua5.4-x64,chilkat-lua5.4-arm64. Thechilkat.dllis code-signed, needs no runtime redistributable, and imports the Lua C API fromlua51.dll,lua54.dllorlua55.dll, so it works with any Lua build that has one (MSVC or MinGW, LuaJIT included). - Linux (glibc): x86_64, aarch64 and armv7l —
chilkat-lua5.4-x86_64-linux,chilkat-lua5.4-aarch64-linux,chilkat-lua5.4-armv7l-linux. - Alpine Linux (musl): x86_64 and aarch64 —
chilkat-lua5.4-x86_64-alpine,chilkat-lua5.4-aarch64-alpine. - macOS: Apple silicon and Intel —
chilkat-lua5.4-arm64-macosx,chilkat-lua5.4-x86_64-macosx.
A Lua API
local chilkat = require("chilkat")
assert(chilkat.unlock_bundle("Anything for 30-day trial"))
local http = chilkat.Http.new()
http.connect_timeout = 10
http:set_event_handler({ percent_done = function(pct) print(pct .. "%") end })
local text, err = http:quick_get_str("https://api.github.com/repos/lua/lua")
if not text then
print(err) -- why it failed (http.last_error_text)
return
end
local json = chilkat.JsonObject.new()
assert(json:load(text))
print(json:int_of("stargazers_count") .. " stars")
- Lua naming. Classes are fields of the module and drop the
Ckprefix (chilkat.Http,chilkat.Zip,chilkat.DateTime);chilkat.Http.new()creates an object. Members keep Chilkat's names in snake_case:QuickGetStrishttp:quick_get_str(url)andConnectTimeoutis the fieldhttp.connect_timeout. - Failures return
nil, err. As withio.open, a method that fails returnsnilfollowed by the object'slast_error_text, soassert(obj:method(...))raises an error with that text. Methods that answer a question (json:has_member,zip:entry_of) returntrueorfalse, and numbers are returned as they are. - Misuse raises an error. A wrong argument type, a missing argument, an out-of-range integer, an object of the wrong class, an unknown property name, or a write to a read-only property raises a Lua error instead of being silently ignored.
- Strings and bytes. Strings go in and out as UTF-8; binary data is Lua strings (
bd:get_data()). There is noCkStringorCkByteData. Indexes are 0-based, as in every Chilkat API. - Events.
abort_check,percent_doneandprogress_infoare delivered to functions installed withobj:set_event_handler({...});set_event_handler(nil)removes them. A true return fromabort_checkorpercent_doneaborts the running method, and an error raised in a handler aborts it and is raised again from the method call. - Lifetime. Objects are garbage collected;
obj:dispose()frees one at once, and in Lua 5.4 and laterlocal http <close> = chilkat.Http.new()frees it when the block ends. - No async. The
*Asyncmethods and theTaskandTaskChainclasses are not included.
📝 Scope Chilkat for Lua is a new product; no other Chilkat product is affected. See the Chilkat for Lua page for the downloads and getting-started instructions, the Chilkat Lua reference documentation for the Lua signature of every class member, and the Chilkat Lua examples.
🧩 Chilkat C++ over CBridge — one download for every C++ compiler
Chilkat C++ over CBridge is new in v11.7.0: the Chilkat C++ API — the same classes and the same header files as the Chilkat C/C++ libraries — as a small layer of C++ source that you compile with your own compiler and that forwards every call to the Chilkat C bridge shared library. Because the library has a plain C interface, one library per platform works with every compiler, compiler version and C++ runtime; you no longer need a Chilkat static library built for your exact toolchain (or your _GLIBCXX_USE_CXX11_ABI setting). Existing Chilkat C++ programs compile unchanged.
Installation
Download chilkat-cpp-bridge.zip from the Chilkat C++ over CBridge page and unzip it. There is nothing to install or register, and no project or CMake files: add include and src to your include path, compile src/ChilkatCppBridgeAll.cpp (it includes every wrapper source), link the library for your platform, and ship that one library file beside your executable:
:: Visual C++
cl /EHsc /I include /I src myapp.cpp src\ChilkatCppBridgeAll.cpp lib\windows-x64\chilkat_c_bridge_x64.lib
# Linux
g++ -I include -I src myapp.cpp src/ChilkatCppBridgeAll.cpp \
-Llib/linux-x86_64 -lchilkat_c_bridge_x86_64 -Wl,-rpath,'$ORIGIN' -o myapp
The wrapper sources are portable C++98: no STL, exceptions or RTTI required, and no configuration macros. The README.md in the download has the commands for every toolchain.
Platforms and compilers
- Windows: 64-bit, 32-bit and ARM64 —
chilkat_c_bridge_x64.dll,chilkat_c_bridge_win32.dllandchilkat_c_bridge_arm64.dll, code-signed, each with an import library. Visual C++ (any version) links the.lib; MinGW-w64 links the DLL directly; C++Builder'sbcc64xlinks the.lib, andbcc32c,bcc32andbcc64use an import library made withimplib -aormkexp. - Linux (glibc 2.27 or newer): x86_64, x86, arm (armv7l) and arm64 —
libchilkat_c_bridge_<arch>.so, for any GCC or Clang. - Alpine Linux (musl): x86_64 and arm64.
- macOS: one universal (Apple silicon and Intel), code-signed
libchilkat_c_bridge.dylibwith the install name@rpath/libchilkat_c_bridge.dylib.
The same C++ API
#include <stdio.h>
#include "CkGlobal.h"
#include "CkHttp.h"
#include "CkJsonObject.h"
int main()
{
CkGlobal glob;
if (!glob.UnlockBundle("Anything for 30-day trial")) { printf("%s\n", glob.lastErrorText()); return 1; }
CkHttp http;
http.put_ConnectTimeout(10);
const char *text = http.quickGetStr("https://api.github.com/repos/llvm/llvm-project");
if (!text) { printf("%s\n", http.lastErrorText()); return 1; }
CkJsonObject json;
json.Load(text);
printf("%d stars\n", json.IntOf("stargazers_count"));
return 0;
}
- Source compatible. The headers are the Chilkat C/C++ library headers, byte for byte, and every member is implemented: programs written for the static libraries compile unchanged.
CkStringandCkByteDataresults are produced by the library itself, so encodings and charsets are exactly those of the static libraries. - Wide-character classes (
CkHttpW,CkStringBuilderW, …) are included. The UTF-16Ck*Uclasses, and the Windows-onlyCkRegistryandCkService, are not. - Events. The
CkBaseProgresscallbacks —AbortCheck,PercentDone,ProgressInfoandTaskCompleted— are delivered. The class-specific progress callbacks (CkZipProgress,CkMailManProgress,CkHttpProgress,CkFtp2Progress,CkSFtpProgress,CkTarProgress) are not called; overrides of them still compile. These callbacks will be removed from Chilkat in a future major version. - Asynchronous methods work as in the static libraries;
TaskCompletedruns after the task's final status is set, so it may arrive afterCkTask::Wait()returns. - Matching versions. The wrapper checks the library's interface version at run time; with a library from another Chilkat version no Chilkat object is created and
lastErrorText()says why. On 32-bit Windows the wrapper requires the cdeclchilkat_c_bridge_win32.dllshipped with it. - Windows: as with the static libraries, include
<windows.h>(if at all) before any Chilkat header.
📝 Scope Chilkat C++ over CBridge is a new product. The Chilkat C/C++ static libraries (C/C++ Library Downloads) continue unchanged; a program uses one or the other, not both. The C bridge library gains the functions the C++ API needs; Chilkat for Lazarus and Chilkat for Dart, which use the same library, are unaffected. See the Chilkat C++ over CBridge page for the download, the Chilkat C++ reference documentation (the API is identical) and the Chilkat C++ examples.
📄 Pdf.ExtractText — text, Markdown, search and tables
ExtractText(op, jsonIn, jsonOut) returns a PDF's text in four forms and rates how trustworthy it is. The op is text (plain text as flowing paragraphs, visual lines, or a layout-preserving grid), markdown (headings, emphasis, lists, links, code, ruled tables, image placeholders and page markers — for AI and search pipelines), search (a string or regular expression, with page and context for each match), tables (rows and cells with spans), or assess (only the quality report). Scope is set by page and, optionally, by a label in the text ("label": "Invoice Number" returns the value after it; labelValue selects the rest of the line, the block below, or a whole section). Filters decide whether artifacts, repeated headers and footers, invisible OCR layers, clipped, off-page, obscured and rotated text, form field values and annotations are included; clean-up covers ligatures, hyphenation, ActualText, bidi and Unicode normalization.
jsonIn.UpdateString("label", "Invoice Number");
pdf.ExtractText("text", jsonIn, jsonOut);
string invoiceNo = jsonOut.StringOf("text"); // "INV-2026-0412"
string rating = jsonOut.StringOf("quality.rating"); // good | fair | poor | none
string ocr = jsonOut.StringOf("quality.pagesNeedingOcr"); // e.g. "4,9"
Every call returns a quality object: a rating for the document and each page, the text source (real text, an OCR layer, image only), Unicode coverage, unmapped fonts, the reading-order source, columns detected, and machine-readable issue codes. ExtractTextSb writes large text or Markdown into a StringBuilder. Extraction is read-only and, on an encrypted PDF, requires owner rights or the copy permission (except assess). Chilkat does not perform OCR.
📖 Details Every option, with JSON examples for each operation: PDF Text Extraction (ExtractText). Reference: Pdf.ExtractText.
✏️ Pdf.TextReplace — fill a PDF template
TextReplace(op, jsonIn, jsonOut) replaces {{placeholders}} and the values after labels such as Invoice #: directly in a PDF's page content. The new text is drawn with the font and size of the text it replaces, so a font rule applies: a standard-14 font can draw what its encoding has, an embedded font what it has glyphs for. op = "check" finds every placeholder and label and reports, per field, whether the replacement is possible and why not (missingGlyph, notInEncoding, nonStandardUnembeddedFont, placeholderSpansFonts, ...) without changing anything; op = "apply" makes every replacement or none. Options cover the placeholder delimiters, which label occurrence to replace, alignment (keep the left edge, the right edge, or the center, or decide automatically), and what to do with unused placeholders and unmatched values. Placeholders found in metadata, link targets or form fields are reported rather than changed.
jsonIn.UpdateString("placeholders.customer", "Acme Corp");
jsonIn.UpdateString("placeholders.total", "$1,234.56");
jsonIn.UpdateString("labels[0].label", "Invoice #:");
jsonIn.UpdateString("labels[0].value", "INV-2026-0412");
if (pdf.TextReplace("apply", jsonIn, jsonOut)) pdf.WriteFile("invoice_0412.pdf");
else Console.WriteLine(jsonOut.Emit()); // "problems": [ { "field": ..., "reason": ..., "chars": ... } ]
An applied replacement is saved as a full rewrite of the file, so the old values do not survive in an earlier revision. On a signed PDF, apply is refused while PreserveSignatures is true.
📖 Details The font rule, every option and problem code, with JSON examples: PDF Text Replacement (TextReplace). Reference: Pdf.TextReplace.
💧 Pdf.Watermark — add, detect, list, remove
Watermark(op, jsonIn, jsonOut) covers the whole watermark lifecycle. add places text (with {page}, {pages}, {date:FORMAT} and {filename} placeholders), a JPEG or PNG image, or a page of another PDF (a letterhead) on the selected pages, with a full set of options: standard-14 or embedded fonts, fill, outline and invisible rendering, color, opacity, rotation (including diagonal), blend mode, 9-position alignment with margins and offsets, fit-to-page scaling, tiling, a background box and border, and a link over the stamp. The approach decides how it is stored: a marked-content artifact (the default; removable, skipped by text extraction), an annotation (always on top; the one approach allowed on a signed PDF), an optional-content layer (can be hidden or print-only), or plain page content (tamper-resistant, not removable). detect, list and remove work on Chilkat's own watermarks, Acrobat's, Watermark annotations and tagged artifacts from any producer, filtered by page, approach, source, id, label or text.
jsonIn.Load("{ \"type\": \"text\", \"text\": \"CONFIDENTIAL\", \"id\": \"conf\", \"color\": \"#C00000\", " +
" \"opacity\": 0.25, \"rotation\": \"diagonal\", \"scale\": { \"fit\": \"page\", \"fitPercent\": 80 } }");
pdf.Watermark("add", jsonIn, jsonOut); // jsonOut: { "id": "conf", "pages": "1-12" }
pdf.WriteFile("confidential.pdf");
jsonIn.Load("{ \"source\": \"acrobat\" }");
pdf.Watermark("remove", jsonIn, jsonOut); // jsonOut: { "removed": 1 }
📖 Details Every option, the filter and the list report, with JSON examples: PDF Watermarks (Watermark). Reference: Pdf.Watermark.
🔒 Pdf.Protection — encryption and permissions
Protection(op, jsonIn, jsonOut) encrypts a PDF, changes or removes its encryption, and reports its security. encrypt takes the method (password, or certificate for recipients added with AddEncryptCert), the algorithm (rc4-40, rc4-128, aes-128, aes-256-r5, aes-256 — the default, revision 6 — or aes-256-gcm), the scope (all, allExceptMetadata, attachmentsOnly) and the permissions (print quality, modify, copy, accessibility, annotate, fill forms, assemble; changeSecurity per certificate recipient); the passwords come from the UserPassword and OwnerPassword properties. removeEncryption, removeRestrictions, changePasswords and changePermissions do what they say; getInfo reports the security the PDF was loaded with and the security it will be saved with. Every change needs owner rights — Chilkat does not ignore owner restrictions — and takes effect when the PDF is written. PreLoadCheck / PreLoadCheckBd report a file's encryption method, algorithm, whether a password is needed, its permissions or its certificate recipients before loading; SetDecryptCert opens a certificate-encrypted PDF.
pdf.UserPassword = "open-me";
pdf.OwnerPassword = "admin-secret";
jsonIn.UpdateString("permissions.print", "lowQuality");
jsonIn.UpdateBool("permissions.copy", false);
pdf.Protection("encrypt", jsonIn, jsonOut); // AES-256 revision 6 by default
pdf.WriteFile("protected.pdf"); // PDF 1.7, Adobe extension level 8
📖 Details Every operation and option, certificate encryption and PreLoadCheck, with JSON examples: PDF Encryption and Permissions (Protection). Reference: Pdf.Protection.
📦 Pdf.Optimize — lossless size reduction
Optimize(op, jsonIn, jsonOut) measures and shrinks a PDF without changing how any page looks. audit reports where the file's bytes go (images, fonts, page content, forms, annotations, structure tree, metadata, embedded files, old revisions, overhead) and what each option would save; optimize applies the lossless preset or any selection of switches: Flate recompression and legacy-filter replacement, PNG predictors for images, duplicate stream and object removal, unused objects, resources and layers, content-stream coalescing and tidying, TrueType font subsetting, object streams and dropping old incremental revisions. JPEG, JPEG 2000, JBIG2 and CCITT data are never re-encoded. The report lists what was done and the bytes saved per option, and anything skipped with the reason. PDF/A rules are respected, signed PDFs are refused while PreserveSignatures is true, and encrypted PDFs keep their security.
pdf.Optimize("audit", jsonIn, jsonOut);
if (jsonOut.IntOf("estimatedSavings.total") > jsonOut.IntOf("fileSize") / 10) {
pdf.Optimize("optimize", jsonIn, jsonOut);
pdf.WriteFile("out.pdf");
}
📖 Details Every option and both reports, with JSON examples: PDF Optimization (Optimize). Reference: Pdf.Optimize.
💾 Pdf.WriteFile, WriteBd, the in-memory "2" methods and PreserveSignatures
The new PDF features change the loaded document in memory; WriteFile(path) and WriteBd(bd) write it with every pending change. When nothing requires more, the PDF is written as an incremental update that leaves existing signatures valid; an Optimize, a security change or an applied TextReplace requires a full rewrite. The new PreserveSignatures property (default true) governs signed PDFs across the features: while it is true, operations that would invalidate signatures (TextReplace apply, Watermark adds other than annotations and all removals, Protection changes, Optimize) are refused with the reason in LastErrorText; set it to false to allow them and accept that the signatures become invalid.
AddEmbeddedFiles2(json) is AddEmbeddedFiles without the output file: it takes the same JSON (the files to embed, with their descriptions, subtypes and AFRelationship), adds them to the loaded PDF in memory, and leaves the writing to WriteFile or WriteBd — so attachments can be combined with a watermark, encryption or optimization in one pass, or followed by SignPdf. The embedded files are part of the document as soon as the call returns (NumEmbeddedFiles, GetEmbeddedFileInfo).
jsonIn.UpdateString("files[0].localFilePath", "invoice.xml");
jsonIn.UpdateString("files[0].description", "Invoice data");
jsonIn.UpdateString("files[0].AFRelationship", "Data");
pdf.AddEmbeddedFiles2(jsonIn);
pdf.Protection("encrypt", jsonProtect, jsonOut);
pdf.WriteFile("invoice_with_data.pdf");
SignPdf2(json) is likewise SignPdf without the output file: the same JSON options, but the signature is added to the loaded PDF in memory and the writing is left to WriteFile or WriteBd. The signature is part of the document as soon as the call returns (NumSignatures, VerifySignature), so a second call adds another signature or a DocTimeStamp on top of it. Changes pending from other methods are committed before the signature is added, so it covers the document's current state; the signature itself is an incremental update that leaves earlier signatures valid. (SignPdf and SignPdfBd are unchanged: they write the signed PDF and leave the object as it was.) In the same way, AddVerificationInfo2(json) adds the LTV verification information (the DSS with the certificates, OCSP responses and CRLs of the existing signatures) to the loaded PDF in memory, where AddVerificationInfo writes it to a file; and UpdateMetadata2(sb) replaces or adds the XMP metadata of the loaded PDF, where UpdateMetadata and UpdateMetadataBd also write the result.
pdf.Watermark("add", jsonWm, jsonOut);
pdf.AddEmbeddedFiles2(jsonFiles);
pdf.SignPdf2(jsonSig);
pdf.AddVerificationInfo2(jsonEmpty);
pdf.WriteFile("signed.pdf");
🔐 SSH (Ssh, Scp, SFtp, SshTunnel) — strict key exchange, post-quantum key exchange, EtM MACs
These changes apply to all SSH connections in Chilkat: Ssh, Scp, SFtp and SshTunnel. They all use the same SSH transport, and none of the changes needs any code change.
- Strict key exchange (
kex-strict-c-v00@openssh.com): the standard countermeasure for the Terrapin attack (CVE-2023-48795). It is used automatically when the server supports it. - Post-quantum hybrid key exchange:
mlkem768x25519-sha256andsntrup761x25519-sha512. These are preferred automatically when the server supports them. - Encrypt-then-MAC (EtM) MACs offered by default again. Some servers now accept only EtM MACs, AWS Transfer Family’s newer security policies for example. They will connect with no special settings. When a server doesn’t support strict key exchange, Chilkat still avoids the combination that Terrapin can exploit (a CBC cipher with an EtM MAC).
📝 Scope Strict key exchange only protects a connection when both sides support it. Applications that set UncommonOptions to +ssh-hmac-etm (the setting previously needed for EtM-only servers) can keep it; it still places the EtM MACs first. To turn a feature off, for example for a server that mishandles it, add NoSshStrictKex, NoSshPqKex or NoSshEtm to UncommonOptions.
🖥️ Socket TLS server — TLS 1.3, SNI certificate selection, ALPN, session resumption
A Chilkat TLS server now speaks TLS 1.3. A server is started as before — InitSslServer, BindAndListen, AcceptNext — and TLS 1.3 is negotiated automatically with clients that support it. No code change and no new setting is needed. Clients that do not offer TLS 1.3 continue to negotiate TLS 1.2 or lower exactly as before. The Chilkat TLS client has supported TLS 1.3 since v9.5.0.83; this release brings the server side to the same level.
Server-side TLS 1.3 includes the TLS 1.3 cipher suites (TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384 and TLS_CHACHA20_POLY1305_SHA256), X25519 and the NIST curves for key exchange, HelloRetryRequest when the client’s first key share is not one the server accepts, and client certificate authentication. SslProtocol works as it does for a client, so a server can be restricted to TLS 1.3 with "TLS 1.3", or allow 1.2 and later with "TLS 1.2 or higher".
Three capabilities arrive with it, each with new public API. They apply to TLS 1.2 as well as TLS 1.3 unless noted.
Serving more than one hostname (SNI)
A listening socket can present a different certificate for each hostname a client asks for, so one listener can serve several sites. The certificate is chosen after the client’s hello is read, by exact name first, then by a leading wildcard, then by a default.
CkSocket listener;
listener.AddSslServerCert(certA,"www.example.com"); // exact name
listener.AddSslServerCert(certB,"*.api.example.com"); // one label, so a.api.example.com but not a.b.api.example.com
listener.InitSslServer(certDefault); // used when nothing matches
listener.BindAndListen(443,5);
CkSocket sock;
if (listener.AcceptNext(20000,sock)) {
const char *requested = sock.sniHostname(); // the hostname this client asked for
}
AddSslServerCert may be called before or after InitSslServer, and while the socket is already listening. Set RequireSniMatch to refuse a connection whose requested hostname matches nothing, instead of serving the default certificate.
Application protocol negotiation (ALPN)
A server can now negotiate ALPN, and a client can offer more than one protocol. The new AlpnProtocols property holds the list as JSON, in preference order; the existing AlpnProtocol property still sets or reads a single protocol id.
listener.put_AlpnProtocols("[\"h2\",\"http/1.1\"]"); // what this server supports, most preferred first
// ... on the accepted socket:
const char *agreed = sock.alpnProtocol(); // "h2", or "" if ALPN was not negotiated
The server chooses the first protocol on its own list that the client also offered. A server that leaves the list empty ignores ALPN, as before. A server that sets a list and shares no protocol with the client refuses the connection with the no_application_protocol alert, as RFC 7301 requires.
Session resumption
A Chilkat TLS server now issues session tickets, and resumes sessions from them. A resumed handshake skips the certificate exchange and the server’s signature, so it is both smaller and faster than a full handshake. This works for TLS 1.2 (RFC 5077 session tickets) and TLS 1.3 (RFC 8446 pre-shared keys), with a Chilkat client or any other.
Nothing needs to be configured: a ticket key is generated automatically, and resumption works for the life of the listener. SetTlsTicketKey is only needed when tickets must stay valid across processes or after a restart — give every server instance the same 32-byte key. Calling it again with a different key rotates: tickets issued under the previous key are still accepted until they expire. Set TlsTicketLifetimeSeconds to 0 to issue no tickets at all.
Read TlsSessionResumed on an accepted socket, or on a client socket after Connect, to see whether a connection was resumed. On a resumed connection no certificates are exchanged; on the server, the client certificate from the original handshake is restored, so GetRcvdClientCert still returns it.
New API
| Member | Kind | Purpose |
|---|---|---|
AddSslServerCert(cert, hostname) NEW | method | Add a server certificate to present when the client’s SNI hostname matches. Exact name, leading wildcard, or an empty hostname for a default. |
ClearSslServerCerts() NEW | method | Remove every certificate added by AddSslServerCert. The InitSslServer certificate is unaffected. |
RequireSniMatch NEW | property (bool) | Refuse a connection whose SNI hostname matches no certificate, with the unrecognized_name alert, instead of serving the default. Default false. |
AlpnProtocols NEW | property (string) | The ALPN protocol list as JSON, in preference order. What a server supports, or what a client offers. |
SetTlsTicketKey(binKey) NEW | method | Set the 32-byte key used to encrypt session tickets, so tickets survive a restart or are shared between server instances. Calling it again rotates the key. |
TlsTicketLifetimeSeconds NEW | property (int) | How long an issued session ticket stays valid. Default 7200 (2 hours), maximum 604800 (7 days). 0 issues no tickets. |
TlsSessionResumed NEW | property (bool, read-only) | Whether this connection was resumed from a session ticket rather than fully handshaked. |
AlpnProtocol | property (string) | Now returns the negotiated protocol after a handshake. Setting it still sets a single protocol id. |
SniHostname | property (string) | On an accepted socket, now the hostname the client asked for. On a client, still the name to send. |
📝 Compatibility An existing TLS server keeps working unchanged. TLS 1.3 is negotiated only with clients that ask for it, the SNI and ALPN features do nothing until a certificate or protocol list is added, and session tickets only change what a server offers a client that supports resumption. The one behavior change to note is that AlpnProtocol now returns the negotiated protocol after a handshake; for a connection that negotiated the protocol that was set — which is every case before v11.7.0, because only one protocol could be offered — the value is unchanged.
✍️ CodeSign — Authenticode signatures for MSI, PowerShell and VBScript files
CodeSign can now sign, verify and remove Authenticode signatures on Windows Installer packages, PowerShell files and VBScript files, in addition to EXE and DLL files. The same methods are used for every file type — AddSignature, VerifySignature, RemoveSignature and GetSignerCert — and no new settings are needed: the file type is recognized automatically.
| File type | Extensions | Where the signature is stored |
|---|---|---|
| Windows executables and DLLs | .exe, .dll and other PE files such as .sys and .ocx | The PE certificate table, as before |
| Windows Installer packages NEW | .msi | The package’s digital signature stream |
| PowerShell scripts, modules and data files NEW | .ps1, .psm1, .psd1 | A # SIG # Begin signature block comment block appended to the file; the file’s text encoding (UTF-8, UTF-8 with BOM, or UTF-16) is kept |
| VBScript files NEW | .vbs | A '' SIG '' Begin signature block comment block appended to the file |
Chilkat computes each file’s digest with the Windows component for that file type, the same one Microsoft’s signtool uses, so the signed files verify with signtool verify and PowerShell’s Get-AuthenticodeSignature. The JSON options of AddSignature (hash algorithm, timestamp authority) and certificates on USB tokens and smart cards work as they do for EXE and DLL files. RemoveSignature removes the signature block from a script and the signature from an MSI package. Any other file type, for example .js, .cab or .cat, is rejected with a “not supported” error and the file is left unchanged.
CkCodeSign codeSign;
CkJsonObject options;
options.UpdateString("hashAlg","sha256");
options.UpdateBool("timestampToken.enabled",true);
options.UpdateString("timestampToken.tsaUrl","http://timestamp.example.com");
// cert is a CkCert with access to its private key (PFX, Windows certificate store, USB token, ...)
bool success = codeSign.AddSignature("C:/build/setup.msi",cert,options); // or a .ps1, .psm1, .psd1, .vbs, .exe or .dll
⚠️ MSI and ECDSA certificates Windows does not accept an Authenticode signature on an MSI package when the signing certificate has an ECDSA key: signtool verify and Get-AuthenticodeSignature report the signature as invalid (TRUST_E_BAD_DIGEST, 0x80096010). This is a limitation of signed MSI files on Windows, not a Chilkat defect: an MSI signed by Microsoft’s signtool with the same certificate is rejected in the same way, and its signed content is identical to the signature Chilkat produces. Use an RSA code-signing certificate for MSI packages. ECDSA certificates work for EXE, DLL, PowerShell and VBScript files.
📝 Scope Windows only, as for all of CodeSign. Signing and verifying EXE and DLL files works as before, apart from the fixes described under CodeSign fixes.
🔗 HttpCurl — variables in an AddOutput2 record spec and in the output file path
The arrayPath, wherePath, whereValue and subPath passed to HttpCurl.AddOutput2 can now contain variable names enclosed in {{ and }}. Variables could already be used in the path, the query parameters and the body of a curl command. Now they can also say which record an output is taken from, so the record no longer has to be identified by a value the application knows in advance.
Each of the four is stored exactly as it was passed, and the references in it are resolved every time that output is evaluated — not when AddOutput2 was called. The variable’s current value is therefore the one used, whether it was set by SetVar or produced as the output of another curl function. A reference can be the whole value or part of a longer string, such as result.{{list_name}}.items.
CkHttpCurl httpCurl;
httpCurl.SetVar("drive_name","Shared Documents");
// A function that lists a site's drives. Its inputs are site_id and access_token.
httpCurl.AddFunction("listDrives","curl https://graph.example.com/sites/{{site_id}}/drives "
"-H \"Authorization: Bearer {{access_token}}\"");
// From the response, take the "id" of the array element whose "name" is the current value
// of drive_name, and put it in the drive_id variable.
httpCurl.AddOutput2("listDrives","drives","name","{{drive_name}}",true,"id","drive_id");
A variable referenced in a record spec is treated as an input of the curl function, exactly like one appearing in the curl command itself, so the execution plan runs whatever produces it before the function that needs it. If a referenced variable has no value and nothing can produce it, the reference is left in place, no array element matches it, and the LastErrorText reports the failure with both the resolved and the unresolved form of the record spec.
The output file path
Variables now resolve in the local file path a curl command writes its response to. That is the path given with -o or --output, the directory given with --output-dir, and the URL that -O (--remote-name) takes a filename from. Previously these were used exactly as written, so -o "{{report_name}}.pdf" created a file whose name was literally {{report_name}}.pdf — braces are legal in filenames on Windows and on Linux, so it succeeded and reported success.
httpCurl.SetVar("customer","acme");
httpCurl.SetVar("report_name","q3-summary");
httpCurl.DoYourThing("curl https://api.example.com/reports/{{report_name}} "
"--output-dir \"reports/{{customer}}\" -o \"{{report_name}}.pdf\"");
// writes reports/acme/q3-summary.pdf
Two refusals come with this. A path that still contains an unresolved {{varName}} is an error rather than a filename — in a URL or a request body an unresolved reference is left in place, but in a file path that is the behavior this change exists to remove. And because a variable’s value can come from a server’s response (AddOutput, AddOutput2), a variable whose value contains a .. directory segment is refused, so a response cannot move the output file out of the directory the curl command named. A .. written in the curl command itself is the application’s own business and still works. The resolved path is written to the LastErrorText as resolvedOutputPath.
📝 Compatibility A record spec or an output path that contains no {{ behaves exactly as before. The caseSensitive argument of AddOutput2 is not text and is never substituted, and the jsonPath of the simpler AddOutput does not take variable references. One interaction worth knowing: --output-dir is combined in only when the filename contains no directory separator, so a variable whose value contains one takes the filename out of that rule — the same rule curl documents.
🔗 HttpCurl — more curl options, response headers, and a stricter command parser
HttpCurl now understands a much larger part of the curl command line, so more commands pasted from documentation or from a shell run as they are. The options below were previously either rejected as a syntax error or, worse, silently ignored.
| Option | What it does in HttpCurl |
|---|---|
-I, --head | Sends a HEAD request. The response header is available in ResponseHeader; there is no body. (Before, it was parsed but a GET was sent.) |
-G, --get | Appends the -d / --data-urlencode items to the URL’s query string and sends a GET instead of a POST. |
--json <data> | Sends the data with Content-Type: application/json and Accept: application/json, as curl 7.82+ does. @file is accepted. |
-b, --cookie | Cookie data ("name=value; name2=value2") to send, or the path of a Netscape/Mozilla format cookie file to read — the same cookies.txt format curl and wget use. |
-c, --cookie-jar <file> | Writes every cookie received, across redirects, to a Netscape format cookie file when the request completes. A later command can send them back with -b. |
--digest, --anyauth, --basic | With -u user:password: Digest authentication (the server’s 401 challenge is answered and the request is sent again), or with --anyauth the strongest scheme the server offers (Digest, then Basic). Without one of these, -u is Basic, as before. --ntlm and --negotiate are recognized but not supported. |
--oauth2-bearer <token> | Sends Authorization: Bearer <token>. |
-m, --max-time <seconds> | A limit on the whole operation — connecting, sending, waiting for and reading the response, redirects included — not just on each idle period. Fractions are allowed (-m 2.5). |
--connect-timeout <seconds> | A limit on establishing the connection. |
--max-redirs <num> | The most redirects -L follows. The default is now 50, as in curl; it was a fixed 4. -1 means no limit. |
-z, --time-cond <date | file> | Sends If-Modified-Since (or If-Unmodified-Since with a leading -) from a date expression or from a file’s last-modified time. A 304 Not Modified response is a success with an empty body, not an error. |
-D, --dump-header <file> | Writes the header of every response, redirects included, to the file. |
-i, --include | Writes the response header before the body in the -o/-O output file. The body returned in memory (ResponseBodyStr, GetResponseJson, …) is never changed, so JSON responses still parse. |
--compressed | Asks for a gzip or deflate compressed response and decompresses it. (Before, it was parsed but did nothing.) |
-v, --verbose | Turns on verbose logging (LastErrorText) for that call. |
-a, --append | Appends the response to an existing -o/-O file instead of replacing it. |
-R, --remote-time | Gives the -o/-O file the Last-Modified time of the response. |
Options that describe what HttpCurl does anyway are accepted without effect: -s, -S (nothing is printed), -f and --fail-with-body (DoYourThing already fails on a 4xx or 5xx response and keeps the body), and -k/--insecure (Chilkat does not verify the server certificate unless the application asks for it). -4, -6, -n, --disable-epsv, -B and --form-escape are accepted and ignored, with a note in LastErrorText. A response compressed with an encoding Chilkat cannot decode (Brotli, zstd) now fails with the new FailReason 11 and a clear message, instead of being returned still compressed.
The command parser itself was rewritten around a table of options, which fixes several things curl users expect:
- Combined short flags such as
-sSL,-fsSLor-sLo filework. Before, a group of flags was ignored as a whole, so the common-sSLlost its-L. - Values attached to short options work:
-XPUT,-ofile.bin,-d@body.json. -Otakes no argument, as in curl. Before,curl -O https://example.com/file.zipconsumed the URL as the option’s value and failed with “No url found”.- curl’s header syntaxes:
-H "X-Empty;"sends the header with an empty value, and-H "Accept:"removes a header HttpCurl would otherwise add. Before, the first sent a header literally namedX-Empty;and the second sent an emptyAccept. --option=value,--no-<flag>, a--that ends the options, and a leadingcurl.exeare accepted.
New API
Response headers were not available at all. Two additions:
ResponseHeader(property) — the last response header as text: the status line (HTTP/1.1 200 OK) followed by the header fields. After-L, it is the header of the final response.GetResponseHeaderField(name)— the value of one field of the last response header, such asContent-TypeorETag; an empty string if the field is absent.
Two smaller corrections: FailReason is 2 for a 401 or 403 response, as documented (it was 3 for the main command, and 2 only for a step of an execution plan), and an SVG response (image/svg+xml) can be read with GetResponseSb and ResponseBodyStr, since it is text.
⚠️ Behavior change An option HttpCurl does not recognize is now an error (FailReason 1, with the option named in LastErrorText) rather than being silently dropped. Dropping it meant, for example, that --digest disappeared and Basic credentials were sent in the clear, or that the option’s value was taken as the URL. Options that are meaningless to HttpCurl but harmless — --progress-bar, -w, --retry, --http2, --tlsv1.2, --ssl-no-revoke, --trace and others — are still accepted, with a note in LastErrorText. Options that would change what is trusted or which client certificate is used (--cacert, --capath, -E/--cert, --key) are refused with a clear error. To get the previous behavior for a command that must keep an option HttpCurl doesn’t know, add IgnoreUnknownCurlOptions to UncommonOptions. Also note the --max-redirs default of 50 where 4 redirects were followed before.
📝 StringBuilder.HtmlToMarkdown — convert HTML to Markdown
StringBuilder.HtmlToMarkdown converts the HTML a StringBuilder holds to Markdown. It is the reverse of MarkdownToHtml, and it is here for the same reason Pdf.ExtractText gained a markdown op in this release: Markdown is the form in which a document reads most cleanly to an LLM, a search index, or a diff.
CkStringBuilder sb;
sb.LoadFile("page.html","utf-8");
bool success = sb.HtmlToMarkdown();
// sb now holds the Markdown.
The conversion is in place: the HTML is replaced by the Markdown. Load the HTML into a second StringBuilder first if you still need it afterwards. If the conversion fails, the contents are left exactly as they were.
The HTML is parsed with the same parser that backs HtmlToXml and HtmlToText, and the resulting document tree produces CommonMark / GitHub-flavored Markdown:
| HTML | Markdown |
|---|---|
h1 … h6 | # … ###### headings |
p, div, section and other block elements | Paragraphs separated by a blank line. br is a hard line break, hr is --- |
b/strong, i/em/cite, del/s/strike | **bold**, *italic*, ~~struck~~ |
code, kbd, samp, tt, var | A backtick code span |
pre | A fenced code block. The language comes from a language-xxx or lang-xxx class on the pre or its code child |
a href, img | [text](url "title") — or <url> when the text is the URL — and  |
ul, ol, li | - and 1. lists, nested lists indented. A checkbox input in a list item becomes [ ] or [x] |
blockquote | Lines prefixed with > |
table | A GitHub pipe table. The first row is the header when it holds th cells or sits in a thead; colspan is honored and align or text-align sets the column alignment. A single-column table becomes plain paragraphs, since such tables are almost always for layout |
dl, dt, dd | A term line followed by : definition lines |
head, script, style, title, meta, link, noscript, iframe, object, embed, svg, canvas, form controls and comments produce no output, so a whole page can be converted without first stripping its chrome. Any other tag is transparent: only its content is emitted. Markdown-significant punctuation in ordinary text is backslash-escaped so that the text renders literally, and text inside code and pre is never escaped. HTML entities are decoded.
📝 Line endings The Markdown uses LF line endings, ends with a single line ending, and never has more than one consecutive blank line outside of a code block. Call ToCRLF afterwards for CRLF. Empty contents, or HTML with no visible content, produce empty contents and success.
📅 .NET Core, .NET Standard and Mono — the DateTime members of the .NET Framework assembly
The .NET Core, .NET Standard and Mono assemblies now have the System.DateTime members that the .NET Framework assembly has. Until now they were missing, so code written for .NET Framework that used one of them, such as zipEntry.FileDateTime, failed to compile with error CS1061. These assemblies call the Chilkat C API, which has no date type, so each date member is now implemented on top of the RFC 822 string member that already existed beside it.
| Member | Built on | Returns |
|---|---|---|
ZipEntry.FileDateTime NEW | FileDateTimeStr | Local time (get and set) |
Email.EmailDate NEW | EmailDateStr | UTC (get and set) |
Email.LocalDate NEW | LocalDateStr | Local time (get and set) |
Cert.ValidFrom, Cert.ValidTo NEW | ValidFromStr, ValidToStr | UTC (read-only) |
SFtpFile.CreateTime, LastAccessTime, LastModifiedTime NEW | CreateTimeStr, LastAccessTimeStr, LastModifiedTimeStr | Local time (read-only) |
CkDateTime.GetAsDateTime(bLocal) NEW | GetAsRfc822(bLocal) | Local time or UTC, as bLocal says |
Each member behaves as it does in the .NET Framework assembly. A returned value has the same DateTimeKind, local or UTC, as shown above. A value being set is taken as local time if its Kind is Local, and as UTC otherwise. The result does not depend on the current culture. The ...Str members are unchanged.
📝 Scope The .NET Core, .NET Standard and Mono assemblies only; the .NET Framework assembly already had these members. EmailEventArgs.Date remains .NET Framework only.
🔑 Http DPoP — RSA and Ed25519 keys, and private_key_jwt client authentication
SetDPoPKey now accepts RSA and Ed25519 keys as well as EC keys. DPoP (RFC 9449) allows any asymmetric signature algorithm, and some authorization servers, such as Okta, use RS256. Before v11.7.0, SetDPoPKey accepted only ECDSA keys. Each DPoP proof is signed with the algorithm that matches the key, and the proof’s jwk header carries the matching public key:
| Key | Proof algorithm |
|---|---|
| EC key on P-256 | ES256 |
| EC key on P-384 | ES384 |
| EC key on P-521 | ES512 |
| RSA key, 2048 bits or more NEW | RS256 |
| Ed25519 key NEW | EdDSA |
For any other key, SetDPoPKey now fails with the reason in LastErrorText: an RSA key smaller than 2048 bits, a DSA key, or an EC key on another curve. Before, an EC key on secp256k1 was accepted, and its proofs were wrongly labeled ES256.
Client assertions (private_key_jwt)
The new SetClientAssertionKey method adds private_key_jwt client authentication (RFC 7523) to the client credentials flow of the AuthToken property. Some authorization servers require a signed client_assertion JWT, instead of a client secret, with each token request, often together with DPoP. The assertion expires after a few minutes and its jti may be used only once, so before v11.7.0 an assertion put in the AuthToken JSON had expired by the time Chilkat requested the next token.
With the key set, Chilkat creates a new assertion for each token request and sends it with client_assertion_type set to urn:ietf:params:oauth:client-assertion-type:jwt-bearer. The assertion has the claims iss and sub (the client_id from the AuthToken JSON), aud (the token_endpoint), iat, exp (5 minutes later) and a unique jti, with the key ID (kid) in its header. The key types and algorithms are those in the table above. The same key may be passed to SetDPoPKey if the server allows it.
📝 Compatibility Applications that use an EC key on P-256, P-384 or P-521 with SetDPoPKey work as before, and the AuthToken flow is unchanged unless SetClientAssertionKey is called. See also the DPoP fixes below.
🛠️ Fixes & Improvements
🐘 PHP on Windows — the extension loads again into PHP 7.x and 8.0–8.3
The v11.6.1 Windows builds of the Chilkat PHP extension for PHP 7.2–7.4 and 8.0–8.3 could not be loaded by the official PHP builds from windows.php.net. v11.7.0 fixes this. PHP refused the DLL at startup with a warning such as:
PHP Warning: PHP Startup: Can't load module 'C:\php\ext\php_chilkat.dll' as it's linked with 14.39,
but the core is linked with 14.29 in Unknown on line 0
On Windows, PHP will not load an extension that was linked with a newer version of Visual Studio than PHP itself. The official PHP 7.2–7.4 builds are linked with Visual Studio 2017, PHP 8.0–8.3 with Visual Studio 2019, and PHP 8.4 and 8.5 with Visual Studio 2022. The v11.6.1 extensions, the first produced by the new code generator, were all linked with Visual Studio 2022, so only the PHP 8.4 and 8.5 builds could be loaded.
In v11.7.0 the extensions for PHP 8.0–8.5 are built with Visual Studio 2019 and those for PHP 7.2–7.4 with Visual Studio 2017, as they were before v11.6.1. An extension linked with an older Visual Studio loads into a newer PHP, so the PHP 8.4 and 8.5 builds load into PHP’s Visual Studio 2022 builds as before. The build now also checks the linker version of every DLL against what its PHP version accepts, so this cannot happen again.
📝 Scope Windows builds of the classic Chilkat PHP extension (CkHttp, CkJsonObject, …) only, both thread-safe and NTS, 32- and 64-bit; the Linux, Alpine Linux and macOS builds were not affected. Nothing in the API changed: chilkat.php has the same classes and methods, and no application code changes are needed — replace the DLL with the v11.7.0 one for your PHP version.
⚡ Hashtable — faster, leaner Swiss Table internals
The Hashtable class now uses a Swiss Table internally. The Swiss Table is a high-performance, open-addressed hash table design originally created by Google engineers in Zurich (named after Swiss efficiency and the .ch country code). It achieves its speed and low memory use by separating metadata from the actual key-value data: a compact array of small per-slot metadata entries, each holding a few bits of its key's hash, is scanned first, so a lookup compares a full key only when the metadata already matches, and the key-value entries are stored without per-entry allocations or chaining.
The result is faster insertion and lookup and lower memory use, most noticeably for tables with many entries. The API and behavior of Hashtable are unchanged; applications benefit simply by updating to v11.7.0.
🍪 Http — cookies set without a Domain attribute are sent back again
In v11.6, a cookie set without a Domain attribute was not sent back to the host that set it. A cookie from api.example.com was recorded as a host-only cookie for the base domain, example.com. Host-only cookies, added in v11.6 as RFC 6265 requires, match only the exact host, so the cookie never matched api.example.com.
In v11.7.0 the cookie is recorded for the full host, api.example.com, and is sent back to that host only. Saved cookies still go to the same cookie file (for example example_com.xml).
✍️ CodeSign — re-signing replaces the signature, stricter verification, and other fixes
- Signing an already-signed file replaces its signature. Before v11.7.0,
AddSignatureon an EXE or DLL that was already signed added a second signature entry to the file’s certificate table. Windows uses only the first entry, so the new signature was ignored. Now the existing signature is removed first, and a file that holds several signatures from earlier versions ends up with exactly one. - Stricter verification of EXE and DLL files (CVE-2013-3900).
VerifySignaturenow also checks the PE certificate table: it must hold exactly one signature entry, containing exactly the signature that was verified, with no extra data. Data in the certificate table is not covered by the Authenticode signature, so anything hidden there would be unsigned. A file that fails this check now causesVerifySignatureto return false. To accept such files, for example installers that deliberately store data in the certificate table, addcodesign-allow-cert-paddingtoUncommonOptions. GetSignerCertafter a failed verification.GetSignerCertnow returns a certificate only after a call toVerifySignaturethat succeeded. Before, it could return the signer certificate of a file that had failed verification.- No empty file is created for a missing path.
AddSignatureandRemoveSignature, called with a path to a file that doesn’t exist, created an empty 0-byte file at that path. They now fail with “The file does not exist.” and create nothing. - A failed signature is no longer written into the file. If computing the file’s hash or creating the signature failed,
AddSignaturecould still write an invalid signature into the file, or report a misleading Windows error instead of the real cause. Now the file is left unchanged andLastErrorTextreports what failed.
📝 Scope If VerifySignature now returns false for an EXE or DLL that it accepted before, look for “certificate table failed the strict check” in LastErrorText. A file signed twice by an earlier version of Chilkat is one such case; signing it again with v11.7.0 leaves it with a single signature.
🔒 TLS — server certificate validation: cross-signed CA certificates, and the root must be trusted
These changes apply to every class that makes TLS connections (Http, Rest, Socket, MailMan, Imap, Ftp2, WebSocket, and others) when RequireSslCertVerify is true.
- Cross-signed CA certificates no longer cause a validation failure. Some CA certificates exist in two forms with the same name and the same key: a self-signed root, and a copy cross-signed by an older root. An example is “USERTrust ECC Certification Authority”, which also exists cross-signed by “AAA Certificate Services”. When a server sent the cross-signed copy, or Windows had it cached among its intermediate certificates, Chilkat followed it to the older root. If that older root was not trusted, the connection failed with “Unable to build certificate chain to root” (
ConnectFailReason106), even though browsers accepted the same server. Now, when a trusted self-signed root with the same name and the same public key exists, Chilkat uses it, as browsers do. The trusted root can be one added withTrustedRoots, or one in the operating system’s trusted root certificates whenTrustSystemCaRootsis true. This affected some servers with Sectigo certificates after Microsoft’s Windows trust-list update of October 26, 2026. - The root certificate must be trusted when no trusted roots are activated. Before v11.7.0, an application that had not activated a
TrustedRootsobject accepted any self-signed root at the top of the server’s certificate chain, including one the server made itself. Now that root must be in the operating system’s trusted root certificates, with the same public key: the Windows “Trusted Root Certification Authorities” store, the system CA bundle on Linux, or the system roots on macOS. Otherwise the connection fails with “The certificate chain ends at a root certificate that is not trusted” (ConnectFailReason106). Applications that activateTrustedRootsare not affected. - Stricter matching against the Windows trusted root store. On Windows, a self-signed root was treated as trusted if a certificate in the “Trusted Root Certification Authorities” store had the same subject name or the same subject key identifier. Both are just fields of the certificate, which anyone can copy. The public key must now match as well. This also applies to signature verification (for example
Crypt2and S/MIME) whenTrustedRootsis activated withTrustSystemCaRoots. - Finding the root in the operating system’s trusted roots. Because a self-signed root now has to be found there, Chilkat looks for it in more than one way: the certificate itself, by its SHA-1 thumbprint; then by subject key identifier; then by subject name. The public key must match in every case. A certificate that is its own root — a self-signed server certificate, as used on many internal servers — often carries no subject key identifier, which is only required of CA certificates, so looking for the certificate itself is what settles those. When none of the lookups find it, Chilkat asks Windows’ own trust evaluation, the same one the browsers on that machine use, which also recognizes a root deployed by Group Policy or by enterprise policy rather than installed into the per-user or per-machine store.
⚠️ Behavior change With RequireSslCertVerify set to true and no TrustedRoots activated, connections to servers whose root certificate isn’t in the operating system’s trusted roots now fail. Typical cases are internal servers with a self-signed certificate or a private CA. Public servers are not affected. To keep such connections working, add the server’s root (or its self-signed certificate) to a TrustedRoots object and call Activate. Leave TrustSystemCaRoots true to keep trusting the operating system’s roots as well. To restore the previous behavior instead, add TrustAnySelfSignedRoot to UncommonOptions. On a system where Chilkat can’t read any trusted root certificates (for example a Linux system without /etc/ssl/certs/ca-certificates.crt), the previous behavior is kept and a warning is logged; activate TrustedRoots there to have the root checked.
🔒 TLS — hostname verification: stricter matching of wildcard certificate names
When RequireHostnameMatch is true (Http and MailMan), the hostname is compared with the names in the server certificate’s Subject Alternative Name (SAN). Earlier versions matched a wildcard name too loosely and accepted certificates that RFC 9525 (formerly RFC 6125) says to reject. A wildcard is now accepted only as the entire left-most label, and it stands for exactly one label:
*.example.commatcheswww.example.com, but nota.b.example.comand not the bareexample.com.- A wildcard that is only part of a label, or in any label but the first, is not accepted:
w*.example.com,www.*.com. - A wildcard directly under a top-level domain or a country-code second level is not accepted:
*.com,*.co.uk. - A wildcard never matches an IP address.
RequireHostnameMatch is false by default, so only applications that turned it on are affected.
⚠️ Behavior change A server whose certificate has *.example.com but no entry for example.com is no longer accepted for the bare example.com. Its certificate needs a SAN entry for the bare domain as well, which browsers and OpenSSL already require. LastErrorText says so when a connection is refused for this reason.
📜 Cert — the certificate chain reported for a connection, and a cached VerifySignature result
Three fixes in the Cert class. None of them changes whether a TLS connection succeeds or fails — a handshake builds and checks its own chain. They change what Chilkat reports about a certificate.
- The certificate chain reported for a TLS connection is now the chain that was used. Applications that log a server’s certificate chain —
Http.GetServerSslCertorSocket.GetServerCert, thenCert.GetCertChain— could get a chain that differed from the one the connection used, ending at a cross-signed copy of a CA certificate and reporting the chain as not trusted, which is misleading when the connection itself was fine. The chain was rebuilt from the certificate stores, which accumulate certificates from earlier connections and need not hold the intermediate certificates the server sent. NowGetServerSslCertandGetServerCertattach the chain from the TLS handshake to the certificate they return, andGetCertChainandBuildCertChainreport that chain rather than rebuilding one. The chain is also completed for connections made withRequireSslCertVerifyset to false, so those report the same chain a verifying connection would instead of the server’s certificate alone. - Chains built from the certificate stores prefer a trusted self-signed root, as TLS does. When a chain is built from the certificate stores rather than taken from a handshake —
Cert.GetCertChain,Cert.BuildCertChain,Cert.VerifySignature,CertChain— it no longer follows a cross-signed copy of a CA certificate to an untrusted older root. The rule described above for TLS server certificate validation is now the same rule used here, so the two cannot disagree about the same certificate. A chain that previously stopped short of a trusted root can now reach one, soVerifySignatureandCertChain.ReachesRootcan report success where they previously did not. Cert.VerifySignatureno longer returns a previous certificate’s result. The method keeps the chain it builds, and the verification result with it, so that repeated calls are cheap. Loading a different certificate into the sameCertobject did not discard them, so the next call reported the first certificate’s result — a certificate whose issuer is in no certificate store could be reported as verified. The kept result is now tied to the certificate it was built for and to the certificate stores it was built from, and is discarded when either changes, including byLoadFromFile,LoadPem,SetFromEncoded,LoadFromBinary, theLoadBy...methods andUseCertVault.
🖥️ Socket TLS server — ECDSA server certificates work with TLS 1.2
A TLS server started with Socket.InitSslServer using a certificate with an ECDSA key failed every TLS 1.2 handshake. The server labeled the signature in its key-exchange message as RSA, whatever the key type, so clients tried to check an ECDSA signature as an RSA one. A Chilkat client reported “Unable to parse RSA public key”. The ECDSA signature was also sent as raw r‖s values instead of the DER encoding TLS requires (RFC 8422), which other TLS clients reject.
In v11.7.0 the server labels the signature to match its key (ECDSA or RSA) and DER-encodes ECDSA signatures, so ECDSA server certificates work with Chilkat and with other TLS clients. Servers with RSA certificates are unchanged.
🔏 Crypt2, S/MIME and PDF signing — ECDSA signatures are always valid DER
Most CMS signatures created with an ECDSA key were invalid, although the signing method returned success. A CMS signature (PKCS#7, CAdES) stores the ECDSA signature as two ASN.1 INTEGERs, r and s. When the first byte of a value is 0x80 or more, DER requires a leading 0x00 byte; without it the value reads as negative, and validators such as OpenSSL, BouncyCastle, .NET and the EU DSS validator reject the signature. Chilkat decided whether a signature was already DER-encoded by comparing its size with a fixed size for the curve, which gave the wrong answer whenever r or s needed the 0x00 byte or was shorter than usual:
- Smart cards, USB tokens, HSMs and cloud keys that return raw r‖s values (Windows CNG, PKCS#11, smart card minidrivers): the values were wrapped as INTEGERs without the 0x00 byte, so the signature was invalid whenever r or s began with a byte of 0x80 or more — about three signatures out of four.
- Signers that already return DER (private keys in memory, for example loaded from a PFX, and the macOS keychain): a valid DER signature that was not exactly the fixed size was split in half and wrapped again, which made it invalid.
- P-521 keys: the SEQUENCE length was encoded incorrectly, so every signature was invalid.
Because r and s are different for every signature, the same code, key and data produced a valid signature on some runs and an invalid one on others. Validators found the message digest correct and reported only the signature itself as invalid (for example SignatureIntact=false).
In v11.7.0 a signature that is already valid DER is kept unchanged, and raw r‖s values are encoded as minimal DER INTEGERs, with the 0x00 byte where it is needed. If the signer returns something that is neither, signing now fails with the reason in LastErrorText instead of producing an invalid signature.
📝 Scope ECDSA keys only; RSA signatures are not affected. The fix applies wherever Chilkat creates a CMS signature: Crypt2 (for example CreateDetachedSignature, CreateP7S, CreateP7M, SignBytes, OpaqueSignBytes and SignBdENC, with or without CAdES), S/MIME signing with Mime, Email and MailMan, and PDF signing with Pdf. No application code changes are needed. Signatures created with earlier versions that verify are valid; signatures that fail to verify must be created again.
💳 PKCS#11 smart cards and tokens — PIN-pad readers and tokens that only hash-and-sign
These changes apply to the Pkcs11 class and to every class that signs with a certificate whose private key is on a PKCS#11 smart card, USB token or HSM (Crypt2, Pdf, XmlDSigGen, Jws, Jwt, Rsa, SshKey, and TLS client authentication). They were prompted by a D-Trust signature card in a REINER SCT cyberJack PIN-pad reader, used through OpenSC.
- Readers with a PIN pad. Some smart card readers have their own keypad: the PIN is entered on the reader and never passes through the application. Logging in to such a token with Chilkat did not work — with an empty PIN, the token’s driver (for example OpenSC) rejected the login as a PIN of the wrong length, and the reader never prompted. Now, when the PIN is empty —
Pkcs11.LoginorQuickSessionwith an empty PIN, orCert.SmartCardPinleft empty — Chilkat asks the token whether it has a PIN pad and, if so, lets the reader collect the PIN. This also covers the login Chilkat does on its own before signing, and the extra PIN entry that some tokens require before each signature or decryption. The special PIN value"null_ptr", which has requested this since v10.0.2, still works; for a PIN-pad reader whose driver does not report the keypad, addPkcs11NullPintoUncommonOptions. A PIN mistyped on the reader is no longer remembered as a failed PIN, so the next attempt is not refused by Chilkat. - Tokens that offer only hash-and-sign mechanisms. Chilkat hashes the data itself and asks the token to sign the digest with
CKM_ECDSA,CKM_RSA_PKCSorCKM_RSA_PKCS_PSS. Some tokens list only the mechanisms that hash on the token (CKM_ECDSA_SHA256,CKM_SHA256_RSA_PKCS,CKM_SHA256_RSA_PKCS_PSS, and so on), and signing failed withCKR_MECHANISM_INVALID. Chilkat now reads the slot’s mechanism list once per session; when the digest-signing mechanism is missing (or the module rejects it) and the matching hash-and-sign mechanism is present, it passes the data to the token to hash and sign. The signature is identical. The data is available for CAdES and PDF signatures (the signed attributes), TLS 1.3 client authentication, JWS, JWT, XML signatures (the canonicalized SignedInfo), SSH public-key authentication, andRsasigning of data;Rsa.SignHash,Ecc.SignHashand TLS 1.2 client authentication only have the digest, and thereLastErrorTextnow names the hash-and-sign mechanism the token would accept. To select the signing mechanism yourself, addPkcs11SignMechanism{CKM_ECDSA_SHA384}(a name or a hex value such as{0x1045}) toUncommonOptions. Pkcs11.Discoverand the log misreported the module’s library version when its major or minor number was 0 — OpenSC 0.27 was reported as 32.32. TheCK_INFOfields are now read at their fixed positions.
📝 Note No application code changes are needed for PIN-pad readers or hash-and-sign-only tokens: leave the PIN empty for a PIN-pad reader, and sign as before.
🗜️ Compression — the “gzip” algorithm now produces and reads gzip data
Before v11.7.0, setting Algorithm to gzip silently selected deflate. gzip was not a recognized name, so the compress methods produced raw deflate data, data in the gzip format failed to decompress, and Algorithm read back as deflate.
In v11.7.0, gzip selects the gzip format (RFC 1952): a gzip header, the deflate data, and a trailer with the CRC-32 and size of the uncompressed data. When gzip data is decompressed in one call (DecompressBd, DecompressBytes, DecompressString, DecompressFile, and the other one-call methods with FirstChunk and LastChunk both true), the CRC-32 and size are checked, so damaged or truncated data fails instead of returning partial output. The Begin/More/End streaming methods do not check the trailer.
⚠️ Behavior change An application that sets Algorithm to gzip now gets gzip data from the compress methods, where it got raw deflate data before. Data compressed that way by an earlier version is raw deflate: decompress it with Algorithm set to deflate.
🔑 Http DPoP — the htu and iat claims, and full-length EC coordinates in JWKs
- The
htuclaim of a DPoP proof is the request URL without its query string. RFC 9449 requires only the scheme, host, port and path. For each request after the token request, Chilkat included the query string and dropped a port other than the default, anhttpURL began withhttp:/, and a request sent through an HTTP proxy had a malformed URL. Servers that comparehtuexactly rejected such proofs. - The
iatclaim of a DPoP proof is a number. It was sent as a string, such as"1793630335". RFC 9449 requires a NumericDate (RFC 7519), which is a JSON number, and strict servers reject a string. - An EC key written as a JWK has full-length values. In a JWK,
xandy, anddfor a private key, must be the full size of the curve: 32 bytes for P-256, 48 for P-384 and 66 for P-521 (RFC 7518). Chilkat dropped leading zero bytes, so the JWK was too short for about three P-521 keys out of four, and occasionally for P-256 and P-384 keys. Servers that check the length rejected the DPoP proofs signed with such a key, because the proof carries the key as a JWK. The same JWK is returned byPublicKey.GetJwkandPrivateKey.GetJwk, and is the basis ofGetJwkThumbprint.
⚠️ Behavior change For an EC key whose JWK was too short, GetJwk now returns longer x, y or d values, and GetJwkThumbprint returns a different value: the correct one (RFC 7638), which other implementations compute. A thumbprint of such a key stored by an earlier version must be computed again. For every other key, nothing changes.
For previous versions and archived updates, visit the Release Notes Archive.