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/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 93a5617aea60..e2a27dd9427b 100644 Binary files a/eng/common/testproxy/dotnet-devcert.pfx and b/eng/common/testproxy/dotnet-devcert.pfx differ diff --git a/eng/common/testproxy/localhost.conf b/eng/common/testproxy/localhost.conf index 2e03415293cc..55505e63dd8f 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 @@ -5,19 +8,19 @@ distinguished_name = subject req_extensions = req_ext x509_extensions = x509_ext -[ subject ] +[subject] 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 +basicConstraints = critical, CA:false +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..2bc1b6c86a4b --- /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/dotnet-devcert.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."