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

Unable to retrieve the certificate chain from Azure Trusted Signing (socket write error) #220

Closed ROGG437063 closed 2 weeks ago

ROGG437063 commented 1 month ago
jsign: Unable to retrieve the certificate chain 'codesigningaccount/codesigningprofile'
java.security.KeyStoreException: Unable to retrieve the certificate chain 'codesigningaccount/codesigningprofile'
    at net.jsign.jca.AzureTrustedSigningService.getCertificateChain(AzureTrustedSigningService.java:101)
    at net.jsign.jca.SigningServiceKeyStore.engineGetCertificateChain(SigningServiceKeyStore.java:43)
    at java.base/java.security.KeyStore.getCertificateChain(Unknown Source)
    at net.jsign.SignerHelper.build(SignerHelper.java:321)
    at net.jsign.SignerHelper.sign(SignerHelper.java:435)
    at net.jsign.SignerHelper.execute(SignerHelper.java:278)
    at net.jsign.JsignCLI.execute(JsignCLI.java:166)
    at net.jsign.JsignCLI.main(JsignCLI.java:45)
Caused by: javax.net.ssl.SSLHandshakeException: Remote host terminated the handshake
    at java.base/sun.security.ssl.SSLSocketImpl.handleEOF(Unknown Source)
    at java.base/sun.security.ssl.SSLSocketImpl.decode(Unknown Source)
    at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(Unknown Source)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
    at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
    at java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(Unknown Source)
    at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source)
    at java.base/sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(Unknown Source)
    at java.base/sun.net.www.protocol.http.HttpURLConnection.getOutputStream(Unknown Source)
    at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(Unknown Source)
    at net.jsign.jca.RESTClient.query(RESTClient.java:89)
    at net.jsign.jca.RESTClient.post(RESTClient.java:60)
    at net.jsign.jca.AzureTrustedSigningService.sign(AzureTrustedSigningService.java:142)
    at net.jsign.jca.AzureTrustedSigningService.getCertificateChain(AzureTrustedSigningService.java:98)
    ... 7 more
    Suppressed: java.net.SocketException: Software caused connection abort: socket write error
        at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
        at java.base/java.net.SocketOutputStream.socketWrite(Unknown Source)
        at java.base/java.net.SocketOutputStream.write(Unknown Source)
        at java.base/sun.security.ssl.SSLSocketOutputRecord.encodeAlert(Unknown Source)
        at java.base/sun.security.ssl.TransportContext.fatal(Unknown Source)
        at java.base/sun.security.ssl.TransportContext.fatal(Unknown Source)
        ... 18 more
Caused by: java.io.EOFException: SSL peer shut down incorrectly
    at java.base/sun.security.ssl.SSLSocketInputRecord.read(Unknown Source)
    at java.base/sun.security.ssl.SSLSocketInputRecord.readHeader(Unknown Source)
    at java.base/sun.security.ssl.SSLSocketInputRecord.decode(Unknown Source)
    at java.base/sun.security.ssl.SSLTransport.decode(Unknown Source)
    ... 20 more

Command used

Get-ChildItem -Include $includes -Recurse | Foreach-Object { "--storetype TRUSTEDSIGNING --keystore $rootUrl --storepass $token --alias $signingAccount/$signingProfile $($_.fullname)"} | foreach-object {Start-Process "jsign" $_ -NoNewWindow}

Is it possible, running the process like this causes it to fight over the connection. I am using jsign installed through chocolatey with the jar replaced by the in-dev version from https://github.com/ebourg/jsign/actions/runs/9038916734.

ebourg commented 1 month ago

Thank you for reporting this issue. What version of Java do you use? Are you behind a proxy?

ROGG437063 commented 1 month ago

I am not behind a proxy and I am using the java version that is bundled with the 6.0 chocolatey release.

ebourg commented 1 month ago

Do you get the same error if you run this command from another location (at home or at work) ?

ROGG437063 commented 1 month ago

I actually had tested the command before without getting the error, on the same machine manually, whereas when getting the error it was using that machine as a build agent for azure devops. So I assume it is a race condition kind of thing, caused by starting multiple jsign processes at the same time.

ebourg commented 1 month ago

What's the execution context when it fails? A Docker container on a Windows server?

The error messages "Software caused connection abort: socket write error" and "SSL peer shut down incorrectly" smell like a network issue typically caused by a firewall or antivirus software terminating the connection.

ROGG437063 commented 1 month ago

It is running on a windows server in this context.

ROGG437063 commented 1 month ago

As a sidenote, my approach goes against your advice to provide a filelist as the input for the command when using trusted signing. The reason for that is that we ran into the max lenght of the command line, which is most likely caused by the fact that the chocolatey bin uses a .cmd wrapper. Would you be open to using a powershell wrapper instead.

ebourg commented 1 month ago

we ran into the max lenght of the command line

How many files are you signing? I just noticed that your script invokes Start-Process so I guess several processes are spawned in parallel, and that may be the cause of this issue. A network component probably saturates at some point, maybe the Azure server simply blocks the flood of connections. What happens if you add a 1 second delay before starting the process?

Would you be open to using a powershell wrapper instead.

Is it possible to have both? I'm not familiar with PowerShell, any help would be welcome.

ROGG437063 commented 1 month ago

I am signing about 200 files when the error happened. I was assuming the same thing, I am just not very familiar with the kind of network resources used, and if you can over saturate those or maybe have them be locked by the other process.

I am not sure if it is possible to have both a powershell wrapper and cmd wrapper at the same time. I assume it would be possible to make either an entirely separate powershell module for it, or to register the powershell wrapper as a different bin, maybe called jsignps, during the chocolatey installation. I am not sure if this kind of command line max length is also an issue on ohter platforms than windows.

ebourg commented 1 month ago

Alternatively, Jsign could be improved to support glob patterns and/or signing a whole directory tree recursively. Or accept a file containing a list of files to sign as input.

ROGG437063 commented 1 month ago

Honestly, i'd like to see both, though the second is more powerful, and will probably help more people. I am not sure if globs on windows and linux are equivalent enough to add that without too much difficulty.

ebourg commented 1 month ago

I've implemented the file list in d668c2e07e1e6a1b59c270f49a60d8dec3cd1799. You just have to create a text file with the list of files to sign, one per line, and then specify this file with the @ prefix:

jsign --storetype TRUSTEDSIGNING --keystore {url} --storepass {token} --alias account/profile @files.txt
ROGG437063 commented 1 month ago

Sounds good, i'll test if it works for me. Edit: I see some of the tests failed, so the build did not complete.

ROGG437063 commented 1 month ago

I also have some more or less working code for a very simple powershell module wrapper around jsign, though it probably needs more testing to become part of the main package.

ebourg commented 1 month ago

I see some of the tests failed, so the build did not complete

I've fixed the build error, you can get the artifacts here: https://github.com/ebourg/jsign/actions/runs/9113199977/artifacts/1509522053

ROGG437063 commented 1 month ago

I saw, and will test the feature on my side.

ROGG437063 commented 1 month ago

I get below error when using it with the following list file

jsign: Failed to read the file list: temp.txt
java.nio.charset.MalformedInputException: Input length = 1
        at java.base/java.nio.charset.CoderResult.throwException(Unknown Source)
        at java.base/sun.nio.cs.StreamDecoder.implRead(Unknown Source)
        at java.base/sun.nio.cs.StreamDecoder.read(Unknown Source)
        at java.base/java.io.InputStreamReader.read(Unknown Source)
        at java.base/java.io.BufferedReader.fill(Unknown Source)
        at java.base/java.io.BufferedReader.readLine(Unknown Source)
        at java.base/java.io.BufferedReader.readLine(Unknown Source)
        at java.base/java.nio.file.Files.readAllLines(Unknown Source)
        at java.base/java.nio.file.Files.readAllLines(Unknown Source)
        at net.jsign.JsignCLI.expand(JsignCLI.java:183)
        at net.jsign.JsignCLI.execute(JsignCLI.java:171)
        at net.jsign.JsignCLI.main(JsignCLI.java:50)

temp.txt I also tried without the quotes Does this require a specific java distribution. I believe I am using Azul Zulu JRE 17.48.15

ROGG437063 commented 1 month ago

Edit: I am probably wrong about this because the error outputs the filename correctly.

I think the cause might be the substring(1) for the filename. I use below command or similar java -jar .\jsign-6.1-SNAPSHOT.jar --storetype TRUSTEDSIGNING --keystore neu.codesigning.azure.net --storepass $token --alias prd10523MKBTsa/codesigning "@temp.txt" I assume the quotes around the filename become part of the string, meaning the @ and endquote remain as part of the filepath. I can however not leave them out, because powershell assumes I am trying to use variable splatting using the @ otherwise.

ebourg commented 1 month ago
jsign: Failed to read the file list: temp.txt
java.nio.charset.MalformedInputException: Input length = 1
        at java.base/java.nio.charset.CoderResult.throwException(Unknown Source)
        at java.base/sun.nio.cs.StreamDecoder.implRead(Unknown Source)
        at java.base/sun.nio.cs.StreamDecoder.read(Unknown Source)
        at java.base/java.io.InputStreamReader.read(Unknown Source)
        at java.base/java.io.BufferedReader.fill(Unknown Source)
        at java.base/java.io.BufferedReader.readLine(Unknown Source)
        at java.base/java.io.BufferedReader.readLine(Unknown Source)
        at java.base/java.nio.file.Files.readAllLines(Unknown Source)
        at java.base/java.nio.file.Files.readAllLines(Unknown Source)
        at net.jsign.JsignCLI.expand(JsignCLI.java:183)
        at net.jsign.JsignCLI.execute(JsignCLI.java:171)
        at net.jsign.JsignCLI.main(JsignCLI.java:50)

Files.readAllLines() fails to read the text file due its encoding (UTF-16 with byte order marks). Try writing the file in UTF-8 without BOM or in ISO-8859-1.

@temp.txt can be quoted, but not the files inside.

ebourg commented 1 month ago

Another iteration, supporting UTF-16 files and quoted file names: https://github.com/ebourg/jsign/actions/runs/9116054327/artifacts/1510286219

ROGG437063 commented 1 month ago

New build seems to be working well.

ROGG437063 commented 1 month ago

Given that the filelist makes it unneccesary to solve this issue, that doesn't really seem to be a real bug, I think this issue can be closed. However, I was wondering if you already have an ETA on the full 6.1 release?

ebourg commented 1 month ago

I've further refined the command line tool, it now supports wildcard patterns. For example:

The artifacts are here if you want to give it a try: https://github.com/ebourg/jsign/actions/runs/9129076140/artifacts/1513339707

The original issue wasn't a bug but it still led to interesting improvements.

I think I'll cut the new release next month.

ROGG437063 commented 1 month ago

Sounds good, wildcard pasterns are a great addition, especially as signtool and trusted singing powershell seem to omit these features, which makes signing multiple files a nightmare.