Search code examples
c#signhashicorp-vaultvaultsharp

How do I sign with HashiCorp Vault


i don't know if this question is very easy and I just didn't figure it out how to sign with HashiCorp-Vault´s Api VaultSharp, but I am despairing.

The entire Documentation with examples can be found here: https://github.com/rajanadar/VaultSharp Encryption and Decryption works fine. Only Signing is a problem.
Code for Encryption:

public byte[] EncryptData(byte[] data, string keyName)
{
   SecretsEngine transitSecretsEngine = new SecretsEngine
   {
      Type = SecretsEngineType.Transit,
      Path = path
   };

   Client.V1.System.MountSecretBackendAsync(transitSecretsEngine).Wait();
   Client.V1.Secrets.Transit.CreateEncryptionKeyAsync(keyName, new CreateKeyRequestOptions()
   {
      Exportable = true
   }, path).Wait();
     
   EncryptRequestOptions encryptOptions = new EncryptRequestOptions
   {
      Base64EncodedPlainText = Convert.ToBase64String(data),
      ConvergentEncryption = true,
   };

   Secret<EncryptionResponse> encryptionResponse = Client.V1.Secrets.Transit.EncryptAsync(keyName, 
   encryptOptions, path).Result;
   
   string cipherText = encryptionResponse.Data.CipherText;
   return Encoding.Unicode.GetBytes(cipherText);
}

Code for Decryption:

public byte[] DecryptData(string ciphertext, string keyName)
{
   DecryptRequestOptions decryptOptions = new DecryptRequestOptions
   {
      CipherText = ciphertext,
   };
   Secret<DecryptionResponse> decryptionResponse = Client.V1.Secrets.Transit.DecryptAsync(keyName, 
   decryptOptions, path).Result;
   
   return Convert.FromBase64String(decryptionResponse.Data.Base64EncodedPlainText);
}

Here is my Code Trial for signing:

public byte[] Sign(byte[] plaintextBytes, string keyName)
{
   byte[] hash = ComputeHash(plaintextBytes,SHA256.Create());
   GCKMS.SignatureOptions options = new GCKMS.SignatureOptions()
   {
      Digest = Convert.ToBase64String(hash),
   };
   Secret<GCKMS.SignatureResponse> result = Client.V1.Secrets.GoogleCloudKMS.SignAsync(keyName, 
   options).Result;
   return Encoding.Unicode.GetBytes(result.Data.Signature);
}

The Error is:

VaultSharp.Core.VaultApiException: {"errors":["no handler for route 'gcpkms/sign/Manuel'"]}


Last but not least my Code for validating the signature:

public bool ValidateSignature(byte[] plaintextByte, byte[] signature, string keyName)
{
   GCKMS.VerificationOptions option = new GCKMS.VerificationOptions
   {
      Digest = Encoding.Unicode.GetString(ComputeHash(plaintextByte)),
      Signature = Encoding.Unicode.GetString(signature)
   };
   Secret<GCKMS.VerificationResponse> result = 
   Client.V1.Secrets.GoogleCloudKMS.VerifyAsync(keyName, option).Result;
   return result.Data.Valid;
}

I am not sure but this could be because I don't use a SecretsEngine with a Path. I could not find any SecretsEngine for GoogleCloudKms.

Useful information:
I generate the Path with Guid.NewGuid().ToString();.
ComputeHash is a self written Function that computes the Hash with a give Algorithm. The default algorithm is SHA256.
GCMS is a short version of the Namespace VaultSharp.V1.SecretsEngines.GoogleCloudKMS

Any ideas and suggestions are very welcome.
Thanks in advance!


Solution

  • Although Vault offers convenient signature with Transit, the C# wrapper you are using does not support it.

    Google KMS does offer signature, but its interface is more complex: you have to do the hash yourself and keep track of the key versions.

    What I suggest is that you play a trick on your API wrapper:

    You still have to base64 your data before sending it to Vault, to avoid binary encoding issues.

    So assuming that:

    • You want to sign the text StackOverflow
    • The transit back-end is mounted under transit
    • Your signature key is named my-key

    This should get you started:

    var value = new Dictionary<string, object> { "input", Convert.ToBase64String(Encoding.UTF8.GetBytes("StackOverflow")) } };
    var writtenValue = await vaultClient.V1.Secrets.KeyValue.V1.WriteSecretAsync("sign/my-key", value, "transit");