From 5a12a8923934303c4a431cad4a9502b23d81e3c8 Mon Sep 17 00:00:00 2001 From: Heath Stewart Date: Tue, 5 May 2026 21:22:58 -0700 Subject: [PATCH 1/2] Separate self-signed CA and TLS certificate Rust's default HTTP stack - reqwest - does not support a self-signed TLS certificate. Instead, we split the CA out while retaining the old dotnet-devcert.crt public key PEM. dotnet-devcert.pfx is still the key pair for the server TLS certificate, but is signed by the separate CA. This should require no code changes to other languages' test-proxy implementations. Relates to Azure/azure-sdk-for-rust#4345 --- eng/common/testproxy/.gitignore | 3 + eng/common/testproxy/ca.conf | 17 ++++ eng/common/testproxy/ca.crt | 18 +++++ eng/common/testproxy/dotnet-devcert.crt | 35 ++++----- eng/common/testproxy/dotnet-devcert.pfx | Bin 2627 -> 3467 bytes eng/common/testproxy/localhost.conf | 10 ++- eng/common/testproxy/localhost.crt | 21 +++++ eng/common/testproxy/rotate.sh | 100 ++++++++++++++++++++++++ 8 files changed, 181 insertions(+), 23 deletions(-) create mode 100644 eng/common/testproxy/.gitignore create mode 100644 eng/common/testproxy/ca.conf create mode 100644 eng/common/testproxy/ca.crt create mode 100644 eng/common/testproxy/localhost.crt create mode 100755 eng/common/testproxy/rotate.sh diff --git a/eng/common/testproxy/.gitignore b/eng/common/testproxy/.gitignore new file mode 100644 index 000000000000..87235280c852 --- /dev/null +++ b/eng/common/testproxy/.gitignore @@ -0,0 +1,3 @@ +*.csr +*.key +*.srl diff --git a/eng/common/testproxy/ca.conf b/eng/common/testproxy/ca.conf new file mode 100644 index 000000000000..936e317dc73d --- /dev/null +++ b/eng/common/testproxy/ca.conf @@ -0,0 +1,17 @@ +# Configuration for the self-signed CA used to sign the localhost TLS certificate. +# Parameters are kept in sync with the original dotnet-devcert.crt so that +# dotnet dev-certs and other tooling recognises the certificate chain. + +[req] +prompt = no +default_bits = 2048 +distinguished_name = subject +x509_extensions = x509_ext + +[ subject ] +commonName = Azure SDK + +[x509_ext] +basicConstraints = critical, CA:true +keyUsage = critical, keyCertSign, cRLSign +subjectKeyIdentifier = hash diff --git a/eng/common/testproxy/ca.crt b/eng/common/testproxy/ca.crt new file mode 100644 index 000000000000..2158e805d820 --- /dev/null +++ b/eng/common/testproxy/ca.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC+DCCAeCgAwIBAgIUQSfEYlZEwuRS9elscvH2orem7+YwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJQXp1cmUgU0RLMB4XDTI2MDUwNjAzMjc0N1oXDTI3MDUw +NjAzMjc0N1owFDESMBAGA1UEAwwJQXp1cmUgU0RLMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA5m62azoHdadoISPK6k0ymBfjgNgr/uuQDgEPCsufVK9g +0buf2ewvOIU1pJCWNYHCqqLTArmyyavDhM8ASIlb4rdRkuWjLaVxvQ+RxrKgYY4p +xB09fkZOOsG9O+ztj3ssm5qW6vZY1KcL8XGCEXQX55XaCikbg2IWOOV1LQzAbgFq +QYB4m84x7tmUrfI0TNblq0BRiLKZQYvPfQasTa76tMZUEvBUCfK7auHQCUZt1ORc +yAu16BiEL3MOSi9vhoVgWFLG0Wl0w825oZcrBpH6m+KCZ5Jyo2TgebQAqADMYf37 +ha/NjvTLCZ+pBS+YTXz3lc09kInzcmG97IZlqdrQ8wIDAQABo0IwQDAPBgNVHRMB +Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUGw7rgyiywUwEpTbF +pG+/TKJW8AAwDQYJKoZIhvcNAQELBQADggEBALiizNASsF8VYEqtTunSbyu/xIea +sxJ8z0Oczk3Xn5bzy1FJmZq86hzfer5cYP14eaBuCM1rCioxA0n3ZbaTTmhQwccx +tfs4lQLrE772DJ0wxLbg8jvX7h7x9boyap5pvxv4yW54mQFgXBUjKutl36ZYLHKQ +G8ZMT34FG+g3NUMa+BNrqbYFSy5pomHyiTDxMRJAqIn/G1rGlYCRzOP6jm0oogGI +CA8+B/Cw/0XONJCoOCZV97lAwZz1EoH7QTtVqRMHbKfwKZOKjrB/LdLkhD3CnRda +PNqyXc2isGPyOlVm1HWoQGe0x6fDd/MObHpnTS4/hb3mLuOghdkUgtNDE6w= +-----END CERTIFICATE----- diff --git a/eng/common/testproxy/dotnet-devcert.crt b/eng/common/testproxy/dotnet-devcert.crt index 931b6e739722..d12cd49a7a65 100644 --- a/eng/common/testproxy/dotnet-devcert.crt +++ b/eng/common/testproxy/dotnet-devcert.crt @@ -1,21 +1,18 @@ -----BEGIN CERTIFICATE----- -MIIDZzCCAk+gAwIBAgIUXUCvBB5U6rYQyAEu5rApzjp3RQYwDQYJKoZIhvcNAQEL -BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI1MDcyMzE3NTUyM1oXDTI2MDcy -MzE3NTUyM1owFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEA67HYsBPB865zJS8TDwUBgFSKqWgJ7dGTQh3aTlvamFED -5/kHnL7oTr3x/6Hylnajf0v4vGWmSok0/3SAcbpr/9l19/7zbpA1LLGzu8G909o5 -38Wl5sNbGvQIMK91KxbHRXlVMKoYTIL38cNdZvhzfb9m9Tew6vPmz4ABMPiwYS9T -R19lAPwmQYwce00NkKaQE5+6pzsPhnG/o/Ww9rBE370fidXn8jhqLSOEk+hbp3ju -KlxeSrVHAqlvTzlvSTZGRyxioRLDEMFT3ka1cyLo6HP3U7lj76mlJBibahE+ylL+ -z594fzHnfYPQaN5g13G9H2oxTg+VwwNtL1U737FNiwIDAQABo4GwMIGtMA8GA1Ud -EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGmMBYGA1UdJQEB/wQMMAoGCCsGAQUF -BwMBMBcGA1UdEQEB/wQNMAuCCWxvY2FsaG9zdDA6BgorBgEEAYI3VAEBBCwMKkFT -UC5ORVQgQ29yZSBIVFRQUyBkZXZlbG9wbWVudCBjZXJ0aWZpY2F0ZTAdBgNVHQ4E -FgQUg5xH/y5mh7lGgF+y+1sJC7ZMN9owDQYJKoZIhvcNAQELBQADggEBAK/gRk/L -5q4/xXXYW77WawygxpGmTgBLDHkiJViMnJ4VDk6q97/DMABIqCp3sv3FNP9sOssG -ypgmX4jYbyrLNwfvdtpVaiHAHvrTfAtrfHgG0EVM1DRUJ1e0/SRfYf1QTdVxEZv+ -3IM+0roYa8EtQq8BxSsAZz+zhNMoav8nXQVhR7+v5NI5vzT0TVncfXIYYYfLhllb -wcmh9iQuXMifj7WohOFE1XK4O6Bats/6V85ZSGDl3npEpYcgBwyxNQE5hKn/lG5b -3DDkpCTeoMZxAHLo39RzAy0WJTF1KPHQ2EzUa+MfoTNWNrhYSW3IqI3xfPzevSDx -BTBk4gS9MSt2Jj8= +MIIC+DCCAeCgAwIBAgIUTERanoHsZOEjB76v65W0rLn2V6YwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJQXp1cmUgU0RLMB4XDTI2MDUwNjAzMzYzMVoXDTI3MDUw +NjAzMzYzMVowFDESMBAGA1UEAwwJQXp1cmUgU0RLMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAvx6UCYiwPOpnqdM1HbR8t0bjwBeVa/sbrrFdLdgb/xaH +KfAsRVrH/OJ2jl+jlcJnztbq0eSA190mweUwx4S5D7cxCa6qEduAR+k+8ouGafHr +WQdOad21w021OCflEEuSmRqShFCywtDSgSWkisS4NIrDr0ICZktRS+VhhS30PQXq +tK/3Aa2m3Kq1HRzPYgwZqjP2c4+EkTHsampWqR/pPZNP/aMnJmTfXAkXJKU5lWXX +Zvb59BTHE8VV4XSx9mTR6oaFjBwmW5InughnVS5R9HokmfqbBqqHYxNyguzcvnl6 +otefWjKoMdDU/cpIOQZD0eOwaL5RfvXszYqkOZA4WwIDAQABo0IwQDAPBgNVHRMB +Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUk8UofztnXWxIU77V +8Wxr6XX0w7UwDQYJKoZIhvcNAQELBQADggEBAEGM74rMZuT6qQeBPADgsg9TKJCP +PoruOauStTTriejBtOJXdxnaPOP22vhnFvrsxxRgajlpBKQsb3mJCp5pBtSFhcoO +0sGrcV/fjhTY7n3/vaZgqkv1NF9mkuMkfaXS0lf+FwYjPuX5bmM1o/ZxB2GFc9UV +yAQQZjG2XNh110z3orEhF7k/3VtuF9T2zW1Sc2kpy90iNR3a28C48wKnfJuQ63/D +W2FUtNVII0kLyVP6F457rodZ1Ih++Zr2IS4OxYew0jDqW3mG7govmj1G286sjbSp +v6RUMq0dIm4eQ/xz63CCtpZKDf2mIEXapJq3Y2qDTO+jmqIjCvlb1vg3CFs= -----END CERTIFICATE----- diff --git a/eng/common/testproxy/dotnet-devcert.pfx b/eng/common/testproxy/dotnet-devcert.pfx index 93a5617aea60ca987fe13413c181f97a9c20c60d..e2a27dd9427b54e489a601bfd895f2cf84d641b4 100644 GIT binary patch literal 3467 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 literal 2627 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 diff --git a/eng/common/testproxy/localhost.conf b/eng/common/testproxy/localhost.conf index 2e03415293cc..1ed28f330c2e 100644 --- a/eng/common/testproxy/localhost.conf +++ b/eng/common/testproxy/localhost.conf @@ -1,3 +1,6 @@ +# TLS certificate configuration for the localhost leaf certificate. +# This cert is signed by the self-signed CA defined in ca.conf. + [req] prompt = no default_bits = 2048 @@ -9,15 +12,14 @@ x509_extensions = x509_ext commonName = localhost [req_ext] -basicConstraints = critical, CA:true +basicConstraints = critical, CA:false subjectAltName = @alt_names [x509_ext] -basicConstraints = critical, CA:true -keyUsage = critical, keyCertSign, cRLSign, digitalSignature,keyEncipherment +keyUsage = critical, digitalSignature, keyEncipherment extendedKeyUsage = critical, serverAuth subjectAltName = critical, @alt_names -1.3.6.1.4.1.311.84.1.1 = ASN1:UTF8String:ASP.NET Core HTTPS development certificate # Needed to get it imported by dotnet dev-certs +1.3.6.1.4.1.311.84.1.1 = ASN1:UTF8String:ASP.NET Core HTTPS development certificate # Needed to get it imported by dotnet dev-certs [alt_names] DNS.1 = localhost diff --git a/eng/common/testproxy/localhost.crt b/eng/common/testproxy/localhost.crt new file mode 100644 index 000000000000..54a4a688df1d --- /dev/null +++ b/eng/common/testproxy/localhost.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIUdURcL17NeFzznnxY1Bcx5Jl4WYIwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJQXp1cmUgU0RLMB4XDTI2MDUwNjAzMzYzMVoXDTI3MDUw +NjAzMzYzMVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA2kzmhldqDQRPbsze56o2UsTImNfh55MkbHrZkt/lASXX +zJn33M4L+rIRFrwBiG8ruI1sOCNyQcgXaayx/F5suYOjAE2qwQSxSgrILe96upDO +M5H3syVCSsd+oThlD0BnrxYYUOvY6cQCw96pthFiR4rRYv/fiMPLQmkoD2FUXHy/ +ZY6KD+oAKybKfbosh3LqPBKF6R7/Xcxt4GFwkh8rl+HTgSMRvrjAheh8HJ5k0xVF +Mv4OPUTRIvFwBlLW6oxbMEMUt3TNhvjkOyUlf5XRXNRLy7DNEw7WcwuEnzC+X2HK +ideAYhAOQJWjkaiPkaNn9vrTwitLxu0KAbHY+lhSXwIDAQABo4HAMIG9MA4GA1Ud +DwEB/wQEAwIFoDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDATAXBgNVHREBAf8EDTAL +gglsb2NhbGhvc3QwOgYKKwYBBAGCN1QBAQQsDCpBU1AuTkVUIENvcmUgSFRUUFMg +ZGV2ZWxvcG1lbnQgY2VydGlmaWNhdGUwHQYDVR0OBBYEFFOSvaEPxxURdZAsFk4j +d3WcEJ/pMB8GA1UdIwQYMBaAFJPFKH87Z11sSFO+1fFsa+l19MO1MA0GCSqGSIb3 +DQEBCwUAA4IBAQBwHqrqN83moN4PjACwFL0auY4U+3Y9x8AQ0dTMHd5Dy20461uw +5Y3ZCkGNlsqiuzeSWRZMzQ6oW8qO/Hd/yZmi7sORDOa0UVL6yQcllTivFvfPFAhf +Wc2bHrS+CJnhX8Lwk15MWoCqV5CrcK4XkPuWoRfMDkuiLYGyW94zNfv3LnKJeBh5 +NpVvXgPVmASJehUvhL5/NAnrME8RupzNbmkWP8rhpTG1M8wfBtJV3oJtlukXCPvR +fLW/zgL3XTNGqtnUX567qaSaJ7lvOsaB7CF+E14u21hYpAJ+Ex822aKF0VNGeTc3 +gIp2IQgFBxDGSiQe0ZKmaf1QgW+Yl+u5E85E +-----END CERTIFICATE----- diff --git a/eng/common/testproxy/rotate.sh b/eng/common/testproxy/rotate.sh new file mode 100755 index 000000000000..b9c2015ac2da --- /dev/null +++ b/eng/common/testproxy/rotate.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash + +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +# Generates a self-signed CA and a TLS certificate for localhost signed by that CA, +# then packages them into a PKCS#12 (PFX) file for use with the test proxy. +# +# Usage: ./rotate.sh [--days ] [--password ] +# --days Validity period in days (default: 365) +# --password Password for the PFX file (default: password, matching apply-dev-cert.sh) + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +DAYS=365 +PFX_PASSWORD="password" + +while [[ $# -gt 0 ]]; do + case "$1" in + --days) + DAYS="$2" + shift 2 + ;; + --password) + PFX_PASSWORD="$2" + shift 2 + ;; + *) + echo "Unknown argument: $1" >&2 + exit 1 + ;; + esac +done + +CA_KEY="$SCRIPT_DIR/ca.key" +# dotnet-devcert.crt is the CA public cert installed into trust stores by apply-dev-cert.sh. +CA_CERT="$SCRIPT_DIR/dotnet-devcert.crt" +CA_CONF="$SCRIPT_DIR/ca.conf" + +TLS_KEY="$SCRIPT_DIR/dotnet-devcert.key" +TLS_CSR="$SCRIPT_DIR/dotnet-devcert.csr" +TLS_CERT="$SCRIPT_DIR/localhost.crt" +TLS_PFX="$SCRIPT_DIR/dotnet-devcert.pfx" +TLS_CONF="$SCRIPT_DIR/localhost.conf" + +echo "Generating CA private key..." +openssl genrsa -out "$CA_KEY" 2048 + +echo "Generating self-signed CA certificate (${DAYS} days)..." +openssl req -new -x509 \ + -key "$CA_KEY" \ + -out "$CA_CERT" \ + -days "$DAYS" \ + -config "$CA_CONF" + +echo "Generating TLS private key..." +openssl genrsa -out "$TLS_KEY" 2048 + +echo "Generating TLS certificate signing request..." +openssl req -new \ + -key "$TLS_KEY" \ + -out "$TLS_CSR" \ + -config "$TLS_CONF" + +echo "Signing TLS certificate with CA (${DAYS} days)..." +openssl x509 -req \ + -in "$TLS_CSR" \ + -CA "$CA_CERT" \ + -CAkey "$CA_KEY" \ + -CAcreateserial \ + -out "$TLS_CERT" \ + -days "$DAYS" \ + -extfile "$TLS_CONF" \ + -extensions x509_ext + +echo "Creating PFX bundle (TLS cert + CA chain + private key)..." +openssl pkcs12 -export \ + -out "$TLS_PFX" \ + -inkey "$TLS_KEY" \ + -in "$TLS_CERT" \ + -certfile "$CA_CERT" \ + -password "pass:${PFX_PASSWORD}" + +# Remove OpenSSL serial number tracking file; it is regenerated on each run. +rm -f "$SCRIPT_DIR/ca.srl" + +echo "" +echo "Created files:" +echo " $CA_CERT (CA public cert — install this into trust stores)" +echo " $TLS_CERT" +echo " $TLS_CSR" +echo " $TLS_PFX (CA public cert + TLS public cert + TLS private key)" +echo "" +echo "Private keys (gitignored — do not commit):" +echo " $CA_KEY" +echo " $TLS_KEY" +echo "" +echo "Run apply-dev-cert.sh to install the certificates into your trust stores." From c4ad251c90bca4865eb06801402da2b5c6aca8ed Mon Sep 17 00:00:00 2001 From: Heath Stewart Date: Tue, 5 May 2026 23:32:36 -0700 Subject: [PATCH 2/2] Resolve PR feedback --- eng/common/testproxy/ca.crt | 18 ------------------ eng/common/testproxy/localhost.conf | 3 ++- eng/common/testproxy/rotate.sh | 2 +- 3 files changed, 3 insertions(+), 20 deletions(-) delete mode 100644 eng/common/testproxy/ca.crt diff --git a/eng/common/testproxy/ca.crt b/eng/common/testproxy/ca.crt deleted file mode 100644 index 2158e805d820..000000000000 --- a/eng/common/testproxy/ca.crt +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIC+DCCAeCgAwIBAgIUQSfEYlZEwuRS9elscvH2orem7+YwDQYJKoZIhvcNAQEL -BQAwFDESMBAGA1UEAwwJQXp1cmUgU0RLMB4XDTI2MDUwNjAzMjc0N1oXDTI3MDUw -NjAzMjc0N1owFDESMBAGA1UEAwwJQXp1cmUgU0RLMIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEA5m62azoHdadoISPK6k0ymBfjgNgr/uuQDgEPCsufVK9g -0buf2ewvOIU1pJCWNYHCqqLTArmyyavDhM8ASIlb4rdRkuWjLaVxvQ+RxrKgYY4p -xB09fkZOOsG9O+ztj3ssm5qW6vZY1KcL8XGCEXQX55XaCikbg2IWOOV1LQzAbgFq -QYB4m84x7tmUrfI0TNblq0BRiLKZQYvPfQasTa76tMZUEvBUCfK7auHQCUZt1ORc -yAu16BiEL3MOSi9vhoVgWFLG0Wl0w825oZcrBpH6m+KCZ5Jyo2TgebQAqADMYf37 -ha/NjvTLCZ+pBS+YTXz3lc09kInzcmG97IZlqdrQ8wIDAQABo0IwQDAPBgNVHRMB -Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUGw7rgyiywUwEpTbF -pG+/TKJW8AAwDQYJKoZIhvcNAQELBQADggEBALiizNASsF8VYEqtTunSbyu/xIea -sxJ8z0Oczk3Xn5bzy1FJmZq86hzfer5cYP14eaBuCM1rCioxA0n3ZbaTTmhQwccx -tfs4lQLrE772DJ0wxLbg8jvX7h7x9boyap5pvxv4yW54mQFgXBUjKutl36ZYLHKQ -G8ZMT34FG+g3NUMa+BNrqbYFSy5pomHyiTDxMRJAqIn/G1rGlYCRzOP6jm0oogGI -CA8+B/Cw/0XONJCoOCZV97lAwZz1EoH7QTtVqRMHbKfwKZOKjrB/LdLkhD3CnRda -PNqyXc2isGPyOlVm1HWoQGe0x6fDd/MObHpnTS4/hb3mLuOghdkUgtNDE6w= ------END CERTIFICATE----- diff --git a/eng/common/testproxy/localhost.conf b/eng/common/testproxy/localhost.conf index 1ed28f330c2e..55505e63dd8f 100644 --- a/eng/common/testproxy/localhost.conf +++ b/eng/common/testproxy/localhost.conf @@ -8,7 +8,7 @@ distinguished_name = subject req_extensions = req_ext x509_extensions = x509_ext -[ subject ] +[subject] commonName = localhost [req_ext] @@ -16,6 +16,7 @@ basicConstraints = critical, CA:false subjectAltName = @alt_names [x509_ext] +basicConstraints = critical, CA:false keyUsage = critical, digitalSignature, keyEncipherment extendedKeyUsage = critical, serverAuth subjectAltName = critical, @alt_names diff --git a/eng/common/testproxy/rotate.sh b/eng/common/testproxy/rotate.sh index b9c2015ac2da..2bc1b6c86a4b 100755 --- a/eng/common/testproxy/rotate.sh +++ b/eng/common/testproxy/rotate.sh @@ -84,7 +84,7 @@ openssl pkcs12 -export \ -password "pass:${PFX_PASSWORD}" # Remove OpenSSL serial number tracking file; it is regenerated on each run. -rm -f "$SCRIPT_DIR/ca.srl" +rm -f "$SCRIPT_DIR/dotnet-devcert.srl" echo "" echo "Created files:"