[BUG]: Can't provide my own rootCert.pfx
Description
I was hoping that I should be able to do more or less the following:
openssl req -subj "/CN=Dev Proxy CA" -nodes -x509 -sha256 -days 1825 -newkey rsa:2048 -keyout "ca.key.pem" -out "ca.crt.pem"
openssl pkcs12 -export -out ~/.config/dev-proxy/rootCert.pfx -inkey ca.key.pem -in ca.crt.pem -macalg SHA1 -iter 2000 -maciter -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES
I.e., instead of using a rootCert generated by devproxy on first run, I pre-create rootCert.pfx and place it in the expected location. Sure enough, devproxy starts without errors (even with Trace log level by the way). But! When I try to do any HTTPS call, I get the following:
$ curl -v https://localhost:3002/health
* Uses proxy env variable no_proxy == 'localhost:8006'
* Uses proxy env variable https_proxy == 'http://localhost:8000'
* Host localhost:8000 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:8000...
* connect to ::1 port 8000 from ::1 port 38158 failed: Connection refused
* Trying 127.0.0.1:8000...
* CONNECT: no ALPN negotiated
* allocate connect buffer
* Establish HTTP proxy tunnel to localhost:3002
> CONNECT localhost:3002 HTTP/1.1
> Host: localhost:3002
> User-Agent: curl/8.16.0
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 Connection Established
< Content-Length: 0
* Ignoring Content-Length in CONNECT 200 response
<
* CONNECT phase completed
* CONNECT tunnel established, response 200
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* CAfile: /etc/ssl/certs/ca-certificates.crt
* CApath: none
* TLSv1.3 (OUT), TLS alert, decode error (562):
* TLS connect error: error:0A000126:SSL routines::unexpected eof while reading
* closing connection #0
curl: (35) TLS connect error: error:0A000126:SSL routines::unexpected eof while reading
What I would like to know:
- Why can't devproxy start with a provided rootCert.pfx?
- Why doesn't devproxy log clearly that it can't use the cert, or what it expects?
- Can you glean from the above openssl commands why it wouldn't work? Also see attached here the unencrypted dumps original.txt, which is output from a by-devproxy-generated
rootCert.pfxand custom.txt, which is therootCert.pfxgenerated by the above commands.
Expected behaviour
I expect to be able to provide my own rootCert.pfx, or at least to get clear logging about what is wrong with a provided certificate.
Actual behaviour
devproxy fails silently.
Steps to reproduce
- Place your own
rootCert.pfx - Start
devproxy - Do a call to an HTTPS url
- Observe
unexpected eof while reading
Dev Proxy Version
1.2.0
Operating system (environment)
Linux
Shell
bash
Configuration file
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v1.2.0/rc.schema.json",
"plugins": [
{
"enabled": true,
"name": "OpenApiSpecGeneratorPlugin",
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
"specFormat": "Yaml",
"specVersion": "v3_0"
}
],
"languageModel": {
"enabled": true,
"model": "qwen3-coder"
},
"logLevel": "debug",
"newVersionNotification": "stable",
"showSkipMessages": true,
"showTimestamps": true,
"validateSchemas": true,
"urlsToWatch": [
"*"
]
}
Additional Info
No response
Could the issue be caused by curl not trusting your cert?
@waldekmastykarz no, it can't - that would be a different kind of error; curl trusts certificates in /etc/ca-certificates/trust-source/anchors/, in combination with the update-ca-trust utility, which causes these certificates to exist in the CAFile bundle which you see in the curl output above (/etc/ssl/certs/ca-certificates.crt); the certificate was installed there too and this was verified by calling the service trusting a specific CA (a.k.a. CA TLS pinning) using curl without -k / --insecure. Additionally, I manually checked that the specific certificate did indeed exist in said CAfile.
Regardless, the above, is there a log setting which can show for example if devproxy actually succesfully can use/find the certificate and key within the .pfx file? More logging would be extremely helpful in this case. I was thinking that maybe the certificate is not found by CN=Dev Proxy CA but by localKeyID=00 00 00 00, in which case we would be doomed, because afaik, openssl cannot specify the localKeyID on command-line (and I'm not looking forward to writing a C program to do it). I ask this because I accidentally noticed that the openssl command above to convert to a .pfx file, when said .pfx file exists, actually appends the certificates to the file, so I gather that a .pfx file can be an arbitrary collection of multiple certificates and keys with arbitrary localKeyID's, and if devproxy follows spec, it needs something to "find" its key.. just a brain dump of an idea.
Also, I assume you could reproduce the issue with my instructions - it would be helpful to know you can reproduce it!
Thanks for the clarification. Loading certificates is handled downstream from our code so I don't think we can expose any more logging information unfortunately. Have you tried comparing the certificate you generate with the one you get from Dev Proxy to see if there's a difference?
When I needed a custom cert for use with Dev Proxy, I used the following commands:
# Generate RSA private key (2048 bits)
openssl genrsa -out rootCert.key 2048
# Create root certificate (self-signed, modified to 365 days)
# Valid from: 2025-08-11 06:42:57 (current time - 1 day)
# Valid to: 2026-08-11 06:42:57 (current time + 364 days, since we start 1 day back)
openssl req -new -x509 -key rootCert.key -out rootCert.crt \
-not_before 20250811064257Z \
-not_after 20260811064257Z \
-subj "/CN=YourSubjectCN" \
-addext "keyUsage=critical,keyCertSign,cRLSign" \
-addext "basicConstraints=critical,CA:TRUE,pathlen:0" \
-sha256
# Export as PKCS#12 (empty password, exportable)
openssl pkcs12 -export -out rootCert.pfx -inkey rootCert.key -in rootCert.crt -password pass:
So I tried to further reduce this to figure out why your openssl generation commands work, and mine don't. I figured out this is due to the -not-before value.
For some reason, devproxy seems to require a certificate that is 26 hours "old"; when one specifies -days 365 as opposed to the -not-before and -not-after parameters, the start date is whenever the command runs, and for some reason unknown to me, devproxy does not consider that valid enough.
This has to be a bug right?
Does not work (failed 10/10 times):
# Generate a certificate valid since 24 hours ago
# Note: on macOS, use: date -v -24H +%Y%m%d%H%M%SZ
openssl req -subj "/CN=Foo Cert" -nodes -x509 -sha256 -not_before $(date --date='-24 hours' +%Y%m%d%H%M%SZ) -not_after $(date --date='+1 year' +%Y%m%d%H%M%SZ) -newkey rsa:2048 -addext "keyUsage=critical,keyCertSign,cRLSign" -addext "basicConstraints=critical,CA:TRUE,pathlen:0" -keyout "rootCert.key" -out "rootCert.crt"
Does work (succeeded 10/10 times):
# Generate a certificate valid since 26 hours ago
# Note: on macOS, use: date -v -26H +%Y%m%d%H%M%SZ
openssl req -subj "/CN=Foo Cert" -nodes -x509 -sha256 -not_before $(date --date='-26 hours' +%Y%m%d%H%M%SZ) -not_after $(date --date='+1 year' +%Y%m%d%H%M%SZ) -newkey rsa:2048 -addext "keyUsage=critical,keyCertSign,cRLSign" -addext "basicConstraints=critical,CA:TRUE,pathlen:0" -keyout "rootCert.key" -out "rootCert.crt"
Interesting observation. As far as I know, Dev Proxy internals don't do any specific certificate processing. Here's the point in code where it's loading the certificate: https://github.com/svrooij/unobtanium-web-proxy/blob/5a6d45bcebb55c8e85b75bddc7ffe7f7ebd69d76/src/Unobtanium.Web.Proxy/Certificates/CertificateManager.cs#L609. As far as I can see, there's only a check for not after.
Just to double check: are you running the command to generate the cert on the same machine as where you're loading the cert? Is there any chance this might be related to timezones?
@waldekmastykarz yes, same machine, it's definitely not a wrong-clock-on-other-system issue.
What strikes me is the (in my case) 1 day + 2 hours; could it be an off by one for the day and then 2 hours in my specific case because my system is set to CET, which is +2 UTC?
Here's the point in code where it's loading the certificate: https://github.com/svrooij/unobtanium-web-proxy/blob/5a6d45bcebb55c8e85b75bddc7ffe7f7ebd69d76/src/Unobtanium.Web.Proxy/Certificates/CertificateManager.cs#L609.
I see log calls in that code; can I somehow set a setting, or environment variable that make those log messages appear?
Here's the point in code where it's loading the certificate: https://github.com/svrooij/unobtanium-web-proxy/blob/5a6d45bcebb55c8e85b75bddc7ffe7f7ebd69d76/src/Unobtanium.Web.Proxy/Certificates/CertificateManager.cs#L609.
I see log calls in that code; can I somehow set a setting, or environment variable that make those log messages appear?
At the moment, the only way would be to update how we instantiate the ProxyServer here: https://github.com/dotnet/dev-proxy/blob/fd15fb590f24afbdc3902c9ca581d2aced289d68/DevProxy/Proxy/ProxyEngine.cs#L59 and pass the LoggerFactory to the constructor. You'd need to update the code and build Dev Proxy yourself. We're not doing this at the moment in our code at the moment. Sorry for the trouble.
Closing due to lack of further comments