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

Support for Oracle OCI Vault #213

Closed Edzilla2000 closed 3 months ago

Edzilla2000 commented 3 months ago

Hi, I'm looking at using Oracle OCI Vault HSM with jsign but I don't believe it's supported yet. The service is free up to 10 keys so I believe it could be a great option.

ebourg commented 3 months ago

Yes OCI Vault is on my radar, I haven't got the time to investigate further though. Contributions are welcome :)

Edzilla2000 commented 3 months ago

@ebourg unfortunately I'm not a developer, but if someone works on it I can definitely do tests

ebourg commented 3 months ago

@Edzilla2000 Oracle Cloud support is now implemented, could you give it a try please? There is a snapshot build available here: https://github.com/ebourg/jsign/actions/runs/8455078861/artifacts/1363719072

Edzilla2000 commented 3 months ago

Of course @ebourg . That was... fast, thanks!

Edzilla2000 commented 3 months ago

@ebourg I just tried it, this is what I get:

 java -jar jsign-6.1-SNAPSHOT.jar --storetype ORACLECLOUD --alias ocid1.key.oc1.ca-montreal-1.abcdejfhdsf.fdslkfqhksdfjhsqdgfkjqsdfgjkqsdfjkq --certfile domain.crt putty.exe
Adding Authenticode signature to putty.exe
Exception in thread "main" java.lang.NoClassDefFoundError: net/jsign/bouncycastle/pqc/jcajce/provider/sphincsplus/SPHINCSPlusKeyFactorySpi
        at net.jsign.bouncycastle.jce.provider.BouncyCastleProvider.loadPQCKeys(Unknown Source)
        at net.jsign.bouncycastle.jce.provider.BouncyCastleProvider.setup(Unknown Source)
        at net.jsign.bouncycastle.jce.provider.BouncyCastleProvider.access$000(Unknown Source)
        at net.jsign.bouncycastle.jce.provider.BouncyCastleProvider$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at net.jsign.bouncycastle.jce.provider.BouncyCastleProvider.<init>(Unknown Source)
        at net.jsign.PrivateKeyUtils.readPrivateKeyPEM(PrivateKeyUtils.java:88)
        at net.jsign.PrivateKeyUtils.load(PrivateKeyUtils.java:66)
        at net.jsign.jca.OracleCloudCredentials.getPrivateKey(OracleCloudCredentials.java:74)
        at net.jsign.jca.OracleCloudSigningService.sign(OracleCloudSigningService.java:212)
        at net.jsign.jca.RESTClient.query(RESTClient.java:80)
        at net.jsign.jca.RESTClient.post(RESTClient.java:59)
        at net.jsign.jca.OracleCloudSigningService.sign(OracleCloudSigningService.java:151)
        at net.jsign.jca.SigningServiceSignature.engineSign(SigningServiceSignature.java:46)
        at java.security.Signature$Delegate.engineSign(Unknown Source)
        at java.security.Signature.sign(Unknown Source)
        at net.jsign.bouncycastle.operator.jcajce.JcaContentSignerBuilder$1.getSignature(Unknown Source)
        at net.jsign.bouncycastle.cms.SignerInfoGenerator.generate(Unknown Source)
        at net.jsign.bouncycastle.cms.CMSSignedDataGenerator.generate(Unknown Source)
        at net.jsign.asn1.authenticode.AuthenticodeSignedDataGenerator.getSignerInfo(AuthenticodeSignedDataGenerator.java:56)
        at net.jsign.asn1.authenticode.AuthenticodeSignedDataGenerator.generate(AuthenticodeSignedDataGenerator.java:43)
        at net.jsign.AuthenticodeSigner.createSignedData(AuthenticodeSigner.java:387)
        at net.jsign.AuthenticodeSigner.sign(AuthenticodeSigner.java:362)
        at net.jsign.SignerHelper.sign(SignerHelper.java:396)
        at net.jsign.JsignCLI.execute(JsignCLI.java:135)
        at net.jsign.JsignCLI.main(JsignCLI.java:40)
Caused by: java.lang.ClassNotFoundException: net.jsign.bouncycastle.pqc.jcajce.provider.sphincsplus.SPHINCSPlusKeyFactorySpi
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        ... 26 more

I'm using jdk8, I will try with a more recent version tomorrow.

Edzilla2000 commented 3 months ago

@ebourg I get the same result with a more recent version of Java

ebourg commented 3 months ago

This is a packaging issue, the unused PQC BouncyCastle classes are removed from the final jar but this breaks the parsing of encrypted key files. This was reported in #163 and I kind of hoped to simply ignore this issue since private key files for signing are deprecated, but here the private key is required for calling the OCI API.

ebourg commented 3 months ago

The issue is fixed, here is the snapshot if you want to give it a try: https://github.com/ebourg/jsign/actions/runs/8484299830/artifacts/1370306942

Edzilla2000 commented 2 months ago

@ebourg When trying with a private key that requires a password, it fails because it's not able to get the password from the command line:

java -jar jsign-6.1-SNAPSHOT.jar --keypass password --storetype ORACLECLOUD --alias ocid1.key.oc1.ca-montreal-1.abcdejfhdsf.fdslkfqhksdfjhsqdgfkjqsdfgjkqsdfjkq --certfile domain.crt putty.exe Adding Authenticode signature to putty.exe jsign: Couldn't sign putty.exe java.lang.RuntimeException: Unable to load the private key at net.jsign.jca.OracleCloudCredentials.getPrivateKey(OracleCloudCredentials.java:76) at net.jsign.jca.OracleCloudSigningService.sign(OracleCloudSigningService.java:216) at net.jsign.jca.RESTClient.query(RESTClient.java:80) at net.jsign.jca.RESTClient.post(RESTClient.java:59) at net.jsign.jca.OracleCloudSigningService.sign(OracleCloudSigningService.java:155) at net.jsign.jca.SigningServiceSignature.engineSign(SigningServiceSignature.java:46) at java.base/java.security.Signature$Delegate.engineSign(Signature.java:1424) at java.base/java.security.Signature.sign(Signature.java:713) at net.jsign.bouncycastle.operator.jcajce.JcaContentSignerBuilder$1.getSignature(Unknown Source) at net.jsign.bouncycastle.cms.SignerInfoGenerator.generate(Unknown Source) at net.jsign.bouncycastle.cms.CMSSignedDataGenerator.generate(Unknown Source) at net.jsign.asn1.authenticode.AuthenticodeSignedDataGenerator.getSignerInfo(AuthenticodeSignedDataGenerator.java:56) at net.jsign.asn1.authenticode.AuthenticodeSignedDataGenerator.generate(AuthenticodeSignedDataGenerator.java:43) at net.jsign.AuthenticodeSigner.createSignedData(AuthenticodeSigner.java:387) at net.jsign.AuthenticodeSigner.sign(AuthenticodeSigner.java:362) at net.jsign.SignerHelper.sign(SignerHelper.java:396) at net.jsign.JsignCLI.execute(JsignCLI.java:135) at net.jsign.JsignCLI.main(JsignCLI.java:40) Caused by: java.security.KeyException: Failed to load the private key from C:\Users\edzil.oci\oci_api_key.pem at net.jsign.PrivateKeyUtils.load(PrivateKeyUtils.java:73) at net.jsign.jca.OracleCloudCredentials.getPrivateKey(OracleCloudCredentials.java:74) ... 17 more Caused by: net.jsign.bouncycastle.pkcs.PKCSException: unable to read encrypted data: password empty at net.jsign.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo.decryptPrivateKeyInfo(Unknown Source) at net.jsign.PrivateKeyUtils.readPrivateKeyPEM(PrivateKeyUtils.java:138) at net.jsign.PrivateKeyUtils.load(PrivateKeyUtils.java:70) ... 18 more Caused by: java.lang.IllegalArgumentException: password empty at net.jsign.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$BasePBKDF2.engineGenerateSecret(Unknown Source) at java.base/javax.crypto.SecretKeyFactory.generateSecret(SecretKeyFactory.java:334) at net.jsign.bouncycastle.openssl.jcajce.PEMUtilities.generateSecretKeyForPKCS5Scheme2(Unknown Source) at net.jsign.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder$1.get(Unknown Source) ... 21 more Try `java -jar jsign.jar --help' for more information.

Edzilla2000 commented 2 months ago

When trying with a private key without a password, it fails because I think the URL you're constructing is not correct: It expects "{namespace}-crypto.kms.{region}.oraclecloud.com" and it's using "{namespace}-crypto.kms.{region}.oci.oraclecloud.com"

java -jar jsign-6.1-SNAPSHOT.jar --storetype ORACLECLOUD --alias ocid1.key.oc1.ca-montreal-1.abcde.abcdefghig --certfile domain.crt putty.exe Adding Authenticode signature to putty.exe Apr 04, 2024 10:44:12 AM com.oci.security.keyvault.jca.OCIJcaProvider INFO: OCIJCAProvider constructor is invoked Apr 04, 2024 10:44:12 AM com.oci.security.keyvault.jca.OCIJcaProvider initialize INFO: OCIJCAProvider initialize is invoked Apr 04, 2024 10:44:12 AM com.oci.security.keyvault.jca.implementation.signature.KeyVaultKeylessRsaSignature INFO: KeyVaultKeylessRsaSignature Constructor is invoked Apr 04, 2024 10:44:12 AM com.oci.security.keyvault.jca.implementation.signature.KeyVaultKeylessRsaSignature INFO: KeyVaultKeylessRsaSignature Constructor is invoked Apr 04, 2024 10:44:12 AM com.oci.security.keyvault.jca.implementation.signature.KeyVaultKeylessEcSignature INFO: KeyVaultKeylessEcSignature Constructor is invoked Apr 04, 2024 10:44:12 AM com.oci.security.keyvault.jca.implementation.signature.KeyVaultKeylessEcSignature INFO: KeyVaultKeylessEcSignature Constructor is invoked Apr 04, 2024 10:44:12 AM com.oci.security.keyvault.jca.implementation.signature.KeyVaultKeylessEcSignature INFO: KeyVaultKeylessEcSignature Constructor is invoked Apr 04, 2024 10:44:12 AM com.oci.security.keyvault.jca.OCIJcaProvider lambda$0 INFO: OCIJCAProvider initialize is complete Apr 04, 2024 10:44:12 AM com.oci.security.keyvault.jca.OCIJcaProvider INFO: OCIJCAProvider constructor is completed jsign: Couldn't sign putty.exe net.jsign.bouncycastle.operator.RuntimeOperatorException: exception obtaining signature: java.security.GeneralSecurityException: java.net.UnknownHostException: abcde-crypto.kms.ca-montreal-1.oci.oraclecloud.com at net.jsign.bouncycastle.operator.jcajce.JcaContentSignerBuilder$1.getSignature(Unknown Source) at net.jsign.bouncycastle.cms.SignerInfoGenerator.generate(Unknown Source) at net.jsign.bouncycastle.cms.CMSSignedDataGenerator.generate(Unknown Source) at net.jsign.asn1.authenticode.AuthenticodeSignedDataGenerator.getSignerInfo(AuthenticodeSignedDataGenerator.java:56) at net.jsign.asn1.authenticode.AuthenticodeSignedDataGenerator.generate(AuthenticodeSignedDataGenerator.java:43) at net.jsign.AuthenticodeSigner.createSignedData(AuthenticodeSigner.java:387) at net.jsign.AuthenticodeSigner.sign(AuthenticodeSigner.java:362) at net.jsign.SignerHelper.sign(SignerHelper.java:396) at net.jsign.JsignCLI.execute(JsignCLI.java:135) at net.jsign.JsignCLI.main(JsignCLI.java:40) Caused by: java.security.SignatureException: java.security.GeneralSecurityException: java.net.UnknownHostException: abcde-crypto.kms.ca-montreal-1.oci.oraclecloud.com at net.jsign.jca.SigningServiceSignature.engineSign(SigningServiceSignature.java:48) at java.security.Signature$Delegate.engineSign(Signature.java:1210) at java.security.Signature.sign(Signature.java:582) ... 10 more Caused by: java.security.GeneralSecurityException: java.net.UnknownHostException: abcde-crypto.kms.ca-montreal-1.oci.oraclecloud.com at net.jsign.jca.OracleCloudSigningService.sign(OracleCloudSigningService.java:159) at net.jsign.jca.SigningServiceSignature.engineSign(SigningServiceSignature.java:46) ... 12 more Caused by: java.net.UnknownHostException: abcde-crypto.kms.ca-montreal-1.oci.oraclecloud.com at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:184) at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) at java.net.Socket.connect(Socket.java:589) at sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:666) at sun.security.ssl.BaseSSLSocketImpl.connect(BaseSSLSocketImpl.java:173) at sun.net.NetworkClient.doConnect(NetworkClient.java:180) at sun.net.www.http.HttpClient.openServer(HttpClient.java:463) at sun.net.www.http.HttpClient.openServer(HttpClient.java:558) at sun.net.www.protocol.https.HttpsClient.(HttpsClient.java:264) at sun.net.www.protocol.https.HttpsClient.New(HttpsClient.java:367) at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(AbstractDelegateHttpsURLConnection.java:191) at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1156) at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1050) at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:177) at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1334) at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1309) at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:259) at net.jsign.jca.RESTClient.query(RESTClient.java:88) at net.jsign.jca.RESTClient.post(RESTClient.java:59) at net.jsign.jca.OracleCloudSigningService.sign(OracleCloudSigningService.java:155) ... 13 more Try `java -jar jsign.jar --help' for more information.

Edzilla2000 commented 2 months ago

If I add the host to the hosts file, I get this error.

jsign: Couldn't sign putty.exe
java.security.SignatureException: Signature verification failed, the private key doesn't match the certificate
        at net.jsign.AuthenticodeSigner.verify(AuthenticodeSigner.java:502)
        at net.jsign.AuthenticodeSigner.createSignedData(AuthenticodeSigner.java:390)
        at net.jsign.AuthenticodeSigner.sign(AuthenticodeSigner.java:362)
        at net.jsign.SignerHelper.sign(SignerHelper.java:396)
        at net.jsign.JsignCLI.execute(JsignCLI.java:135)
        at net.jsign.JsignCLI.main(JsignCLI.java:40)
Caused by: java.security.SignatureException: Signature length not correct: got 512 but was expecting 256
        at sun.security.rsa.RSASignature.engineVerify(RSASignature.java:189)
        at java.security.Signature$Delegate.engineVerify(Signature.java:1222)
        at java.security.Signature.verify(Signature.java:655)
        at net.jsign.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder$SigVerifier.verify(Unknown Source)
        at net.jsign.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder$RawSigVerifier.verify(Unknown Source)
        at net.jsign.bouncycastle.cms.SignerInformation.doVerify(Unknown Source)
        at net.jsign.bouncycastle.cms.SignerInformation.verify(Unknown Source)
        at net.jsign.bouncycastle.cms.CMSSignedData.verifySignatures(Unknown Source)
        at net.jsign.AuthenticodeSigner.verify(AuthenticodeSigner.java:493)
        ... 5 more

This might be because I'm using a self signed certificate, as we've not yet gone ahead and bought a code signing certificate since we're still evaluating the process. But it works when I follow this documentation: https://www.ateam-oracle.com/post/oci-jca-provider-use-case-signing-jar-files

ebourg commented 2 months ago

When trying with a private key that requires a password, it fails because it's not able to get the password from the command line: Caused by: java.lang.IllegalArgumentException: password empty at net.jsign.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$BasePBKDF2.engineGenerateSecret(Unknown Source) at java.base/

Are you sure the password is specified in the .oci/config file ? It looks like the password is missing or empty.

Edzilla2000 commented 2 months ago

When trying with a private key that requires a password, it fails because it's not able to get the password from the command line: Caused by: java.lang.IllegalArgumentException: password empty at net.jsign.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$BasePBKDF2.engineGenerateSecret(Unknown Source) at java.base/

Are you sure the password is specified in the .oci/config file ? It looks like the password is missing or empty.

It's not, I used the "--keypass" argument. I'd rather not put it in a file but if I have to it's not such an issue

ebourg commented 2 months ago

I see, --keypass is not used by this keystore type, I'll think about it.

Alternatively you can set the OCI_CLI_PASS_PHRASE environment variable if you don't want to store the password in a file.

Edzilla2000 commented 2 months ago

That's perfect, thanks.

ebourg commented 2 months ago

When trying with a private key without a password, it fails because I think the URL you're constructing is not correct: It expects "{namespace}-crypto.kms.{region}.oraclecloud.com" and it's using "{namespace}-crypto.kms.{region}.oci.oraclecloud.com"

Caused by: java.net.UnknownHostException: abcde-crypto.kms.ca-montreal-1.oci.oraclecloud.com

Very interesting, I've created a test key in the eu-paris-1 region and the endpoint was under the oci.oraclecloud.com domain. I wonder if all keys for a given region use the same domain. In this case I could use a static mapping. If not I have to fetch the vault properties before signing.

Edzilla2000 commented 2 months ago

When trying with a private key without a password, it fails because I think the URL you're constructing is not correct: It expects "{namespace}-crypto.kms.{region}.oraclecloud.com" and it's using "{namespace}-crypto.kms.{region}.oci.oraclecloud.com" Caused by: java.net.UnknownHostException: abcde-crypto.kms.ca-montreal-1.oci.oraclecloud.com

Very interesting, I've created a test key in the eu-paris-1 region and the endpoint was under the oci.oraclecloud.com domain. I wonder if all keys for a given region use the same domain. In this case I could use a static mapping. If not I have to fetch the vault properties before signing.

When looking at the definition it does mention the crypto endpoint, so you probably do have to fetch it from the API: image

ebourg commented 2 months ago

Yes, but the API uses the management endpoint, which has to be guessed as well. And looking at the OCI console it seems it's also possible to define private endpoints with custom URIs. So this means the management endpoint has to be an input parameter of the signing process.

I have to map the parameters differently, --keystore would specify the management endpoint, and the profile should be concatenated to the --storepass parameter.

ebourg commented 2 months ago

I've made a few changes:

Let me know how it works for you.