Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to sign with additional certificates #119

Open
MrAlex94 opened this issue Apr 19, 2022 · 22 comments
Open

Add ability to sign with additional certificates #119

MrAlex94 opened this issue Apr 19, 2022 · 22 comments

Comments

@MrAlex94
Copy link

Various other signing tools offer the ability to sign with additional certificates:

SignTool:

Sign command option Description
/ac file Adds an additional certificate from file to the signature block.

AzureSignTool:

Sign command option Description
--additional-certificates [short: -ac, required: no] A list of paths to additional certificates to aide in building a full chain for the signing certificate. Azure SignTool will build a chain, either as deep as it can or to a trusted root. This will also use the Windows certificate store, in addition to any certificates specified with this option. Specifying this option does not guarantee the inclusion of the certificate, only if it is part of the chain. To include multiple certificates, specify this option mulitple times, such as -ac file1.cer -ac file2.cer. The files specified must be public certificates only. They cannot be PFX, PKCS12 or PFX files.

It would be great if jsign also had this ability.

@ebourg
Copy link
Owner

ebourg commented Apr 21, 2022

Did you try the certfile parameter? This allows you to define the certificates added to the signature.

@laurentgo
Copy link

One issue (or difference) I noticed with some tools like jarsigner (used for signing Java archives, and which has a -certchain option too) is that jsign has check to confirm that the public key certificate from the keystore matches the first certificate of the chain.
IMHO this check may be too strong (better would be to check that the public key of the certificate matches the public key used for signing) and I'm running into a situation where I cannot use Google KMS pkcs11 library for signing with jsign because certificates are autogenerated (I'm aware of the existing GOOGLECLOUD provider but the lack of support for application default credentials is a blocker for my use-case)

I'm happy to propose a patch to at least add an option to skip the check

@ebourg
Copy link
Owner

ebourg commented Jun 2, 2022

@laurentgo thank you for the PRs, I'll give them a look. Could you elaborate on the credentials issue with Google KMS?

@ebourg
Copy link
Owner

ebourg commented Jun 2, 2022

@laurentgo I've checked the documentation about application default credentials, have you tried getting an access token with gcloud auth application-default print-access-token and use it for the storepass argument?

https://cloud.google.com/sdk/gcloud/reference/auth/application-default/print-access-token

@laurentgo
Copy link

I guess there's nothing wrong with gcloud command, it's just that our security policies recommend using ADC and not exposing password as arguments, so using GoogleKMS type with jsign is not an option for now, while using libkmsp11 (which supports ADC) as a PKCS#11 provider for jsign doesn't have this authentication issue (but requires an alternative certificate chain).

One nice thing I discovered is that Google has extensive documentation regarding how ADC is supposed to work from an implementation point of view: https://google.aip.dev/auth/4110
Now, this is probably hard/time consuming to implement vs simply using google libraries (but I can understand this is probably not something jsign want to do).

@ebourg
Copy link
Owner

ebourg commented Jun 8, 2022

Technically the access token is not a password, but if your security policies prohibit using it as a command line argument it's possible to read the token from an environment variable, and the actual value never appears as an argument:

GOOGLE_ACCESS_TOKEN=$(gcloud auth application-default print-access-token)
jsign --storetype GOOGLECLOUD --storepass env:GOOGLE_ACCESS_TOKEN ...

Would that work for you?

@laurentgo
Copy link

Possibly (would have to check with our team)? But we would definitively favor a solution relying on ADC mechanism for sure.

That said, I don't want to distract from the original ask which is the ability to add additional certificates and/or use a different certificate chain. If you could have a look at my proposal, it would be much appreciated. And I'd gladly open a separate issue regarding Google authentication.

@ebourg
Copy link
Owner

ebourg commented Jun 13, 2022

If I understand well, adding extra certificates is useful in a cross signing scenario, when the same key is used by several certificates with different trust paths, right?

@laurentgo
Copy link

correct

@MrAlex94
Copy link
Author

Did you try the certfile parameter? This allows you to define the certificates added to the signature.

Sorry I haven't managed to test this yet! I'll be back working on the part of our CI that uses jsign in a week or two and will test then :-)

@laurentgo
Copy link

@ebourg do you think you would have time to also provide feedback on my proposed changes (#128 and #129)?

@MrAlex94
Copy link
Author

MrAlex94 commented Jul 5, 2022

@ebourg - an update on using -certfile:

Currently we are pulling in an EV cert from Azure and using --storetype AZUREKEYVAULT. Using --certfile doesn't work, as we end up with:

jsign: The certificate chain in cert.p7b does not match the chain from the keystore

It is expected that the additional cert is not related at all.
For comparison, using signtool with an EV cert and the additional certificate in cer format works.

@ebourg
Copy link
Owner

ebourg commented Jul 5, 2022

@MrAlex94 Does that mean that when you renew your certificate, you reuse the same private key and update the certificate chain used by the build script, but you do not update the certificate stored in Azure Key Vault?

@MrAlex94
Copy link
Author

MrAlex94 commented Jul 8, 2022

@ebourg, no, everything gets updated and changed. The additional certificate (such as when using /ac with signtool.exe) is completely unrelated to the EV one.

@ebourg
Copy link
Owner

ebourg commented Jul 9, 2022

@mralex so you basically have two certificate chains, one EV, and the other non-EV, both sharing the same private key hosted by Azure Key Vault? I'd be curious to know the use case behind this.

@MrAlex94
Copy link
Author

MrAlex94 commented Jul 11, 2022

@mralex so you basically have two certificate chains, one EV, and the other non-EV, both sharing the same private key hosted by Azure Key Vault? I'd be curious to know the use case behind this.

Apologies, my explanation was a bit poor. Essentially, the other certificate is a dummy certificate (unrelated and not hosted at all on Azure), used for partner builds of a program we have. You can see rationale/usage here:

This method uses a hand-crafted dummy certificate that intentionally isn't good for signing anything (it's self-signed, and expired), but that includes a big empty space where you can drop in arbitrary data. Add in that certificate at signing time (using osslsigncode's -ac option), and then that space can be filled in later with the attribution code/other data. The real signing certificate is still present as well, and it gets accepted by Authenticode as usual.

Basically when a user comes from a marketing campaign, we inject the campaign data into the "empty" cert space so we can attribute it nicely.

@ebourg
Copy link
Owner

ebourg commented Jul 11, 2022

@MrAlex94 Thank you for the clarification. osslsigncode supports the -addUnauthenticatedBlob parameter to inject bytes into the signature that can be altered without invalidating it. That's a simpler alternative than fiddling with the additional certificates. This is a feature I'd like to implement in Jsign as well.

@MrAlex94
Copy link
Author

Oh, didn’t know that was an option in osslsigncode! I wonder if I can get the same results that way.

I’ll give it a test and let you know. Currently I just sign the exe twice, once with the valid cert and once with the dummy. The only difference when using the /ac flag with signtool is that the dummy cert doesn’t appear in the digital signature list on the file properties (I’m not sure why that’s the case?).

Either way, appreciate your vigilance responding to this issue!

Should this be closed, or changed to be the issue for adding the functionality of addUnauthenticatedBlob?

@ebourg
Copy link
Owner

ebourg commented Jul 12, 2022

The only difference when using the /ac flag with signtool is that the dummy cert doesn’t appear in the digital signature list on the file properties (I’m not sure why that’s the case?)

Because the dummy certificate isn't used for signing, it's simply appended to the certificate store embedded within the signature. This store is used by Windows to link the signing certificate with the CA certificate (this is necessary if some of the intermediate certificates are unknown to Windows). The dummy certificate doesn't participate in the certificate chain and is invisible in the file properties. The signature is simply larger.

The addUnauthenticatedBlob feature is slightly different, it adds an entry into the unauthenticated attributes table of the signature. That's more compact that injecting a whole certificate, but it's visible in the file properties

Should this be closed, or changed to be the issue for adding the functionality of addUnauthenticatedBlob?

No I'm still pondering if /ac is worth implementing, I'm just looking for a good use case. I'm tempted to extend the semantic of the certfile parameter and simply add all the certificates in this file even if they do not participate in the certificate chain.

@mikehearn
Copy link

/ac is useful for situations like this one:

https://knowledge.digicert.com/solution/authenticode-signature-verification-fails-with-new-timestamping-cross-root.html

DigiCert's TSA seems like the fastest and most reliable by far but they did some sort of screwup and the way to fix it is by adding extra certificates so a path through their temp fix-it cert can be found. I'm not sure why they haven't fixed this situation yet but it seems to still be operative.

@ebourg
Copy link
Owner

ebourg commented Aug 10, 2022

@mikehearn Thank you for the example!

@ebourg
Copy link
Owner

ebourg commented Dec 12, 2024

@MrAlex94 FYI Jsign now has a tagging feature equivalent to osslsigncode's addUnauthenticatedBlob:

https://ebourg.github.io/jsign/#tagging

A signed file can be modified to include additional data without invalidating the signature. This feature is useful for embedding user identification data, such as a licence key or a session token, within a signed installer when the file is downloaded. Upon installation, the installer extracts this data, enabling the application to automatically authenticate the user without requiring credentials.

For example, to tag a signed installer with a licence key:

jsign tag --value userid:1234-ABCD-5678-EFGH installer.exe

For an executable file, the tag can be found next to the timestamp of the signature, in the last few kilobytes of the file.

A common pattern consist in embedding a default template at build time, providing enough space for the data to be inserted when the file is downloaded. This allows a simple search-and-replace operation to be performed on the download server without needing Jsign to be installed. For example:

jsign tag --value "<TEMPLATE>XXXXXXXXXXXXXXXXXXXXXXXXXXX</TEMPLATE>" installer.exe

If the value parameter is omitted Jsign will insert a default 1KB template filled with zeros, delimited by -----BEGIN TAG----- and -----END TAG----- markers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants