AWS https://itextpdf.com/ en iText DITO Now Available in AWS Marketplace, Offering Intuitive PDF Design and Generation Capabilities to a Wider Audience https://itextpdf.com/blog/itext-news/itext-dito-available-aws-marketplace-offering-intuitive-pdf-generation <span property="schema:name" class="field field--name-title field--type-string field--label-hidden">iText DITO Now Available in AWS Marketplace, Offering Intuitive PDF Design and Generation Capabilities to a Wider Audience </span> <span rel="schema:author" class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/karienpype" typeof="schema:Person" property="schema:name" datatype="">karien.pype</span></span> <span property="schema:dateCreated" content="2021-07-07T11:56:27+00:00" class="field field--name-created field--type-created field--label-hidden"><time datetime="2021-07-07T13:56:27+02:00" title="Wednesday, July 7, 2021 - 13:56" class="datetime">Wed, 07/07/2021 - 13:56</time> </span> <div property="schema:text" class="wysiwyg-content clearfix text-formatted field field--name-body field--type-text-with-summary field__items"> <p align="left" class="MsoNormal" style="text-align:center;mso-pagination:widow-orphan;&#10;background:white"><img alt="main blog AWS final" data-entity-type="file" data-entity-uuid="9e099ba2-de6d-4f57-9e7d-6a253ad0cd81" src="/sites/default/files/inline-images/Main%20blog%20-%201140x300_38.png" width="1140" height="300" loading="lazy" /></p> <p align="left" class="MsoNormal" style="text-align:center;mso-pagination:widow-orphan;&#10;background:white"><strong>BOSTON, MA – July 7 2021 </strong>— iText, a global leader in innovative PDF technology, is pleased to announce its template-based PDF generator, <a href="/en/products/itext-dito" title="iText DITO® ">iText DITO</a>, is now available in AWS Marketplace.</p> <p>iText chose to make iText DITO available on AWS because it offers a simple, scalable way to deploy iText DITO SDK for our users and effortless integration with other tools on its platform. </p> <p>AWS Marketplace is a digital catalog with thousands of software listings from independent software vendors that make it easy to find, test, buy, and deploy software that runs on Amazon Web Services (AWS). AWS is an additional channel that iText embarks upon to facilitate the deployment of its technology.<br />  </p> <p><em>“Being listed in AWS Marketplace offers more developers easier access to our feature-rich and well documented PDF technology, starting first with our latest product iText DITO</em>,” <a href="https://itextpdf.com/en/blog/itext-news/qa-get-know-itext-ceo-gary-fry">said Gary Fry, CEO</a>, iText. “<em>iText DITO builds on the strong momentum behind the rapid adoption of PaaS to deliver a solution for organizations looking to intelligently manage information, while saving invaluable time and resources eliminating the mundane work of the developer.”</em></p> <p>Joining AWS Marketplace comes at a time when many organizations are creating a generation of custom applications that are leveraging the potential of cloud capabilities. In response, SaaS and IaaS providers alike are moving into PaaS to help provide enterprise customers the flexibility and observability they require. </p> <p>As we cater to Enterprise customers, iText is currently working on a streamlined way for its customers to deploy the iText DITO REST SDK/API component on either the Amazon Elastic Container Service (ECS) or the Amazon Elastic Kubernetes Service (EKS). That will ensure that the orchestration and management of PDF generation clusters can be made with ease, and with the confidence that they will never skip a beat.</p> <p><em>“Having a presence on AWS Marketplace will bring our clients unsurpassed, additional value,”</em> <a href="https://itextpdf.com/en/blog/itext-news/qa-get-know-itext-cto-raf-hens">Raf Hens, CTO</a>, iText. <em>“More and more companies see the benefit of PaaS and turn to pre-coded application components that are built into the platform, as they deliver a better quality and are easy to discover. Being a component vendor on a platform like this is important in reaching a broader market, and it’s only the beginning of what we have to offer.”</em></p> <p>iText DITO is immediately available in AWS Marketplace. To find iText DITO SDK/API in AWS Marketplace, <a href="https://aws.amazon.com/marketplace/pp/prodview-hsktyhfws5qxo">go here</a>.<br />  </p> </div> <div class="field field--name-field-tags field--type-entity-reference field__items"> <div class="field__label">Tags</div> <a href="/tags/itext-dito" property="schema:about" hreflang="en">iText DITO</a> <a href="/tags/aws" property="schema:about" hreflang="en">AWS</a> </div> <span class="a2a_kit a2a_kit_size_25 addtoany_list" data-a2a-url="https://itextpdf.com/blog/itext-news/itext-dito-available-aws-marketplace-offering-intuitive-pdf-generation" data-a2a-title="iText DITO Now Available in AWS Marketplace, Offering Intuitive PDF Design and Generation Capabilities to a Wider Audience "><a class="a2a_button_facebook"><i class="fa-brands fa-facebook-f fa-2x"></i></a><a class="a2a_button_twitter"><i class="fa-brands fa-twitter fa-2x"></i></a><a class="a2a_button_linkedin"><i class="fa-brands fa-linkedin-in fa-2x"></i></a><a class="a2a_button_whatsapp"><i class="fa-brands fa-whatsapp fa-2x"></i></a><a class="a2a_button_email"><i class="fa-solid fa-envelope fa-2x"></i></a></span> <div class="field field--name-field-article-type field--type-entity-reference field__items"> <div class="field__label">Article type</div> <a href="/blog-type/itext-news" hreflang="en">iText news</a> </div> <div class="field field--name-field-related-products field--type-entity-reference field__items"> <div class="field__label">Related products</div> <a href="/products/itext-dito" hreflang="en">iText DITO® </a> </div> <div class="field field--name-field-main-image field--type-entity-reference field__items"> <div class="field__label">Main image</div> <a href="/resources/media/images/teaser-aws-final" hreflang="en">teaser AWS final</a> </div> <div class="field field--name-field-promoted-to-home-page-text field--type-string field__items"> <div class="field__label">Promoted to home page text</div> iText DITO Now Available in AWS Marketplace, Offering Intuitive PDF Design and Generation Capabilities to a Wider Audience </div> Wed, 07 Jul 2021 11:56:27 +0000 karien.pype 15246 at https://itextpdf.com https://itextpdf.com/blog/itext-news/itext-dito-available-aws-marketplace-offering-intuitive-pdf-generation#comments Digitally sign a PDF using AWS KMS and iText https://itextpdf.com/blog/technical-notes/using-itext-and-aws-kms-digitally-sign-pdf-document <span property="schema:name" class="field field--name-title field--type-string field--label-hidden">Digitally sign a PDF using AWS KMS and iText</span> <span rel="schema:author" class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/ianmorris" typeof="schema:Person" property="schema:name" datatype="">ian.morris</span></span> <span property="schema:dateCreated" content="2021-01-29T13:10:15+00:00" class="field field--name-created field--type-created field--label-hidden"><time datetime="2021-01-29T14:10:15+01:00" title="Friday, January 29, 2021 - 14:10" class="datetime">Fri, 01/29/2021 - 14:10</time> </span> <div property="schema:text" class="wysiwyg-content clearfix text-formatted field field--name-body field--type-text-with-summary field__items"> <p><img alt="Using iText and AWS KMS to digitally sign a PDF document" data-entity-type="file" data-entity-uuid="bfa65b14-5399-4c21-9206-47bebca99327" src="/sites/default/files/inline-images/Blog%20banner%201140x300_Using%20iText%20and%20AWS%20KMS%20to%20digitally%20sign%20a%20PDF%20document.png" width="1140" height="300" loading="lazy" /></p> <h2 id="UsingiText7andAWSKMStodigitallysignaPDFdocument-Introduction">Introduction</h2> <p><em><strong>Note: This article was originally written for iText Core version 7.1.x. However, the text and code examples have been revised to account for the API changes and improvements to digital signing we introduced in iText Core version 8.</strong></em></p> <p>Here at iText we’ve long been involved with PDF digital signatures. We first published our <a href="https://itextpdf.com/en/resources/books/digital-signatures-pdf" rel="nofollow">digital signatures eBook</a> back in 2013, which provided a comprehensive overview of PDF features, industry standards and technology options relating to secure digital signatures, together with in-depth best practices, real-life examples, and code samples for PDF development.</p> <p>Since then, we’ve continued to promote the technology for secure PDF documents, as it provides integrity, authenticity, non-repudiation, and assurance of when a document was signed. We’ve also kept pace with advances in the field, supporting the PAdES framework and PDF 2.0, and updating our <a href="https://itextpdf.com/en/resources/books/addendum-digital-signatures-pdf-documents" rel="nofollow">Java and C# (.NET) code examples</a> to apply to the latest versions of iText.</p> <p>An essential component in creating a secure digital signature is the generation of an asymmetric key pair, consisting of both a public and a private key. There are a number of ways to generate such a key pair, but one of the most secure is the use of a hardware security module (or HSM). This is a physical computing device and is usually very expensive.</p> <h2 id="UsingiText7andAWSKMStodigitallysignaPDFdocument-HereComesaNewChallenger">Here Comes a New Challenger</h2> <p>However, Amazon Web Services now offers the generation of asymmetric keys as part of its Key Management Service (KMS) which makes it easy to create and manage cryptographic keys and control their use across a range of AWS services and in your applications. Similar to the symmetric key features that were previously available, asymmetric keys can be generated as customer master keys (CMKs) where the private portion never leaves the service, or as a data key where the private portion is returned to your calling application encrypted under a CMK.</p> <p>Since it’s a scalable service with no upfront charges, AWS KMS can be an attractive option for digitally signing PDFs. It’s not all plain sailing though. Since AWS KMS doesn’t store or associate digital certificates with asymmetric CMKs it creates, it’s not <em>directly</em> possible to use the asymmetric CMK for signing PDFs, as you would first have to generate a certificate for the public key of your AWS KMS signing key pair.</p> <p>This topic came up in a recent Stack Overflow question, and the comprehensive answer provided by Michael Klink led to this article which we hope many of you will benefit from. We’ll walk through the whole process of accessing the AWS KMS API to generate a digital signature, and then applying that signature to a PDF with iText. In addition, we’ll also point out some things you’ll need to consider if you plan to do mass-signing operations with AWS KMS.</p> <p>Of course, Amazon is not the only big player in cloud services, and so it should not be surprising that Google and Microsoft also provide similar functionality. Google has its <a href="https://cloud.google.com/security-key-management" rel="nofollow">Cloud Key Management</a> and Microsoft Azure offers their <a href="https://azure.microsoft.com/en-us/services/key-vault/" rel="nofollow">Key Vault</a>, both of which lower the cost of entry to using HSMs for cryptographic key management. While we won’t be covering them in this article, the process of signing a PDF using these services should be largely the same.</p> <p> Once again, we’ve worked with independent PDF expert and top StackOverflow contributor Michael Klink (<a href="https://stackoverflow.com/users/1729265/mkl" rel="nofollow">@mkl</a>) who kindly provided C# versions of his Java code examples from his original answer for our .NET users. Each code snippet included in this article has a link leading to the full Java/C# example on our Knowledge Base.</p> <h2 id="UsingiText7andAWSKMStodigitallysignaPDFdocument-Differentstrokesfordifferentfolks">Different strokes for different folks</h2> <p>A quick note before we continue. While the <a href="https://api.itextpdf.com/iText/java/latest/" rel="nofollow">Java</a> and <a href="https://api.itextpdf.com/iText/dotnet/latest/" rel="nofollow">C#</a> iText Core APIs are largely the same, there are a number of differences in the code examples here, in part due to certain differences in the Java and .NET <a href="https://docs.aws.amazon.com/kms/latest/APIReference/Welcome.html" rel="nofollow">AWS KMS APIs</a>. For example, the latter API uses the <code>async</code> pattern for .NET.</p> <p>In addition, genuine .NET classes were used for the creation of the self-signed certificate and there are some differences between the BouncyCastle Java and .NET APIs. So, the differences in the code are not just in its method name capitalization...</p> <p>Don’t worry though, as we’ll be pointing out these differences where they occur throughout the article</p> </div> <div class="field field--name-field-tags field--type-entity-reference field__items"> <div class="field__label">Tags</div> <a href="/tags/aws" property="schema:about" hreflang="en">AWS</a> <a href="/tags/kms" property="schema:about" hreflang="en">KMS</a> <a href="/tags/cloud" property="schema:about" hreflang="en">Cloud</a> <a href="/tags/digital-signatures" property="schema:about" hreflang="en">Digital signatures</a> <a href="/tags/certificates" property="schema:about" hreflang="en">Certificates</a> <a href="/tags/encryption" property="schema:about" hreflang="en">encryption</a> <a href="/tags/signing" property="schema:about" hreflang="en">signing</a> <a href="/tags/hsm" property="schema:about" hreflang="en">HSM</a> <a href="/tags/itext-8" property="schema:about" hreflang="en">iText 8</a> </div> <span class="a2a_kit a2a_kit_size_25 addtoany_list" data-a2a-url="https://itextpdf.com/blog/technical-notes/using-itext-and-aws-kms-digitally-sign-pdf-document" data-a2a-title="Digitally sign a PDF using AWS KMS and iText"><a class="a2a_button_facebook"><i class="fa-brands fa-facebook-f fa-2x"></i></a><a class="a2a_button_twitter"><i class="fa-brands fa-twitter fa-2x"></i></a><a class="a2a_button_linkedin"><i class="fa-brands fa-linkedin-in fa-2x"></i></a><a class="a2a_button_whatsapp"><i class="fa-brands fa-whatsapp fa-2x"></i></a><a class="a2a_button_email"><i class="fa-solid fa-envelope fa-2x"></i></a></span> <div class="field field--name-field-article-type field--type-entity-reference field__items"> <div class="field__label">Article type</div> <a href="/blog-type/technical-notes" hreflang="en">Technical notes</a> </div> <!-- strip all div's as they break the logic behind brick--no-vertical-padding --> <div> <div class="paragraph paragraph--text-area paragraph--text-area--default"> <h2></h2> <div class="wysiwyg-content clearfix text-formatted field field--name-field-text-area field--type-text-long field__items"> <h3 id="UsingiText7andAWSKMStodigitallysignaPDFdocument-SigningaPdfDocumentusingthedigitalsignaturereturnedbyAWSKMS">Signing a PdfDocument using the digital signature returned by AWS KMS</h3> <p><em>In the context of this article it is assumed that you have stored your credentials in the </em><code><em>default</em></code><em> section of your </em><code><em>~/.aws/credentials</em></code><em> </em><em>file and your region in the </em><code><em>default</em></code><em> </em><em>section of your </em><code><em>~/.aws/config</em></code><em> </em><em>file. Otherwise, you'll have to adapt the </em><code><em>KmsClient</em></code><em> instantiation or initialization in the following code examples.</em></p> </div> </div> <section class="brick jdoodle__brick"> <div class="brick__inner"> <div class="paragraph paragraph--jdoodle paragraph--jdoodle--default"> <h2></h2> <div class="wysiwyg-content clearfix text-formatted field field--name-field-text-area field--type-text-long field__items"> <h3 id="UsingiText7andAWSKMStodigitallysignaPDFdocument-GeneratingaCertificateforanAWSKMSKeyPair">Generating a Certificate for an AWS KMS Key Pair</h3> <p>As we noted above, AWS KMS signs using a plain asymmetric key pair and it does not provide a X.509 certificate for the public key. However, interoperable PDF signatures require a X.509 certificate for the public key, to establish trust in the signature. Thus, the first step to take for interoperable AWS KMS PDF signing is to generate an X.509 certificate for the public key of your AWS KMS signing key pair.</p> <p>For testing purposes, you can create a self-signed certificate using this Java helper method which is based on code from <a href="https://stackoverflow.com/a/43918337/1729265" rel="nofollow">this stack overflow answer</a>:</p> </div> &lt;div data-pym-src=&quot;https://itext.jdoodle.com/embed/itext&quot; data-java-libs=&quot;com.itextpdf:itext7-core:latest.release,org.slf4j:slf4j-api:1.7.28,org.slf4j:slf4j-simple:1.7.28,com.itextpdf:pdfocr-tesseract4:latest.release&quot; data-csharp-libs=&quot;itext7:latest.release&quot; data-show-upload=&quot;false&quot; data-hide-execute=&quot;true&quot; data-max-uploads=&quot;3&quot; data-max-file-size=&quot;5000000&quot;&gt; &lt;div data-script-type=&quot;java&quot;&gt;&lt;script&gt;public static X509Certificate generateSelfSignedCertificate(String keyId, String subjectDN, Function&lt;List&lt;SigningAlgorithmSpec&gt;, SigningAlgorithmSpec&gt; selector) throws IOException, GeneralSecurityException { long now = System.currentTimeMillis(); Date startDate = new Date(now); X500Name dnName = new X500Name(subjectDN); BigInteger certSerialNumber = new BigInteger(Long.toString(now)); // &lt;-- Using the current timestamp as the certificate serial number Calendar calendar = Calendar.getInstance(); calendar.setTime(startDate); calendar.add(Calendar.YEAR, 1); // &lt;-- 1 Yr validity Date endDate = calendar.getTime(); PublicKey publicKey = null; SigningAlgorithmSpec signingAlgorithmSpec = null; try ( KmsClient kmsClient = KmsClient.create() ) { GetPublicKeyResponse response = kmsClient.getPublicKey(GetPublicKeyRequest.builder().keyId(keyId).build()); SubjectPublicKeyInfo spki = SubjectPublicKeyInfo.getInstance(response.publicKey().asByteArray()); JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); publicKey = converter.getPublicKey(spki); List&lt;SigningAlgorithmSpec&gt; signingAlgorithms = response.signingAlgorithms(); signingAlgorithmSpec = selector.apply(signingAlgorithms); } JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(dnName, certSerialNumber, startDate, endDate, dnName, publicKey); ContentSigner contentSigner = new AwsKmsContentSigner(keyId, signingAlgorithmSpec); // Extensions -------------------------- // Basic Constraints BasicConstraints basicConstraints = new BasicConstraints(true); // &lt;-- true for CA, false for EndEntity certBuilder.addExtension(new ASN1ObjectIdentifier(&quot;2.5.29.19&quot;), true, basicConstraints); // Basic Constraints is usually marked as critical. // ------------------------------------- return new JcaX509CertificateConverter().setProvider(&quot;BC&quot;).getCertificate(certBuilder.build(contentSigner)); }&lt;/script&gt;&lt;/div&gt; &lt;div data-script-type=&quot;csharp&quot;&gt;&lt;script&gt;public static X509Certificate2 GenerateSelfSignedCertificate(string keyId, string subjectDN, Func&lt;List&lt;string&gt;, string&gt; selector) { string signingAlgorithm = null; using (var kmsClient = new AmazonKeyManagementServiceClient()) { GetPublicKeyRequest getPublicKeyRequest = new GetPublicKeyRequest() { KeyId = keyId }; GetPublicKeyResponse getPublicKeyResponse = kmsClient.GetPublicKeyAsync(getPublicKeyRequest).Result; List&lt;string&gt; signingAlgorithms = getPublicKeyResponse.SigningAlgorithms; signingAlgorithm = selector.Invoke(signingAlgorithms); byte[] spkiBytes = getPublicKeyResponse.PublicKey.ToArray(); CertificateRequest certificateRequest = null; X509SignatureGenerator simpleGenerator = null; string keySpecString = getPublicKeyResponse.CustomerMasterKeySpec.ToString(); if (keySpecString.StartsWith(&quot;ECC&quot;)) { ECDsa ecdsa = ECDsa.Create(); int bytesRead = 0; ecdsa.ImportSubjectPublicKeyInfo(new ReadOnlySpan&lt;byte&gt;(spkiBytes), out bytesRead); certificateRequest = new CertificateRequest(subjectDN, ecdsa, GetHashAlgorithmName(signingAlgorithm)); simpleGenerator = X509SignatureGenerator.CreateForECDsa(ecdsa); } else if (keySpecString.StartsWith(&quot;RSA&quot;)) { RSA rsa = RSA.Create(); int bytesRead = 0; rsa.ImportSubjectPublicKeyInfo(new ReadOnlySpan&lt;byte&gt;(spkiBytes), out bytesRead); RSASignaturePadding rsaSignaturePadding = GetSignaturePadding(signingAlgorithm); certificateRequest = new CertificateRequest(subjectDN, rsa, GetHashAlgorithmName(signingAlgorithm), rsaSignaturePadding); simpleGenerator = X509SignatureGenerator.CreateForRSA(rsa, rsaSignaturePadding); } else { throw new ArgumentException(&quot;Cannot determine encryption algorithm for &quot; + keySpecString, nameof(keyId)); } X509SignatureGenerator generator = new SignatureGenerator(keyId, signingAlgorithm, simpleGenerator); X509Certificate2 certificate = certificateRequest.Create(new X500DistinguishedName(subjectDN), generator, System.DateTimeOffset.Now, System.DateTimeOffset.Now.AddYears(2), new byte[] { 17 }); return certificate; } } public static HashAlgorithmName GetHashAlgorithmName(string signingAlgorithm) { if (signingAlgorithm.Contains(&quot;SHA_256&quot;)) { return HashAlgorithmName.SHA256; } else if (signingAlgorithm.Contains(&quot;SHA_384&quot;)) { return HashAlgorithmName.SHA384; } else if (signingAlgorithm.Contains(&quot;SHA_512&quot;)) { return HashAlgorithmName.SHA512; } else { throw new ArgumentException(&quot;Cannot determine hash algorithm for &quot; + signingAlgorithm, nameof(signingAlgorithm)); } } public static RSASignaturePadding GetSignaturePadding(string signingAlgorithm) { if (signingAlgorithm.StartsWith(&quot;RSASSA_PKCS1_V1_5&quot;)) { return RSASignaturePadding.Pkcs1; } else if (signingAlgorithm.StartsWith(&quot;RSASSA_PSS&quot;)) { return RSASignaturePadding.Pss; } else { return null; } } class SignatureGenerator : X509SignatureGenerator { public SignatureGenerator(string keyId, string signingAlgorithm, X509SignatureGenerator simpleGenerator) { this.keyId = keyId; this.signingAlgorithm = signingAlgorithm; this.simpleGenerator = simpleGenerator; } public override byte[] GetSignatureAlgorithmIdentifier(HashAlgorithmName hashAlgorithm) { HashAlgorithmName hashAlgorithmHere = GetHashAlgorithmName(signingAlgorithm); if (hashAlgorithm != hashAlgorithmHere) { throw new ArgumentException(&quot;Hash algorithm &quot; + hashAlgorithm + &quot;does not match signing algorithm &quot; + signingAlgorithm, nameof(hashAlgorithm)); } return simpleGenerator.GetSignatureAlgorithmIdentifier(hashAlgorithm); } public override byte[] SignData(byte[] data, HashAlgorithmName hashAlgorithm) { HashAlgorithmName hashAlgorithmHere = GetHashAlgorithmName(signingAlgorithm); if (hashAlgorithm != hashAlgorithmHere) { throw new ArgumentException(&quot;Hash algorithm &quot; + hashAlgorithm + &quot;does not match signing algorithm &quot; + signingAlgorithm, nameof(hashAlgorithm)); } using (var kmsClient = new AmazonKeyManagementServiceClient()) { SignRequest signRequest = new SignRequest() { SigningAlgorithm = signingAlgorithm, KeyId = keyId, MessageType = MessageType.RAW, Message = new MemoryStream(data) }; SignResponse signResponse = kmsClient.SignAsync(signRequest).Result; return signResponse.Signature.ToArray(); } } protected override PublicKey BuildPublicKey() { return simpleGenerator.PublicKey; } string keyId; string signingAlgorithm; X509SignatureGenerator simpleGenerator; }&lt;/script&gt;&lt;/div&gt; &lt;/div&gt; </div> </div> </section> <section class="brick jdoodle__brick"> <div class="brick__inner"> <div class="paragraph paragraph--jdoodle paragraph--jdoodle--default"> <h2></h2> <div class="wysiwyg-content clearfix text-formatted field field--name-field-text-area field--type-text-long field__items"> <p><em><a href="https://kb.itextpdf.com/itext/using-itext-7-and-aws-kms-to-digitally-sign-a-pdf-">CertificateUtils helper method</a></em></p> <p>.NET offers its own means for the creation of certificate requests and self-signed certificates, the <code>CertificateRequest</code> class.</p> <p>As with the BouncyCastle implementation in the Java example, this class also has the actual signature creation (for the self-signed certificate) delegated to a helper, which here is an <code>X509SignatureGenerator</code> instance. Obviously, .NET does not have a ready-to-use variant of that class for AWS KMS signing, so we have to provide one ourselves, the inner class <code>SignatureGenerator</code> in the .NET example. Fortunately, we can re-use .NET variants of <code>X509SignatureGenerator</code> for all methods except the actual <code>SignData</code> signing method.</p> <p>Returning to our Java example, the <code>AwsKmsContentSigner</code> class used in the code above is this implementation of the BouncyCastle interface <code>ContentSigner</code>:</p> <pre> <code class="language-java"><div class="geshifilter"><table class="java geshifilter-java"><tbody><tr class="li1"><td class="ln"><pre class="de1">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 </pre></td><td class="de1"><pre class="de1"><span class="kw1">public</span> <span class="kw1">class</span> AwsKmsContentSigner <span class="kw1">implements</span> ContentSigner <span class="br0">{</span>     <span class="kw1">final</span> <span class="kw3">ByteArrayOutputStream</span> outputStream <span class="sy0">=</span> <span class="kw1">new</span> <span class="kw3">ByteArrayOutputStream</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>     <span class="kw1">final</span> <span class="kw3">String</span> keyId<span class="sy0">;</span>     <span class="kw1">final</span> SigningAlgorithmSpec signingAlgorithmSpec<span class="sy0">;</span>     <span class="kw1">final</span> AlgorithmIdentifier signatureAlgorithm<span class="sy0">;</span>       <span class="kw1">public</span> AwsKmsContentSigner<span class="br0">(</span><span class="kw3">String</span> keyId, SigningAlgorithmSpec signingAlgorithmSpec<span class="br0">)</span> <span class="br0">{</span>         <span class="kw1">this</span>.<span class="me1">keyId</span> <span class="sy0">=</span> keyId<span class="sy0">;</span>         <span class="kw1">this</span>.<span class="me1">signingAlgorithmSpec</span> <span class="sy0">=</span> signingAlgorithmSpec<span class="sy0">;</span>         <span class="kw3">String</span> signatureAlgorithmName <span class="sy0">=</span> signingAlgorithmNameBySpec.<span class="me1">get</span><span class="br0">(</span>signingAlgorithmSpec<span class="br0">)</span><span class="sy0">;</span>         <span class="kw1">if</span> <span class="br0">(</span>signatureAlgorithmName <span class="sy0">==</span> <span class="kw2">null</span><span class="br0">)</span>             <span class="kw1">throw</span> <span class="kw1">new</span> <span class="kw3">IllegalArgumentException</span><span class="br0">(</span><span class="st0">"Unknown signature algorithm "</span> <span class="sy0">+</span> signingAlgorithmSpec<span class="br0">)</span><span class="sy0">;</span>         <span class="kw1">this</span>.<span class="me1">signatureAlgorithm</span> <span class="sy0">=</span> <span class="kw1">new</span> DefaultSignatureAlgorithmIdentifierFinder<span class="br0">(</span><span class="br0">)</span>.<span class="me1">find</span><span class="br0">(</span>signatureAlgorithmName<span class="br0">)</span><span class="sy0">;</span>     <span class="br0">}</span>       @Override     <span class="kw1">public</span> <span class="kw4">byte</span><span class="br0">[</span><span class="br0">]</span> getSignature<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>         <span class="kw1">try</span> <span class="br0">(</span>   KmsClient kmsClient <span class="sy0">=</span> KmsClient.<span class="me1">create</span><span class="br0">(</span><span class="br0">)</span> <span class="br0">)</span> <span class="br0">{</span>             SignRequest signRequest <span class="sy0">=</span> SignRequest.<span class="me1">builder</span><span class="br0">(</span><span class="br0">)</span>                     .<span class="me1">signingAlgorithm</span><span class="br0">(</span>signingAlgorithmSpec<span class="br0">)</span>                     .<span class="me1">keyId</span><span class="br0">(</span>keyId<span class="br0">)</span>                     .<span class="me1">messageType</span><span class="br0">(</span>MessageType.<span class="me1">RAW</span><span class="br0">)</span>                     .<span class="me1">message</span><span class="br0">(</span>SdkBytes.<span class="me1">fromByteArray</span><span class="br0">(</span>outputStream.<span class="me1">toByteArray</span><span class="br0">(</span><span class="br0">)</span><span class="br0">)</span><span class="br0">)</span>                     .<span class="me1">build</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>             SignResponse signResponse <span class="sy0">=</span> kmsClient.<span class="me1">sign</span><span class="br0">(</span>signRequest<span class="br0">)</span><span class="sy0">;</span>             SdkBytes signatureSdkBytes <span class="sy0">=</span> signResponse.<span class="me1">signature</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>             <span class="kw1">return</span> signatureSdkBytes.<span class="me1">asByteArray</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>         <span class="br0">}</span> <span class="kw1">finally</span> <span class="br0">{</span>             outputStream.<span class="me1">reset</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>         <span class="br0">}</span>     <span class="br0">}</span>       @Override     <span class="kw1">public</span> <span class="kw3">OutputStream</span> getOutputStream<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>         <span class="kw1">return</span> outputStream<span class="sy0">;</span>     <span class="br0">}</span>       @Override     <span class="kw1">public</span> AlgorithmIdentifier getAlgorithmIdentifier<span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>         <span class="kw1">return</span> signatureAlgorithm<span class="sy0">;</span>     <span class="br0">}</span>       <span class="kw1">final</span> <span class="kw1">static</span> Map<span class="sy0">&lt;</span>SigningAlgorithmSpec, String<span class="sy0">&gt;</span> signingAlgorithmNameBySpec<span class="sy0">;</span>       <span class="kw1">static</span> <span class="br0">{</span>         signingAlgorithmNameBySpec <span class="sy0">=</span> <span class="kw1">new</span> HashMap<span class="sy0">&lt;&gt;</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span>         signingAlgorithmNameBySpec.<span class="me1">put</span><span class="br0">(</span>SigningAlgorithmSpec.<span class="me1">ECDSA_SHA_256</span>, <span class="st0">"SHA256withECDSA"</span><span class="br0">)</span><span class="sy0">;</span>         signingAlgorithmNameBySpec.<span class="me1">put</span><span class="br0">(</span>SigningAlgorithmSpec.<span class="me1">ECDSA_SHA_384</span>, <span class="st0">"SHA384withECDSA"</span><span class="br0">)</span><span class="sy0">;</span>         signingAlgorithmNameBySpec.<span class="me1">put</span><span class="br0">(</span>SigningAlgorithmSpec.<span class="me1">ECDSA_SHA_512</span>, <span class="st0">"SHA512withECDSA"</span><span class="br0">)</span><span class="sy0">;</span>         signingAlgorithmNameBySpec.<span class="me1">put</span><span class="br0">(</span>SigningAlgorithmSpec.<span class="me1">RSASSA_PKCS1_V1_5_SHA_256</span>, <span class="st0">"SHA256withRSA"</span><span class="br0">)</span><span class="sy0">;</span>         signingAlgorithmNameBySpec.<span class="me1">put</span><span class="br0">(</span>SigningAlgorithmSpec.<span class="me1">RSASSA_PKCS1_V1_5_SHA_384</span>, <span class="st0">"SHA384withRSA"</span><span class="br0">)</span><span class="sy0">;</span>         signingAlgorithmNameBySpec.<span class="me1">put</span><span class="br0">(</span>SigningAlgorithmSpec.<span class="me1">RSASSA_PKCS1_V1_5_SHA_512</span>, <span class="st0">"SHA512withRSA"</span><span class="br0">)</span><span class="sy0">;</span>         signingAlgorithmNameBySpec.<span class="me1">put</span><span class="br0">(</span>SigningAlgorithmSpec.<span class="me1">RSASSA_PSS_SHA_256</span>, <span class="st0">"SHA256withRSAandMGF1"</span><span class="br0">)</span><span class="sy0">;</span>         signingAlgorithmNameBySpec.<span class="me1">put</span><span class="br0">(</span>SigningAlgorithmSpec.<span class="me1">RSASSA_PSS_SHA_384</span>, <span class="st0">"SHA384withRSAandMGF1"</span><span class="br0">)</span><span class="sy0">;</span>         signingAlgorithmNameBySpec.<span class="me1">put</span><span class="br0">(</span>SigningAlgorithmSpec.<span class="me1">RSASSA_PSS_SHA_512</span>, <span class="st0">"SHA512withRSAandMGF1"</span><span class="br0">)</span><span class="sy0">;</span>     <span class="br0">}</span> <span class="br0">}</span></pre></td></tr></tbody></table></div></code></pre> <p> </p> </div> </div> </div> </section> <section class="brick jdoodle__brick"> <div class="brick__inner"> <div class="paragraph paragraph--jdoodle paragraph--jdoodle--default"> <h2></h2> <div class="wysiwyg-content clearfix text-formatted field field--name-field-text-area field--type-text-long field__items"> <p><em><a href="https://kb.itextpdf.com/itext/using-itext-7-and-aws-kms-to-digitally-sign-a-pd-1">AwsContentSigner</a></em></p> <p>For production purposes however, you'll usually want to use a certificate signed by a trusted Certificate Authority (CA). Similar to the example above you can create and sign a <em>certificate request</em> for your AWS KMS public key, send it to your CA of choice, and get back the certificate to use from them.</p> <p>There’s no .NET equivalent of <code>AwsKmsContentSigner.java</code> since our .NET version of <code>CertificateUtils</code> doesn’t use BouncyCastle for certificate generation but instead uses the .NET <code>X509SignatureGenerator</code>, thus no BouncyCastle <code>ContentSigner</code> implementation is required.</p> <h3 id="UsingiText7andAWSKMStodigitallysignaPDFdocument-SigningaPDFusinganAWSKMSKeyPair">Signing a PDF using an AWS KMS Key Pair</h3> <p>To sign a PDF with iText you need an implementation of the iText <code>IExternalSignature</code> or <code>IExternalSignatureContainer</code> interface. Here we use the former:</p> </div> &lt;div data-pym-src=&quot;https://itext.jdoodle.com/embed/itext&quot; data-java-libs=&quot;com.itextpdf:itext7-core:latest.release,org.slf4j:slf4j-api:1.7.28,org.slf4j:slf4j-simple:1.7.28,com.itextpdf:pdfocr-tesseract4:latest.release&quot; data-csharp-libs=&quot;itext7:latest.release&quot; data-show-upload=&quot;false&quot; data-hide-execute=&quot;true&quot; data-max-uploads=&quot;3&quot; data-max-file-size=&quot;5000000&quot;&gt; &lt;div data-script-type=&quot;java&quot;&gt;&lt;script&gt;public class AwsKmsSignature implements IExternalSignature { public AwsKmsSignature(String keyId) { this(keyId, a -&gt; a != null &amp;&amp; a.size() &gt; 0 ? a.get(0) : null); } public AwsKmsSignature(String keyId, Function&lt;List&lt;SigningAlgorithmSpec&gt;, SigningAlgorithmSpec&gt; selector) { this.keyId = keyId; try ( KmsClient kmsClient = KmsClient.create() ) { GetPublicKeyRequest getPublicKeyRequest = GetPublicKeyRequest.builder() .keyId(keyId) .build(); GetPublicKeyResponse getPublicKeyResponse = kmsClient.getPublicKey(getPublicKeyRequest); signingAlgorithmSpec = selector.apply(getPublicKeyResponse.signingAlgorithms()); switch(signingAlgorithmSpec) { case ECDSA_SHA_256: case ECDSA_SHA_384: case ECDSA_SHA_512: case RSASSA_PKCS1_V1_5_SHA_256: case RSASSA_PKCS1_V1_5_SHA_384: case RSASSA_PKCS1_V1_5_SHA_512: case RSASSA_PSS_SHA_256: case RSASSA_PSS_SHA_384: case RSASSA_PSS_SHA_512: break; default: throw new IllegalArgumentException(String.format(&quot;Unknown signing algorithm: %s&quot;, signingAlgorithmSpec)); } } } @Override public String getDigestAlgorithmName() { switch(signingAlgorithmSpec) { case ECDSA_SHA_256: case RSASSA_PKCS1_V1_5_SHA_256: case RSASSA_PSS_SHA_256: return &quot;SHA-256&quot;; case ECDSA_SHA_384: case RSASSA_PKCS1_V1_5_SHA_384: case RSASSA_PSS_SHA_384: return &quot;SHA-384&quot;; case ECDSA_SHA_512: case RSASSA_PKCS1_V1_5_SHA_512: case RSASSA_PSS_SHA_512: return &quot;SHA-512&quot;; default: return null; } } @Override public String getSignatureAlgorithmName() { switch(signingAlgorithmSpec) { case ECDSA_SHA_256: case ECDSA_SHA_384: case ECDSA_SHA_512: return &quot;ECDSA&quot;; case RSASSA_PKCS1_V1_5_SHA_256: case RSASSA_PKCS1_V1_5_SHA_384: case RSASSA_PKCS1_V1_5_SHA_512: return &quot;RSA&quot;; case RSASSA_PSS_SHA_256: case RSASSA_PSS_SHA_384: case RSASSA_PSS_SHA_512: return &quot;RSASSA-PSS&quot;; default: return null; } } @Override public ISignatureMechanismParams getSignatureMechanismParameters() { switch (signingAlgorithmSpec) { case RSASSA_PSS_SHA_256: return RSASSAPSSMechanismParams.createForDigestAlgorithm(&quot;SHA-256&quot;); case RSASSA_PSS_SHA_384: return RSASSAPSSMechanismParams.createForDigestAlgorithm(&quot;SHA-384&quot;); case RSASSA_PSS_SHA_512: return RSASSAPSSMechanismParams.createForDigestAlgorithm(&quot;SHA-512&quot;); case ECDSA_SHA_256: case ECDSA_SHA_384: case ECDSA_SHA_512: case RSASSA_PKCS1_V1_5_SHA_256: case RSASSA_PKCS1_V1_5_SHA_384: case RSASSA_PKCS1_V1_5_SHA_512: default: return null; } } @Override public byte[] sign(byte[] message) throws GeneralSecurityException { try ( KmsClient kmsClient = KmsClient.create() ) { SignRequest signRequest = SignRequest.builder() .signingAlgorithm(signingAlgorithmSpec) .keyId(keyId) .messageType(MessageType.RAW) .message(SdkBytes.fromByteArray(message)) .build(); SignResponse signResponse = kmsClient.sign(signRequest); return signResponse.signature().asByteArray(); } } final String keyId; final SigningAlgorithmSpec signingAlgorithmSpec; }&lt;/script&gt;&lt;/div&gt; &lt;div data-script-type=&quot;csharp&quot;&gt;&lt;script&gt;public class AwsKmsSignature : IExternalSignature { public AwsKmsSignature(string keyId, Func&lt;List&lt;string&gt;, string&gt; selector) { this.keyId = keyId; using (var kmsClient = new AmazonKeyManagementServiceClient()) { GetPublicKeyRequest getPublicKeyRequest = new GetPublicKeyRequest() { KeyId = keyId }; GetPublicKeyResponse getPublicKeyResponse = kmsClient.GetPublicKeyAsync(getPublicKeyRequest).Result; List&lt;string&gt; signingAlgorithms = getPublicKeyResponse.SigningAlgorithms; signingAlgorithm = selector.Invoke(signingAlgorithms); switch(signingAlgorithm) { case &quot;ECDSA_SHA_256&quot;: case &quot;ECDSA_SHA_384&quot;: case &quot;ECDSA_SHA_512&quot;: case &quot;RSASSA_PKCS1_V1_5_SHA_256&quot;: case &quot;RSASSA_PKCS1_V1_5_SHA_384&quot;: case &quot;RSASSA_PKCS1_V1_5_SHA_512&quot;: case &quot;RSASSA_PSS_SHA_256&quot;: case &quot;RSASSA_PSS_SHA_384&quot;: case &quot;RSASSA_PSS_SHA_512&quot;: break; default: throw new ArgumentException(String.Format(&quot;Unknown signing algorithm: {0}&quot;, signingAlgorithm)); } } } public string GetSignatureAlgorithmName() { switch (signingAlgorithm) { case &quot;ECDSA_SHA_256&quot;: case &quot;ECDSA_SHA_384&quot;: case &quot;ECDSA_SHA_512&quot;: return &quot;ECDSA&quot;; case &quot;RSASSA_PKCS1_V1_5_SHA_256&quot;: case &quot;RSASSA_PKCS1_V1_5_SHA_384&quot;: case &quot;RSASSA_PKCS1_V1_5_SHA_512&quot;: return &quot;RSA&quot;; case &quot;RSASSA_PSS_SHA_256&quot;: case &quot;RSASSA_PSS_SHA_384&quot;: case &quot;RSASSA_PSS_SHA_512&quot;: return &quot;RSASSA-PSS&quot;; default: return null; } } public string GetDigestAlgorithmName() { switch (signingAlgorithm) { case &quot;ECDSA_SHA_256&quot;: case &quot;RSASSA_PKCS1_V1_5_SHA_256&quot;: case &quot;RSASSA_PSS_SHA_256&quot;: return &quot;SHA-256&quot;; case &quot;ECDSA_SHA_384&quot;: case &quot;RSASSA_PKCS1_V1_5_SHA_384&quot;: case &quot;RSASSA_PSS_SHA_384&quot;: return &quot;SHA-384&quot;; case &quot;ECDSA_SHA_512&quot;: case &quot;RSASSA_PKCS1_V1_5_SHA_512&quot;: case &quot;RSASSA_PSS_SHA_512&quot;: return &quot;SHA-512&quot;; default: return null; } } public ISignatureMechanismParams GetSignatureMechanismParameters() { switch (signingAlgorithm) { case &quot;RSASSA_PSS_SHA_256&quot;: return RSASSAPSSMechanismParams.CreateForDigestAlgorithm(&quot;SHA-256&quot;); case &quot;RSASSA_PSS_SHA_384&quot;: return RSASSAPSSMechanismParams.CreateForDigestAlgorithm(&quot;SHA-384&quot;); case &quot;RSASSA_PSS_SHA_512&quot;: return RSASSAPSSMechanismParams.CreateForDigestAlgorithm(&quot;SHA-512&quot;); case &quot;ECDSA_SHA_256&quot;: case &quot;ECDSA_SHA_384&quot;: case &quot;ECDSA_SHA_512&quot;: case &quot;RSASSA_PKCS1_V1_5_SHA_256&quot;: case &quot;RSASSA_PKCS1_V1_5_SHA_384&quot;: case &quot;RSASSA_PKCS1_V1_5_SHA_512&quot;: default: return null; } } public byte[] Sign(byte[] message) { using (var kmsClient = new AmazonKeyManagementServiceClient()) { SignRequest signRequest = new SignRequest() { SigningAlgorithm = signingAlgorithm, KeyId=keyId, MessageType=MessageType.RAW, Message=new MemoryStream(message) }; SignResponse signResponse = kmsClient.SignAsync(signRequest).Result; return signResponse.Signature.ToArray(); } } string keyId; string signingAlgorithm; } }&lt;/script&gt;&lt;/div&gt; &lt;/div&gt; </div> </div> </section> <section class="brick jdoodle__brick"> <div class="brick__inner"> <div class="paragraph paragraph--jdoodle paragraph--jdoodle--default"> <h2></h2> <div class="wysiwyg-content clearfix text-formatted field field--name-field-text-area field--type-text-long field__items"> <p><a href="https://kb.itextpdf.com/itext/using-itext-7-and-aws-kms-to-digitally-sign-a-pd-2"><em>AwsKmsSignature</em></a></p> <p>In the constructor we select a signing algorithm available for the key in question. The single argument constructor simply takes the first algorithm from the available ones, the double argument constructor allows you to select a specific one.</p> <p><code>getDigestAlgorithmName</code> returns the name of the respective part of the signature algorithm, <code>getSignatureMechanismParameters</code> returns additional parameters where required, and <code>sign</code> simply creates a signature.</p> <p>For .NET, the <code>AwsKmsSignature</code> class could be ported from Java with very little changes required.</p> <h3 id="UsingiText7andAWSKMStodigitallysignaPDFdocument-PuttingItIntoAction">Putting It Into Action</h3> <p>Assuming your AWS KMS signing key pair has the alias <code>SigningExamples-ECC_NIST_P256</code> <strong><em>and is indeed an ECC_NIST_P256 key pair</em></strong> you can use the code above to sign a PDF like this:</p> </div> &lt;div data-pym-src=&quot;https://itext.jdoodle.com/embed/itext&quot; data-java-libs=&quot;com.itextpdf:itext7-core:latest.release,org.slf4j:slf4j-api:1.7.28,org.slf4j:slf4j-simple:1.7.28,com.itextpdf:pdfocr-tesseract4:latest.release&quot; data-csharp-libs=&quot;itext7:latest.release&quot; data-show-upload=&quot;false&quot; data-hide-execute=&quot;true&quot; data-max-uploads=&quot;3&quot; data-max-file-size=&quot;5000000&quot;&gt; &lt;div data-script-type=&quot;java&quot;&gt;&lt;script&gt;BouncyCastleProvider provider = new BouncyCastleProvider(); Security.addProvider(provider); String keyId = &quot;alias/SigningExamples-ECC_NIST_P256&quot;; AwsKmsSignature signature = new AwsKmsSignature(keyId); Certificate certificate = CertificateUtils.generateSelfSignedCertificate(keyId, &quot;CN=AWS KMS PDF Signing Test,OU=mkl tests,O=mkl&quot;); try ( PdfReader pdfReader = new PdfReader(PDF_TO_SIGN); OutputStream result = new FileOutputStream(SIGNED_PDF)) { PdfSigner pdfSigner = new PdfSigner(pdfReader, result, new StampingProperties().useAppendMode()); IExternalDigest externalDigest = new BouncyCastleDigest(); pdfSigner.signDetached(externalDigest , signature, new Certificate[] {certificate}, null, null, null, 0, CryptoStandard.CMS); }&lt;/script&gt;&lt;/div&gt; &lt;div data-script-type=&quot;csharp&quot;&gt;&lt;script&gt;string keyId = &quot;alias/SigningExamples-ECC_NIST_P256&quot;; Func&lt;System.Collections.Generic.List&lt;string&gt;, string&gt; selector = list =&gt; list.Find(name =&gt; name.StartsWith(&quot;ECDSA_SHA_256&quot;)); AwsKmsSignature signature = new AwsKmsSignature(keyId, selector); System.Security.Cryptography.X509Certificates.X509Certificate2 certificate2 = CertificateUtils.generateSelfSignedCertificate( keyId, &quot;CN=AWS KMS PDF Signing Test ECDSA,OU=mkl tests,O=mkl&quot;, selector ); X509Certificate certificate = new X509Certificate(X509CertificateStructure.GetInstance(certificate2.RawData)); using (PdfReader pdfReader = new PdfReader(PDF_TO_SIGN)) using (FileStream result = File.Create(SIGNED_PDF)) { PdfSigner pdfSigner = new PdfSigner(pdfReader, result, new StampingProperties().UseAppendMode()); pdfSigner.SignDetached(signature, new X509Certificate[] { certificate }, null, null, null, 0, CryptoStandard.CMS); }&lt;/script&gt;&lt;/div&gt; &lt;/div&gt; </div> </div> </section> <section class="brick jdoodle__brick"> <div class="brick__inner"> <div class="paragraph paragraph--jdoodle paragraph--jdoodle--default"> <h2></h2> <div class="wysiwyg-content clearfix text-formatted field field--name-field-text-area field--type-text-long field__items"> <p><em><a href="https://kb.itextpdf.com/itext/using-itext-7-and-aws-kms-to-digitally-sign-a-pd-3">TestSignSimple</a> test </em><code>testSignSimpleEcdsa</code></p> <h3 id="UsingiText7andAWSKMStodigitallysignaPDFdocument-SigningaPDFUsinganAWSKMSKeyPair:Redux">Signing a PDF Using an AWS KMS Key Pair: Redux</h3> <p>Above we used an implementation of <code>IExternalSignature</code> for signing. While that is the easiest way, it has some drawbacks: The class <code>PdfPKCS7</code> used in this case is not very flexible, e.g. it doesn't allow to to add custom attributes to the signature.</p> <p>To not be subject to these limitations, we here use an implementation of <code>IExternalSignatureContainer</code> instead in which we build the complete CMS signature container ourselves using only BouncyCastle functionality.</p> <p>For .NET, while the <code>AwsKmsSignatureContainer</code> class uses BouncyCastle to build the CMS signature container to embed just like in the Java version, there are certain differences in the .NET BouncyCastle API. In particular one does not use an instance of <code>ContentSigner</code> for the actual signing but an instance of <code>ISignatureFactory</code>; that interface represents a factory of <code>IStreamCalculator</code> instances which in their function are equivalent to the <code>ContentSigner</code> in Java. The implementations of these interfaces are <code>AwsKmsSignatureFactory</code> and <code>AwsKmsStreamCalculator</code> in the .NET example.</p> </div> &lt;div data-pym-src=&quot;https://itext.jdoodle.com/embed/itext&quot; data-java-libs=&quot;com.itextpdf:itext7-core:latest.release,org.slf4j:slf4j-api:1.7.28,org.slf4j:slf4j-simple:1.7.28,com.itextpdf:pdfocr-tesseract4:latest.release&quot; data-csharp-libs=&quot;itext7:latest.release&quot; data-show-upload=&quot;false&quot; data-hide-execute=&quot;true&quot; data-max-uploads=&quot;3&quot; data-max-file-size=&quot;5000000&quot;&gt; &lt;div data-script-type=&quot;java&quot;&gt;&lt;script&gt;public class AwsKmsSignatureContainer implements IExternalSignatureContainer { public AwsKmsSignatureContainer(X509Certificate x509Certificate, String keyId) { this(x509Certificate, keyId, a -&gt; a != null &amp;&amp; a.size() &gt; 0 ? a.get(0) : null); } public AwsKmsSignatureContainer(X509Certificate x509Certificate, String keyId, Function&lt;List&lt;SigningAlgorithmSpec&gt;, SigningAlgorithmSpec&gt; selector) { this.x509Certificate = x509Certificate; this.keyId = keyId; try ( KmsClient kmsClient = KmsClient.create() ) { GetPublicKeyRequest getPublicKeyRequest = GetPublicKeyRequest.builder() .keyId(keyId) .build(); GetPublicKeyResponse getPublicKeyResponse = kmsClient.getPublicKey(getPublicKeyRequest); signingAlgorithmSpec = selector.apply(getPublicKeyResponse.signingAlgorithms()); if (signingAlgorithmSpec == null) throw new IllegalArgumentException(&quot;KMS key has no signing algorithms&quot;); contentSigner = new AwsKmsContentSigner(keyId, signingAlgorithmSpec); } } @Override public byte[] sign(InputStream data) throws GeneralSecurityException { try { CMSTypedData msg = new CMSTypedDataInputStream(data); X509CertificateHolder signCert = new X509CertificateHolder(x509Certificate.getEncoded()); CMSSignedDataGenerator gen = new CMSSignedDataGenerator(); gen.addSignerInfoGenerator( new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(&quot;BC&quot;).build()) .build(contentSigner, signCert)); gen.addCertificates(new JcaCertStore(Collections.singleton(signCert))); CMSSignedData sigData = gen.generate(msg, false); return sigData.getEncoded(); } catch (IOException | OperatorCreationException | CMSException e) { throw new GeneralSecurityException(e); } } @Override public void modifySigningDictionary(PdfDictionary signDic) { signDic.put(PdfName.Filter, new PdfName(&quot;MKLx_AWS_KMS_SIGNER&quot;)); signDic.put(PdfName.SubFilter, PdfName.Adbe_pkcs7_detached); } final X509Certificate x509Certificate; final String keyId; final SigningAlgorithmSpec signingAlgorithmSpec; final ContentSigner contentSigner; class CMSTypedDataInputStream implements CMSTypedData { InputStream in; public CMSTypedDataInputStream(InputStream is) { in = is; } @Override public ASN1ObjectIdentifier getContentType() { return PKCSObjectIdentifiers.data; } @Override public Object getContent() { return in; } @Override public void write(OutputStream out) throws IOException, CMSException { byte[] buffer = new byte[8 * 1024]; int read; while ((read = in.read(buffer)) != -1) { out.write(buffer, 0, read); } in.close(); } } }&lt;/script&gt;&lt;/div&gt; &lt;div data-script-type=&quot;csharp&quot;&gt;&lt;script&gt; public class AwsKmsSignatureContainer : IExternalSignatureContainer { public AwsKmsSignatureContainer(X509Certificate x509Certificate, string keyId, Func&lt;List&lt;string&gt;, string&gt; selector) { this.x509Certificate = x509Certificate; this.keyId = keyId; using (var kmsClient = new AmazonKeyManagementServiceClient()) { GetPublicKeyRequest getPublicKeyRequest = new GetPublicKeyRequest() { KeyId = keyId }; GetPublicKeyResponse getPublicKeyResponse = kmsClient.GetPublicKeyAsync(getPublicKeyRequest).Result; List&lt;string&gt; signingAlgorithms = getPublicKeyResponse.SigningAlgorithms; this.signingAlgorithm = selector.Invoke(signingAlgorithms); if (signingAlgorithm == null) throw new ArgumentException(&quot;KMS key has no signing algorithms&quot;, nameof(keyId)); signatureFactory = new AwsKmsSignatureFactory(keyId, signingAlgorithm); } } public void ModifySigningDictionary(PdfDictionary signDic) { signDic.Put(PdfName.Filter, new PdfName(&quot;MKLx_AWS_KMS_SIGNER&quot;)); signDic.Put(PdfName.SubFilter, PdfName.Adbe_pkcs7_detached); } public byte[] Sign(Stream data) { CmsProcessable msg = new CmsProcessableInputStream(data); CmsSignedDataGenerator gen = new CmsSignedDataGenerator(); SignerInfoGenerator signerInfoGenerator = new SignerInfoGeneratorBuilder() .WithSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator()) .Build(signatureFactory, x509Certificate); gen.AddSignerInfoGenerator(signerInfoGenerator); IStore&lt;X509Certificate&gt; store =CollectionUtilities.CreateStore(new List&lt;X509Certificate&gt; { x509Certificate }); gen.AddCertificates(store); CmsSignedData sigData = gen.Generate(msg, false); return sigData.GetEncoded(); } X509Certificate x509Certificate; String keyId; string signingAlgorithm; ISignatureFactory signatureFactory; } class AwsKmsSignatureFactory : ISignatureFactory { private string keyId; private string signingAlgorithm; private AlgorithmIdentifier signatureAlgorithm; public AwsKmsSignatureFactory(string keyId, string signingAlgorithm) { this.keyId = keyId; this.signingAlgorithm = signingAlgorithm; string signatureAlgorithmName = signingAlgorithmNameBySpec[signingAlgorithm]; if (signatureAlgorithmName == null) throw new ArgumentException(&quot;Unknown signature algorithm &quot; + signingAlgorithm, nameof(signingAlgorithm)); // Special treatment because of issue https://github.com/bcgit/bc-csharp/issues/250 switch (signatureAlgorithmName.ToUpperInvariant()) { case &quot;SHA256WITHECDSA&quot;: this.signatureAlgorithm = new AlgorithmIdentifier(X9ObjectIdentifiers.ECDsaWithSha256); break; case &quot;SHA512WITHECDSA&quot;: this.signatureAlgorithm = new AlgorithmIdentifier(X9ObjectIdentifiers.ECDsaWithSha512); break; default: this.signatureAlgorithm = new DefaultSignatureAlgorithmIdentifierFinder().Find(signatureAlgorithmName); break; } } public object AlgorithmDetails =&gt; signatureAlgorithm; public IStreamCalculator&lt;IBlockResult&gt; CreateCalculator() { return new AwsKmsStreamCalculator(keyId, signingAlgorithm); } static Dictionary&lt;string, string&gt; signingAlgorithmNameBySpec = new Dictionary&lt;string, string&gt;() { { &quot;ECDSA_SHA_256&quot;, &quot;SHA256withECDSA&quot; }, { &quot;ECDSA_SHA_384&quot;, &quot;SHA384withECDSA&quot; }, { &quot;ECDSA_SHA_512&quot;, &quot;SHA512withECDSA&quot; }, { &quot;RSASSA_PKCS1_V1_5_SHA_256&quot;, &quot;SHA256withRSA&quot; }, { &quot;RSASSA_PKCS1_V1_5_SHA_384&quot;, &quot;SHA384withRSA&quot; }, { &quot;RSASSA_PKCS1_V1_5_SHA_512&quot;, &quot;SHA512withRSA&quot; }, { &quot;RSASSA_PSS_SHA_256&quot;, &quot;SHA256withRSAandMGF1&quot;}, { &quot;RSASSA_PSS_SHA_384&quot;, &quot;SHA384withRSAandMGF1&quot;}, { &quot;RSASSA_PSS_SHA_512&quot;, &quot;SHA512withRSAandMGF1&quot;} }; } class AwsKmsStreamCalculator : IStreamCalculator&lt;IBlockResult&gt; { private string keyId; private string signingAlgorithm; private MemoryStream stream = new MemoryStream(); public AwsKmsStreamCalculator(string keyId, string signingAlgorithm) { this.keyId = keyId; this.signingAlgorithm = signingAlgorithm; } public Stream Stream =&gt; stream; public IBlockResult GetResult() { try { using (var kmsClient = new AmazonKeyManagementServiceClient()) { SignRequest signRequest = new SignRequest() { SigningAlgorithm = signingAlgorithm, KeyId = keyId, MessageType = MessageType.RAW, Message = new MemoryStream(stream.ToArray()) }; SignResponse signResponse = kmsClient.SignAsync(signRequest).Result; return new SimpleBlockResult(signResponse.Signature.ToArray()); } } finally { stream = new MemoryStream(); } } }&lt;/script&gt;&lt;/div&gt; &lt;/div&gt; </div> </div> </section> <section class="brick jdoodle__brick"> <div class="brick__inner"> <div class="paragraph paragraph--jdoodle paragraph--jdoodle--default"> <h2></h2> <div class="wysiwyg-content clearfix text-formatted field field--name-field-text-area field--type-text-long field__items"> <p id="UsingiText7andAWSKMStodigitallysignaPDFdocument-PuttingItIntoAction:Redux"><a href="https://kb.itextpdf.com/itext/using-itext-7-and-aws-kms-to-digitally-sign-a-pd-4"><em>A</em></a><a href="https://kb.itextpdf.com/itext/using-itext-7-and-aws-kms-to-digitally-sign-a-pd-4"><em>wsKmsSignatureContainer</em></a></p> <h3>Putting It Into Action: Redux</h3> <p>Assuming you have an AWS KMS signing RSA_2048 key pair which has the alias <code>SigningExamples-RSA_2048</code> you can use the code above like this to sign a PDF using RSASSA-PSS:</p> </div> &lt;div data-pym-src=&quot;https://itext.jdoodle.com/embed/itext&quot; data-java-libs=&quot;com.itextpdf:itext7-core:latest.release,org.slf4j:slf4j-api:1.7.28,org.slf4j:slf4j-simple:1.7.28,com.itextpdf:pdfocr-tesseract4:latest.release&quot; data-csharp-libs=&quot;itext7:latest.release&quot; data-show-upload=&quot;false&quot; data-hide-execute=&quot;true&quot; data-max-uploads=&quot;3&quot; data-max-file-size=&quot;5000000&quot;&gt; &lt;div data-script-type=&quot;java&quot;&gt;&lt;script&gt;BouncyCastleProvider provider = new BouncyCastleProvider(); Security.addProvider(provider); String keyId = &quot;alias/SigningExamples-RSA_2048&quot;; X509Certificate certificate = CertificateUtils.generateSelfSignedCertificate(keyId, &quot;CN=AWS KMS PDF Signing Test,OU=mkl tests,O=mkl&quot;); AwsKmsSignatureContainer signatureContainer = new AwsKmsSignatureContainer(certificate, keyId, TestSignSimple::selectRsaSsaPss); try ( PdfReader pdfReader = new PdfReader(PDF_TO_SIGN); OutputStream result = new FileOutputStream(SIGNED_PDF)) { PdfSigner pdfSigner = new PdfSigner(pdfReader, result, new StampingProperties().useAppendMode()); pdfSigner.signExternalContainer(signatureContainer, 8192); }&lt;/script&gt;&lt;/div&gt; &lt;div data-script-type=&quot;csharp&quot;&gt;&lt;script&gt;string keyId = &quot;alias/SigningExamples-RSA_2048&quot;; Func&lt;System.Collections.Generic.List&lt;string&gt;, string&gt; selector = list =&gt; list.Find(name =&gt; name.StartsWith(&quot;RSASSA_PSS&quot;)); System.Security.Cryptography.X509Certificates.X509Certificate2 certificate2 = CertificateUtils.generateSelfSignedCertificate( keyId, &quot;CN=AWS KMS PDF Signing Test RSAwithMGF1,OU=mkl tests,O=mkl&quot;, selector ); X509Certificate certificate = new X509Certificate(X509CertificateStructure.GetInstance(certificate2.RawData)); AwsKmsSignatureContainer signature = new AwsKmsSignatureContainer(certificate, keyId, selector); using (PdfReader pdfReader = new PdfReader(PDF_TO_SIGN)) using (FileStream result = File.Create(SIGNED_PDF)) { PdfSigner pdfSigner = new PdfSigner(pdfReader, result, new StampingProperties().UseAppendMode()); pdfSigner.SignExternalContainer(signature, 8192); }&lt;/script&gt;&lt;/div&gt; &lt;/div&gt; </div> </div> </section> <section class="brick jdoodle__brick"> <div class="brick__inner"> <div class="paragraph paragraph--jdoodle paragraph--jdoodle--default"> <h2></h2> <div class="wysiwyg-content clearfix text-formatted field field--name-field-text-area field--type-text-long field__items"> <p><em><a href="https://kb.itextpdf.com/itext/using-itext-7-and-aws-kms-to-digitally-sign-a-pd-3">TestSignSimple</a> test <code>testSignSimpleRsaSsaPssExternal</code></em></p> <p>with this selector function for Java:</p> <pre> <code class="language-java"><div class="geshifilter"><table class="java geshifilter-java"><tbody><tr class="li1"><td class="ln"><pre class="de1">1 2 3 4 5 6 </pre></td><td class="de1"><pre class="de1"><span class="kw1">static</span> SigningAlgorithmSpec selectRsaSsaPss <span class="br0">(</span>List<span class="sy0">&lt;</span>SigningAlgorithmSpec<span class="sy0">&gt;</span> specs<span class="br0">)</span> <span class="br0">{</span>     <span class="kw1">if</span> <span class="br0">(</span>specs <span class="sy0">!=</span> <span class="kw2">null</span><span class="br0">)</span>         <span class="kw1">return</span> specs.<span class="me1">stream</span><span class="br0">(</span><span class="br0">)</span>.<span class="me1">filter</span><span class="br0">(</span>spec <span class="sy0">-&gt;</span> spec.<span class="me1">toString</span><span class="br0">(</span><span class="br0">)</span>.<span class="me1">startsWith</span><span class="br0">(</span><span class="st0">"RSASSA_PSS"</span><span class="br0">)</span><span class="br0">)</span>.<span class="me1">findFirst</span><span class="br0">(</span><span class="br0">)</span>.<span class="me1">orElse</span><span class="br0">(</span><span class="kw2">null</span><span class="br0">)</span><span class="sy0">;</span>     <span class="kw1">else</span>         <span class="kw1">return</span> <span class="kw2">null</span><span class="sy0">;</span> <span class="br0">}</span></pre></td></tr></tbody></table></div></code></pre> <p> </p> </div> </div> </div> </section> <section class="brick jdoodle__brick"> <div class="brick__inner"> <div class="paragraph paragraph--jdoodle paragraph--jdoodle--default"> <h2></h2> <div class="wysiwyg-content clearfix text-formatted field field--name-field-text-area field--type-text-long field__items"> <p>For .NET, since the C# AWS KMS API works with String representations of the algorithm the corresponding expression on the C# side is:</p> <pre> <code class="language-java"><div class="geshifilter"><table class="java geshifilter-java"><tbody><tr class="li1"><td class="ln"><pre class="de1">1 </pre></td><td class="de1"><pre class="de1">Func<span class="sy0">&lt;</span><span class="kw3">System</span>.<span class="kw3">Collections</span>.<span class="me1">Generic</span>.<span class="me1">List</span><span class="sy0">&lt;</span>string<span class="sy0">&gt;</span>, string<span class="sy0">&gt;</span> selector <span class="sy0">=</span> list <span class="sy0">=&gt;</span> list.<span class="me1">Find</span><span class="br0">(</span>name <span class="sy0">=&gt;</span> name.<span class="me1">StartsWith</span><span class="br0">(</span><span class="st0">"RSASSA_PSS"</span><span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span></pre></td></tr></tbody></table></div></code></pre> <p> </p> </div> </div> </div> </section> <div class="paragraph paragraph--text-area paragraph--text-area--default"> <h2></h2> <div class="wysiwyg-content clearfix text-formatted field field--name-field-text-area field--type-text-long field__items"> <h3 id="UsingiText7andAWSKMStodigitallysignaPDFdocument-FinalThoughtsandMass-SigningConsiderations">Final Thoughts and Mass-Signing Considerations</h3> <p>If you plan to do mass-signing using AWS KMS, you should be aware of the request quotas established by AWS KMS for some of its operations:</p> <table align="left" border="1" cellpadding="2" cellspacing="2" class="custom"><thead><tr><td> <p><strong>Quota Name</strong></p> </td> <td> <p><strong>Default value (per second)</strong></p> </td> </tr></thead><tbody><tr><td> <p>Cryptographic operations (RSA) request rate</p> </td> <td> <p>500 (shared) for RSA CMKs</p> </td> </tr><tr><td> <p>Cryptographic operations (ECC) request rate</p> </td> <td> <p>300 (shared) for elliptic curve (ECC) CMKs</p> </td> </tr><tr><td> <p>GetPublicKey request rate</p> </td> <td> <p>5</p> </td> </tr></tbody></table><p><br /><em>(excerpt from "AWS Key Management Service Developer Guide" / "Quotas" / "Request Quotas" / </em><a href="https://docs.aws.amazon.com/kms/latest/developerguide/requests-per-second.html#rps-table" rel="nofollow"><em>"Request quotas for each AWS KMS API operation"</em></a><em> viewed 1/28/2021</em><em>)</em></p> <p>The <em>RSA</em> and <em>ECC cryptographic operations request rates</em> are probably not a problem. Or more to the point, if they are a problem, AWS KMS is most likely not the right signing product for your needs. You should instead look for actual HSMs, be they physical or as-a-service, e.g., <a href="https://aws.amazon.com/cloudhsm/" rel="nofollow">AWS CloudHSM</a>.</p> <p>The <em>GetPublicKey request rate</em> on the other hand may well be a problem: Both the <code>AwsKmsSignature</code> and <code>AwsKmsSignatureContainer</code> constructors respectively call that method. Naive mass-signing code based on them, would therefore be limited to 5 signatures per second.</p> <p>Depending on your use case there are different strategies to tackle this problem.</p> <p><strong>If very few instances of your signing code are running concurrently and they are using only a very few different keys</strong>, you can simply re-use your <code>AwsKmsSignature</code> and <code>AwsKmsSignatureContainer</code> objects, either creating them at start-up or on-demand, and then caching them.</p> <p><strong>Otherwise</strong>, you should refactor the use of the <code>GetPublicKey</code> method outside of the <code>AwsKmsSignature</code> and <code>AwsKmsSignatureContainer</code> constructors. It is used inside there only to determine which AWS KMS signing algorithm identifier to use when signing with the key in question. Obviously, you can instead store that identifier together with the key identifier, making that <code>GetPublicKey</code> call unnecessary.</p> </div> </div> <div class="paragraph paragraph--text-area paragraph--text-area--default"> <h2> <div id="Conclusion" class="anchor field field--name-field-section-heading field--type-string field__items"> Conclusion </div> </h2> <div class="wysiwyg-content clearfix text-formatted field field--name-field-text-area field--type-text-long field__items"> <p>We hope you have found this article and its code examples useful if you’ve run into issues when using the AWS KMS or equivalent services. Once again, we’d like to thank Michael Klink for taking the time to port his Java examples from the initial Stack Overflow question to .NET, and indeed for his many contributions to the iText community.</p> <p> </p> </div> </div> </div> <div class="field field--name-field-related-products field--type-entity-reference field__items"> <div class="field__label">Related products</div> <a href="/products/itext-core" hreflang="en">iText Core</a> <a href="/products/itext-suite" hreflang="en">iText Suite</a> <a href="/products/itext-community" hreflang="en">iText Community</a> </div> <div class="field field--name-field-main-image field--type-entity-reference field__items"> <div class="field__label">Main image</div> <a href="/resources/media/images/using-itext-and-aws-kms-teaser" hreflang="en">Using iText and AWS KMS teaser</a> </div> <div class="field field--name-field-promoted-to-home-page-text field--type-string field__items"> <div class="field__label">Promoted to home page text</div> Newest blog: Digitally sign a PDF using AWS KMS and iText </div> Fri, 29 Jan 2021 13:10:15 +0000 ian.morris 14831 at https://itextpdf.com https://itextpdf.com/blog/technical-notes/using-itext-and-aws-kms-digitally-sign-pdf-document#comments