{"id":12275,"date":"2025-11-03T11:00:10","date_gmt":"2025-11-03T10:00:10","guid":{"rendered":"https:\/\/www.basyskom.de\/?p=12275"},"modified":"2025-11-03T11:48:21","modified_gmt":"2025-11-03T10:48:21","slug":"qt-webengine-custom-server-certificates","status":"publish","type":"post","link":"https:\/\/www.basyskom.de\/en\/qt-webengine-custom-server-certificates\/","title":{"rendered":"Qt WebEngine Custom Server Certificates"},"content":{"rendered":"<div data-elementor-type=\"wp-post\" data-elementor-id=\"12275\" class=\"elementor elementor-12275\" data-elementor-post-type=\"post\">\n\t\t\t\t<div class=\"elementor-element elementor-element-7a5be96 e-flex e-con-boxed wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-column-slider-no wpr-equal-height-no e-con e-parent\" data-id=\"7a5be96\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-b790e5e elementor-widget elementor-widget-text-editor\" data-id=\"b790e5e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<div>One of our clients is building an application with a web-based front-end. In order to provide a clean, controlled environment where no browser extensions or user settings interfere, it is viewed in a dedicated browser built with Qt WebEngine.<\/div>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e2a061f elementor-widget elementor-widget-text-editor\" data-id=\"e2a061f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\tThe corresponding web server uses a self-signed TLS certificate. That\u2019s fair for the environment where the application is deployed in. However, this also means that the certificate and its issuing CA must be added to the system\u2019s trust store. Adding a new Root CA rightfully needs administrative privileges, but is also not really what we want: the certificate is only relevant for our application and we want it to run without such pre-configuration.\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-22f585a e-flex e-con-boxed wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-column-slider-no wpr-equal-height-no e-con e-parent\" data-id=\"22f585a\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-64b75ef elementor-widget elementor-widget-text-editor\" data-id=\"64b75ef\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Since Qt 5.13, Qt WebEngine has had an in-memory <em>client<\/em> certificate store where you can add additional certificates without affecting the system\u2019s certificate store. However, there has been no API to add <em>server<\/em> certificates. Luckily, Chromium 122, which coincidentally is the base for Qt 6.8 LTS, refactored certificate handling: the creation parameters for its \u201ccert verifier\u201d now accept a list of <code data-no-translation=\"\">initial_additional_certificates<\/code> that are considered irrespective of the platform\u2019s trust store. Therefore, we intended to add a new API to <code data-no-translation=\"\">QWebEngineProfile<\/code> so an application can provide a <code data-no-translation=\"\">QList&lt;QSslCertificate&gt;<\/code> of additional certificates.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-78c9699 elementor-widget elementor-widget-text-editor\" data-id=\"78c9699\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Unfortunately, since this is only designed to be used during the creation of the cert verifier, our new <code data-no-translation=\"\">QWebEngineProfile::setAdditionalTrustedCertificates<\/code> method would take effect only once, before the website is loaded for the first time and subsequent calls effectively did nothing. That\u2019s not a very predictable API design. Luckily again, Qt 6.9 introduced the <code data-no-translation=\"\">QWebEngineProfileBuilder<\/code> specifically intended for this kind of \u201cwrite-once\u201d API.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-f1bd4ec elementor-widget elementor-widget-text-editor\" data-id=\"f1bd4ec\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>We moved the method there, added some unit tests, and the just released Qt 6.10 now supports setting additional trusted server certificates:\u00a0<a href=\"https:\/\/doc.qt.io\/qt-6\/qwebengineprofilebuilder.html#setAdditionalTrustedCertificates\" target=\"_blank\" rel=\"noopener\">QWebEngineProfileBuilder::setAdditionalTrustedCertificates<\/a>.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9cfcfd5 elementor-widget elementor-widget-elementor-syntax-highlighter\" data-id=\"9cfcfd5\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"elementor-syntax-highlighter.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<pre data-no-translation=\"\"><code class='language-cpp' data-no-translation=\"\">auto rootCa = QSslCertificate::fromPath(u&quot;...pem&quot;_s); \/\/ QSslCertificate::fromPath also new in Qt 6.10.\n\nQWebEngineProfileBuilder builder;\nbuilder.setAdditionalTrustedCertificates({rootCa});\n\nm_profile.reset(builder.createProfile(profileName)); <\/code><\/pre><script>\nif (!document.getElementById('syntaxed-prism')) {\n\tvar my_awesome_script = document.createElement('script');\n\tmy_awesome_script.setAttribute('src','https:\/\/www.basyskom.de\/wp-content\/plugins\/syntax-highlighter-for-elementor\/assets\/prism2.js');\n\tmy_awesome_script.setAttribute('id','syntaxed-prism');\n\tdocument.body.appendChild(my_awesome_script);\n} else {\n\twindow.Prism && Prism.highlightAll();\n}\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-6ac3102 elementor-widget elementor-widget-text-editor\" data-id=\"6ac3102\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Amusingly, when the changes were merged, some security monitoring system (through Qt\u2019s GitHub mirror) sent an email advising us that we might have leaked a private key on a git repository. Yes, thanks for the heads-up but that key is just used by our unit test<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>","protected":false},"excerpt":{"rendered":"<p>In this blog post, we\u2019re having a look at how we added support for custom server certificates to Qt WebEngine. This way an application can talk to a server using a self-signed TLS certificate without adding it to the system-wide certificate store.<\/p>","protected":false},"author":23,"featured_media":4011,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1,2,8],"tags":[15,854,874,853,856],"class_list":["post-12275","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-allgemein","category-blog","category-qt","tag-qt","tag-ssl","tag-tls","tag-web","tag-webengine"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/12275","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/users\/23"}],"replies":[{"embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/comments?post=12275"}],"version-history":[{"count":25,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/12275\/revisions"}],"predecessor-version":[{"id":12331,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/posts\/12275\/revisions\/12331"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/media\/4011"}],"wp:attachment":[{"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/media?parent=12275"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/categories?post=12275"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.basyskom.de\/en\/wp-json\/wp\/v2\/tags?post=12275"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}