0

Has anyone implemented Cryptographic Message Syntax using Google's HSM and KMS service?

It is difficult to tell if this functionality is built into the Tinklibrary or not.

There is no Google Engine for OpenSSL or BoringSSL (would love to be corrected if not the case) and as the engine needs to be written in clang I imagine it is rather difficult to include the tink.so library?

If anyone has any information on performing these types of operations on Google's KMS service It would be greatly appreciated.

jmwilkosz
  • 11
  • 1
  • 5

2 Answers2

3

At present, this would require a fair amount of custom code, though it's technically possible. This functionality is not built into Tink, nor is there a Cloud KMS engine available for OpenSSL or BoringSSL.

Probably the easiest path would be to use the Cloud KMS Java client with the CMS support in Bouncycastle, though I'm not sure if Java is an option for your use case. I could write up a sample of how to do that if you thought it'd be useful.

bdhess
  • 628
  • 3
  • 6
0

Thanks for the direction @bdhess!

I have provided some code snippets for those interested in attempting similar functionality. The main class to note is ContentSignerFactory.java this is where the API magic happens.

There is a very helpful pdf for bouncycastle: https://www.bouncycastle.org/fips-java/BCFipsIn100.pdf

NOTE: I am not a java programmer

Cms.java

public class Cms {
    public static byte[] signDataKms(
            String credentialsKeyPath,
            String keyName,
            X509Certificate signingCert,
            byte data[]) throws Exception {

        List<X509Certificate> certList = new ArrayList<>();
        certList.add(signingCert);
        Store certs = new JcaCertStore(certList);

        CMSTypedData cmsData = new CMSProcessableByteArray(data);

        DigestCalculatorProvider digProvider =
                new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
        JcaSignerInfoGeneratorBuilder signerInfoGeneratorBuilder =
                new JcaSignerInfoGeneratorBuilder(digProvider);

        //SignedHash is a base64-encoded PKCS1 block.
        ContentSigner sha1Signer = ContentSignerFactory.getContentSigner((stream) -> {
            try {
                return Kms.signAsymmetric(credentialsKeyPath, keyName, stream.toByteArray());
            } catch (IOException e) {
                e.printStackTrace();
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
            return new byte[0];
        }, "SHA256WITHRSA");

        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();

        gen.addSignerInfoGenerator(signerInfoGeneratorBuilder.build(sha1Signer, signingCert));
        gen.addCertificates(certs);
        CMSSignedData cms = gen.generate(cmsData, true);

        return cms.toASN1Structure().getEncoded(ASN1Encoding.DER);
    }
}

ContentSignerFactory.java

public class ContentSignerFactory {

    public static ContentSigner getContentSigner(Function<ByteArrayOutputStream, byte[]> lambda, String algorithm) {
        return new ContentSigner() {
            //This is to ensure that signature is created using the right data.
            ByteArrayOutputStream stream = new ByteArrayOutputStream();

            @Override
            public byte[] getSignature() {
                //Calling HSM here instead, the stream is the AttributeMap
                byte[] data = lambda.apply(stream);
                return data;
            }

            //Perhaps called by BouncyCastle library to provide the content
            @Override
            public OutputStream getOutputStream() {
                return stream;
            }

            @Override
            public AlgorithmIdentifier getAlgorithmIdentifier() {
                return new DefaultSignatureAlgorithmIdentifierFinder().find(algorithm);
            }
        };
    }
}

Kms.java

public class Kms {
    public static byte[] signAsymmetric(String credentialsKeyPath, String keyName, byte[] message)
            throws IOException, NoSuchAlgorithmException {
        // Create the Cloud KMS client.
        try (KeyManagementServiceClient client
                     = KeyManagementServiceClient.create(getKeyManagementServiceSettings(credentialsKeyPath))) {

            // Note: some key algorithms will require a different hash function
            // For example, EC_SIGN_P384_SHA384 requires SHA-384
            byte[] messageHash = MessageDigest.getInstance("SHA-256").digest(message);

            AsymmetricSignRequest request = AsymmetricSignRequest.newBuilder()
                    .setName(keyName)
                    .setDigest(Digest.newBuilder().setSha256(ByteString.copyFrom(messageHash)))
                    .build();

            AsymmetricSignResponse response = client.asymmetricSign(request);
            return response.getSignature().toByteArray();
        }
    }
}
jmwilkosz
  • 11
  • 1
  • 5