{"id":1965,"date":"2025-07-03T21:00:44","date_gmt":"2025-07-04T03:00:44","guid":{"rendered":"https:\/\/www.schwie.com\/brad\/?p=1965"},"modified":"2026-02-24T12:06:43","modified_gmt":"2026-02-24T18:06:43","slug":"pyhanko-and-macos","status":"publish","type":"post","link":"https:\/\/www.schwie.com\/brad\/?p=1965","title":{"rendered":"pyHanko and macOS"},"content":{"rendered":"<p>I&#8217;m trying to be better about digitally signing .pdf documents that I author as a professional engineer. And without needing to pay Adobe to use Acrobat. Enter pyHanko, which does not have a graphical user interface; however, with a little fussing it works well.<\/p>\n<p>First, open Terminal.app and execute the following command to create a certificate and private key:<\/p>\n<p><code>openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 3650<\/code><\/p>\n<p>When prompted, I entered:<\/p>\n<p>Country Name (2 letter code) [AU]:US<br \/>\nState or Province Name (full name) [Some-State]:Minnesota<br \/>\nLocality Name (eg, city) []:<br \/>\nOrganization Name (eg, company) [Internet Widgits Pty Ltd]:Barr Engineering Co.<br \/>\nOrganizational Unit Name (eg, section) []:<br \/>\nCommon Name (e.g. server FQDN or YOUR name) []:Bradford Schwie<br \/>\nEmail Address []:bschwie@barr.com<\/p>\n<p>Next combine the above generated cert.pem and key.pem into certif.p12 with this command:<\/p>\n<p><code>openssl pkcs12 -export -out certif.p12 -in cert.pem -inkey key.pem<\/code><\/p>\n<p>Verify the certificate.p12 contains a certificate and key with this command:<\/p>\n<p><code>openssl pkcs12 -info -in certif.p12<\/code><\/p>\n<p>Finally, create a .pdf with command:<\/p>\n<p><code>pyhanko sign addsig --field Sig1 pkcs12 input.pdf output_signed.pdf certif.p12<\/code><\/p>\n<p><code>pyhanko sign addsig --field 1\/70,400,390,350\/Sig1 --style-name default pkcs12 input.pdf output_signed.pdf certif.p12<\/code><\/p>\n<p>I could probably do more fussing with the signature appearance, but it took a fair amount of effort and this was before Liam bailed me out &#8211; thank you! Hopefully others find this as helpful as I do.<\/p>\n<p>** Update 11-26-2025 **<br \/>\nToday I was signing a document that another user had already digitally signed. No sweat, pyHanko seems to handle this situation with the following example that I used:<\/p>\n<p><code>pyhanko sign addsig --field 1\/90,215,360,185\/Sig1 --style-name default --no-strict-syntax pkcs12 input.pdf output_signed.pdf certif.p12<\/code><\/p>\n<p>** Update 12-4-2025 **<br \/>\nToday I signed a couple letter documents. The following pyHanko command worked for me once I navigated to the Archive folder on the Desktop, where I had the other files discussed above.<\/p>\n<p><code>pyhanko sign addsig --field 1\/70,275,370,225\/Sig1 --style-name default --no-strict-syntax pkcs12 input.pdf output_signed.pdf certif.p12<\/code><\/p>\n<p>** Update 12-30-2025 **<br \/>\nTo stamp the fourth page of a .pdf, replace &#8220;field 1&#8221; (above), with &#8220;field 4&#8221; as shown below:<\/p>\n<p><code>pyhanko sign addsig --field 4\/140,150,410,120\/Sig1 --style-name default --no-strict-syntax pkcs12 input.pdf output_signed.pdf certif.p12<\/code><\/p>\n<p>** Update 2-24-2026 **<br \/>\nFor moving the signature around, I have found that keep the height of the signature box constant allows you to widen the box to create separation between the signature graphic and the date. This gets me separation on the first page of a wide form:<\/p>\n<p>pyhanko sign addsig &#8211;field 1\/90,108,520,78\/Sig1 &#8211;style-name default &#8211;no-strict-syntax pkcs12 input.pdf signed.pdf certif.p12<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;m trying to be better about digitally signing .pdf documents that I author as a professional engineer. And without needing to pay Adobe to use Acrobat. Enter pyHanko, which does not have a graphical user interface; however, with a little &hellip; <a href=\"https:\/\/www.schwie.com\/brad\/?p=1965\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1965","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.schwie.com\/brad\/index.php?rest_route=\/wp\/v2\/posts\/1965","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.schwie.com\/brad\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.schwie.com\/brad\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.schwie.com\/brad\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.schwie.com\/brad\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1965"}],"version-history":[{"count":10,"href":"https:\/\/www.schwie.com\/brad\/index.php?rest_route=\/wp\/v2\/posts\/1965\/revisions"}],"predecessor-version":[{"id":2093,"href":"https:\/\/www.schwie.com\/brad\/index.php?rest_route=\/wp\/v2\/posts\/1965\/revisions\/2093"}],"wp:attachment":[{"href":"https:\/\/www.schwie.com\/brad\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1965"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.schwie.com\/brad\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1965"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.schwie.com\/brad\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1965"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}