Skip to main content
Skip table of contents

Using iText and AWS KMS to digitally sign a PDF document: Part 3

This example was written for the article "Using iText and AWS KMS to digitally sign a PDF document" and shows an implementation of the iText IExternalSignature interface for the purposes of signing a PDF using an AWS KMS Key Pair.

In the constructor we select a signing algorithm available for the key in question. However, as explained in the article you may want to enforce use of a specific hashing algorithm instead of simply taking the first algorithm.

In this example getHashAlgorithm and getEncryptionAlgorithm return the name of the respective part of the signature algorithm and sign simply creates a signature.

AwsKmsSignature
JAVA
package com.itextpdf.signingexamples.aws.kms;

import java.security.GeneralSecurityException;
import java.util.List;
import java.util.function.Function;

import com.itextpdf.signatures.IExternalSignature;

import com.itextpdf.signatures.ISignatureMechanismParams;
import com.itextpdf.signatures.RSASSAPSSMechanismParams;

import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.services.kms.KmsClient;
import software.amazon.awssdk.services.kms.model.GetPublicKeyRequest;
import software.amazon.awssdk.services.kms.model.GetPublicKeyResponse;
import software.amazon.awssdk.services.kms.model.MessageType;
import software.amazon.awssdk.services.kms.model.SignRequest;
import software.amazon.awssdk.services.kms.model.SignResponse;
import software.amazon.awssdk.services.kms.model.SigningAlgorithmSpec;

/**
 * @author mkl
 */
public class AwsKmsSignature implements IExternalSignature {
    public AwsKmsSignature(String keyId) {
        this(keyId, a -> a != null && a.size() > 0 ? a.get(0) : null);
    }

    public AwsKmsSignature(String keyId, Function<List<SigningAlgorithmSpec>, SigningAlgorithmSpec> 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("Unknown signing algorithm: %s", signingAlgorithmSpec));
            }
        }
    }

    @Override
    public String getDigestAlgorithmName() {
        switch(signingAlgorithmSpec) {
        case ECDSA_SHA_256:
        case RSASSA_PKCS1_V1_5_SHA_256:
        case RSASSA_PSS_SHA_256:
            return "SHA-256";
        case ECDSA_SHA_384:
        case RSASSA_PKCS1_V1_5_SHA_384:
        case RSASSA_PSS_SHA_384:
            return "SHA-384";
        case ECDSA_SHA_512:
        case RSASSA_PKCS1_V1_5_SHA_512:
        case RSASSA_PSS_SHA_512:
            return "SHA-512";
        default:
            return null;
        }
    }

    @Override
    public String getSignatureAlgorithmName() {
        switch(signingAlgorithmSpec) {
        case ECDSA_SHA_256:
        case ECDSA_SHA_384:
        case ECDSA_SHA_512:
            return "ECDSA";
        case RSASSA_PKCS1_V1_5_SHA_256:
        case RSASSA_PKCS1_V1_5_SHA_384:
        case RSASSA_PKCS1_V1_5_SHA_512:
            return "RSA";
        case RSASSA_PSS_SHA_256:
        case RSASSA_PSS_SHA_384:
        case RSASSA_PSS_SHA_512:
            return "RSASSA-PSS";
        default:
            return null;
        }
    }

    @Override
    public ISignatureMechanismParams getSignatureMechanismParameters() {
        switch (signingAlgorithmSpec)
        {
            case RSASSA_PSS_SHA_256:
                return RSASSAPSSMechanismParams.createForDigestAlgorithm("SHA-256");
            case RSASSA_PSS_SHA_384:
                return RSASSAPSSMechanismParams.createForDigestAlgorithm("SHA-384");
            case RSASSA_PSS_SHA_512:
                return RSASSAPSSMechanismParams.createForDigestAlgorithm("SHA-512");
            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;
}


AwsKmsSignature
C#
using Amazon.KeyManagementService;
using Amazon.KeyManagementService.Model;
using iText.Signatures;
using System;
using System.Collections.Generic;
using System.IO;

namespace iText.SigningExamples.AwsKms
{
    public class AwsKmsSignature : IExternalSignature
    {
        public AwsKmsSignature(string keyId, Func<List<string>, string> selector)
        {
            this.keyId = keyId;
            using (var kmsClient = new AmazonKeyManagementServiceClient())
            {
                GetPublicKeyRequest getPublicKeyRequest = new GetPublicKeyRequest() { KeyId = keyId };
                GetPublicKeyResponse getPublicKeyResponse = kmsClient.GetPublicKeyAsync(getPublicKeyRequest).Result;
                List<string> signingAlgorithms = getPublicKeyResponse.SigningAlgorithms;
                signingAlgorithm = selector.Invoke(signingAlgorithms);
                switch(signingAlgorithm)
                {
                    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 ArgumentException(String.Format("Unknown signing algorithm: {0}", signingAlgorithm));
                }
            }
        }

        public string GetSignatureAlgorithmName()
        {
            switch (signingAlgorithm)
            {
                case "ECDSA_SHA_256":
                case "ECDSA_SHA_384":
                case "ECDSA_SHA_512":
                    return "ECDSA";
                case "RSASSA_PKCS1_V1_5_SHA_256":
                case "RSASSA_PKCS1_V1_5_SHA_384":
                case "RSASSA_PKCS1_V1_5_SHA_512":
                    return "RSA";
                case "RSASSA_PSS_SHA_256":
                case "RSASSA_PSS_SHA_384":
                case "RSASSA_PSS_SHA_512":
                    return "RSASSA-PSS";
                default:
                    return null;
            }
        }

        public string GetDigestAlgorithmName()
        {
            switch (signingAlgorithm)
            {
                case "ECDSA_SHA_256":
                case "RSASSA_PKCS1_V1_5_SHA_256":
                case "RSASSA_PSS_SHA_256":
                    return "SHA-256";
                case "ECDSA_SHA_384":
                case "RSASSA_PKCS1_V1_5_SHA_384":
                case "RSASSA_PSS_SHA_384":
                    return "SHA-384";
                case "ECDSA_SHA_512":
                case "RSASSA_PKCS1_V1_5_SHA_512":
                case "RSASSA_PSS_SHA_512":
                    return "SHA-512";
                default:
                    return null;
            }
        }

        public ISignatureMechanismParams GetSignatureMechanismParameters()
        {
            switch (signingAlgorithm)
            {
                case "RSASSA_PSS_SHA_256":
                    return RSASSAPSSMechanismParams.CreateForDigestAlgorithm("SHA-256");
                case "RSASSA_PSS_SHA_384":
                    return RSASSAPSSMechanismParams.CreateForDigestAlgorithm("SHA-384");
                case "RSASSA_PSS_SHA_512":
                    return RSASSAPSSMechanismParams.CreateForDigestAlgorithm("SHA-512");
                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;
            }
        }

        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;
    }
}


Note: The article assumes that you have stored your credentials in the default section of your ~/.aws/credentials file and your region in the default section of your ~/.aws/config file. Otherwise, you'll have to adapt the KmsClient instantiation or initialization in the code examples written for this article.

For the other examples relating to this article, please see the following links:


JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.