ebourg / jsign

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

A lot of reading of Google Cloud KMS keyrings and keys during signing #94

Closed rvadim closed 3 years ago

rvadim commented 3 years ago

STR:

  1. Generate asymmetric key and CA
    gcloud kms keyrings create test --location <region>```
    gcloud kms keys create my-key --keyring test --location <region> --purpose "asymmetric-signing" --default-algorithm "rsa-sign-pkcs1-2048-sha256"
    openssl genrsa -out ca.key 2048
  2. Create CSR and Certificate
    git clone https://github.com/mattes/google-cloud-kms-csr
    cd google-cloud-kms-csr
    go build -o csr
    ./csr -key $(gcloud kms keys versions list  --key my-key --keyring test --location=<region>) -out my.csr --common-name MyOrg
    openssl -req -in my.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out my.crt
  3. Run jsign
    for i in {1..1000}; do 
    jsign --storetype GOOGLECLOUD
    --storepass "$(gcloud auth print-access-token)"
    --keystore "projects/<project>/locations/<location>/keyRings/test"
    --alias "my-key"
    --certfile my.crt
    my.exe
    done

Expected result: Binary signed

Actual result:

java.io.IOException: 429 - RESOURCE_EXHAUSTED: Quota exceeded for quota metric 'Read requests' and limit 'Read requests per minute' of service 'cloudkms.googleapis.com' for consumer 'project_number:<project-id>

screenshot-console cloud google com-2021 07 05-15_13_45

So, each run of jsign makes 2 reading and 1 crypto operation. But default quotas are:

Also jsign require a lot of permissions for one signing For key:

cloudkms.cryptoKeyVersions.list
cloudkms.cryptoKeyVersions.useToSign
cloudkms.locations.get
cloudkms.locations.list
resourcemanager.projects.get

For keyring:

cloudkms.cryptoKeys.list

Actually only one permission required cloudkms.cryptoKeyVersions.useToSign and only 2 parameters to run key.id (example: projects/<project id>/locations/<location>/keyRings/<keyring>/cryptoKeys/<key name>/cryptoKeyVersions/<version> and algorithm (example: RSA).

I have 2 suggestions:

I will try to implement second.

ebourg commented 3 years ago

Interesting, I wasn't aware of the request limit. I can add a cache in GoogleCloudSigningService, but that won't work for repeated calls from the command line. Out of curiosity, how many files are you signing usually?

ebourg commented 3 years ago

I've made a couple of changes that should improve the situation:

So if you sign n files this results in two requests to fetch the private key + n signing requests (instead of 3*n requests previously).

rvadim commented 3 years ago

Out of curiosity, how many files are you signing usually?

More then 100k per month ( Near 1000 files per pipeline, few piplines per day.

I've made a couple of changes that should improve the situation:

Wow! Thank you for such fast response. It's good feature actually. I will think how to use it in our pipelines)

ebourg commented 3 years ago

More then 100k per month ( Near 1000 files per pipeline, few piplines per day.

Impressive, you could consider buying your own HSM at this point ;)

I've added another tweak, you can now append the key algorithm to the key alias (only if the version is specified), this saves one more request: --alias mykey/cryptoKeyVersions/2:ECDSA

There is still one extra request to fetch all the keys available that could be removed.

ebourg commented 3 years ago

The request to get all the keys has been removed, there is now only one request per file signed.

I'd still recommend signing multiple files with a single invocation of jsign from the command line, it should be slightly faster.

rvadim commented 3 years ago

Impressive, you could consider buying your own HSM at this point ;)

Yes) We are thinking about it, but for now we will use Cloud HSM for a reason.

I've added another tweak, you can now append the key algorithm to the key alias (only if the version is specified), this saves one more request: --alias mykey/cryptoKeyVersions/2:ECDSA

The request to get all the keys has been removed, there is now only one request per file signed. I'd still recommend signing multiple files with a single invocation of jsign from the command line, it should be slightly faster.

You have greatly accelerated our work! Thank you very much. We will do performance testing and I will share the results here.

rvadim commented 3 years ago

screenshot-console cloud google com-2021 07 06-12_09_13 screenshot-console cloud google com-2021 07 06-12_10_07

2 peaks:

We don't have reads at all! Now we are limited only by the number of cryptographic operations per region. I will also try to use bulk signing.

Thank you again :+1:

ebourg commented 3 years ago

Nice! Thank you for torturing testing Jsign thoroughly.