ebourg / jsign

Java implementation of Microsoft Authenticode for signing Windows executables, installers & scripts
https://ebourg.github.io/jsign
Apache License 2.0
250 stars 107 forks source link

Added support for using Google Cloud KMS via Hashicorp Vault #144

Closed MariaMerkel closed 1 year ago

MariaMerkel commented 1 year ago

This PR enables support for using Google Cloud KMS via Hashicorp Vault via the Vault Google Cloud KMS secrets engine.

Using this instead of the direct Google Cloud KMS integration is useful in some applications where the additional authentication, policy or auditing features of Vault are required. I have added support to JSign CLI to use the service with a Vault token, but in practice the more likely application is for automated usage, where the application obtains a temporary token via another authentication method (such as client certificate authentication) and then passes it to JSign.

MariaMerkel commented 1 year ago

Test failures seem to be related to a change in the eSigner API, not this commit

ebourg commented 1 year ago

Thank you for the PR. I didn't know Hashicorp Vault could be used to access Google Cloud KMS, that would be a good addition to Jsign. Since Hashicorp Vault supports several secrets engines, do you think this could be generalized to support any engine and not just Google Cloud KMS? Or is the API too differents for each engine? If that's possible we could have a HASHICORPVAULT store type, and the actual engine/key would be specified by the keystore and alias parameters.

MariaMerkel commented 1 year ago

The only other relevant secrets engine would be the Transit one, where Vault stores the private key directly, however use in Jsign would be limited because publicly-trusted CAs are no longer allowed to issue code signing certificates to keys not stored and generated on an HSM.

The Transit engine uses a slightly different API format, so it will not work with the current code. It would be trivial to add support for this, but I am unsure if there would be any demand as this would be limited to code signing within a private PKI. Users would also need to specify the engine type as it only seems to be possible to retrieve a list of engines with their type if the token has certain administrative permissions, which would not be desirable for a standard application.

It's also worth noting that the Transit engine is primarily meant to encrypt data at-rest and while it does support signatures, it returns a Vault-specific string format that includes the key version for verification via the Vault API. While it is trivial to extract the signature itself from this, it doesn't seem like that is the intended use case and I'm unsure whether this is guaranteed to remain the case.

ebourg commented 1 year ago

The AWS and Azure secrets engines do not support private keys?

MariaMerkel commented 1 year ago

The AWS and Azure secrets engines only create credentials for those platforms via IAM (this is also the case for the Google Cloud secrets engine, which is separate from the Google Cloud KMS one). There doesn't seem to be an official Azure Key Vault or AWS KMS engine, I'm not sure why that is.

Of course, the AWS or Azure engine could be used to create credentials which are then used to execute a signature, but it seems more sensible for that to be done via the integrating application using Vault to generate credentials that it then passes to Jsign for the existing integrations.

ebourg commented 1 year ago

Ok, and for Google Cloud KMS the url always ends with /v1/gcpkms, or is it endpoint specific?

MariaMerkel commented 1 year ago

The part after the API version is the path selected when enabling the secrets engine. It's gcpkms by default, but it can be changed and in setups where the engine is enabled multiple times (for example for multiple Google Cloud accounts), each instance will have a different path.

ebourg commented 1 year ago

I got a look at the Vault API documentation, and it seems that the GET gcpkms/keys/:key request returns the current version of the key:

{
  "data": {
    "id": "projects/my-project/locations/my-location/keyRings/my-keyring/cryptoKeys/my-crypto-key",
    "labels": {
      "foo": "bar"
    },
    "next_rotation_time_seconds": 1536613424,
    "primary_version": "3",
    "purpose": "encrypt_decrypt",
    "rotation_schedule_seconds": 259200,
    "state": "enabled"
  }
}

Could this version be used by default if the user hasn't specified one explicitly instead of throwing an UnrecoverableKeyException?

MariaMerkel commented 1 year ago

Looks like I missed that. I will take a look tomorrow and push an update if feasible.

MariaMerkel commented 1 year ago

I have taken another look now. The problem here is that Google Cloud does not have a flag for a primary key version of asymmetric keys, only of symmetric ones.

The Google Cloud signing service gets around this by just using the latest key version if none is specified, but Vault does not expose a list of key versions, only the primary one (and this field does not exist in our use case as we are using asymmetric keys).

ebourg commented 1 year ago

Ok, thank you for checking.

I'm hesitating on the name of the storetype, I'm leaning toward naming it HASHICORPVAULT, with the assumption that it'll be possible to sign with other secrets engine in the future, or just make it a variant of the GOOGLECLOUD type as you did (but maybe GOOGLECLOUD+suffix instead of prefix+GOOGLECLOUD).

MariaMerkel commented 1 year ago

I will leave the naming up to you, however I think simply naming it HASHICORPVAULT could be confusing because it is not compatible with any other secrets engine that currently exists (like the Transit one, which, while not useful for jsign, does support signatures). Of course, there is also no guarantee that other KMS secrets engines will exist in the future, or that they will use the same API structure.

ebourg commented 1 year ago

The PR has been merged with some modifications to adapt to the latest SignerHelper changes. I don't have a Vault instance to test, so I hope I haven't broken something in the process.