Skip to content

Commit

Permalink
add basic support for pkcs12 import (microsoft#1351)
Browse files Browse the repository at this point in the history
  • Loading branch information
wfurt authored Mar 24, 2021
1 parent c7aacdc commit a1644f9
Show file tree
Hide file tree
Showing 8 changed files with 291 additions and 10 deletions.
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,11 @@ if(WIN32)
list(APPEND QUIC_COMMON_DEFINES QUIC_DISABLE_RESUMPTION)
endif()

if(QUIC_TLS STREQUAL "schannel" OR QUIC_TLS STREQUAL "mitls")
# Disable until CAPI support is fixed
list(APPEND QUIC_COMMON_DEFINES QUIC_DISABLE_PFX_TESTS)
endif()

if(QUIC_TLS STREQUAL "openssl" OR QUIC_TLS STREQUAL "schannel")
# OpenSSL and Schannel don't support 0-RTT yet.
message(STATUS "Disabling 0-RTT support")
Expand All @@ -275,6 +280,7 @@ if(WIN32)
if(QUIC_TLS STREQUAL "stub")
list(APPEND QUIC_COMMON_DEFINES QUIC_TLS_STUB)
list(APPEND QUIC_COMMON_DEFINES QUIC_DISABLE_TICKET_KEY_TESTS)
list(APPEND QUIC_COMMON_DEFINES QUIC_DISABLE_PFX_TESTS)
endif()

if(QUIC_ENABLE_SANITIZERS)
Expand Down Expand Up @@ -394,6 +400,7 @@ else()
if(QUIC_TLS STREQUAL "stub")
list(APPEND QUIC_COMMON_DEFINES QUIC_TLS_STUB)
list(APPEND QUIC_COMMON_DEFINES QUIC_DISABLE_TICKET_KEY_TESTS)
list(APPEND QUIC_COMMON_DEFINES QUIC_DISABLE_PFX_TESTS)
endif()

set(QUIC_C_FLAGS ${QUIC_COMMON_FLAGS})
Expand Down
66 changes: 66 additions & 0 deletions scripts/install-test-certificates.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<#
.SYNOPSIS
This script provides helpers to generate test certificate for MsQuic tests.
.PARAMETER OutputFile
Specifies the build configuration to test.
.EXAMPLE
install-test-certificates.ps1 -OutputFile ./artifacts/bin/macos/x64_Debug_openssl/test.pfx
#>

param (
[Parameter(Mandatory = $true)]
[string]$OutputFile = ""
)

$Subject = [X500DistinguishedName]::new("CN=localhost")
[System.DateTimeOffset]$NotBefore = [System.DateTimeOffset]::Now.AddDays(-1)
[System.DateTimeOffset]$NotAfter = [System.DateTimeOffset]::Now.AddDays(365)

# EKU
$EkuOidCollection = [System.Security.Cryptography.OidCollection]::new()
$EkuOidCollection.Add([System.Security.Cryptography.Oid]::new("1.3.6.1.5.5.7.3.1", "Server Authentication"))
$EnhancedKeyUsages = [System.Security.Cryptography.X509Certificates.X509EnhancedKeyUsageExtension]::new($EkuOidCollection, <# critical #> $false)

# Create Basic Constraints
$BasicConstraints = [System.Security.Cryptography.X509Certificates.X509BasicConstraintsExtension]::new(
<# certificateAuthority #> $false,
<# hasPathLengthConstraint #> $false,
<# pathLengthConstraint #> 0,
<# critical #> $false)

$Extensions = [System.Collections.Generic.List[System.Security.Cryptography.X509Certificates.X509Extension]]::new()
$Extensions.Add($EnhancedKeyUsages)
$Extensions.Add($BasicConstraints)

$PrivateKey = [System.Security.Cryptography.RSA]::Create(2048)

# Create Certificate Request
$CertRequest = [System.Security.Cryptography.X509Certificates.CertificateRequest]::new(
$Subject,
$PrivateKey,
[System.Security.Cryptography.HashAlgorithmName]::SHA256,
[System.Security.Cryptography.RSASignaturePadding]::Pkcs1)

# Create the Subject Key Identifier extension
$SubjectKeyIdentifier = [System.Security.Cryptography.X509Certificates.X509SubjectKeyIdentifierExtension]::new(
$CertRequest.PublicKey,
<# critical #> $false)
$Extensions.Add($SubjectKeyIdentifier)

foreach ($Extension in $Extensions)
{
$CertRequest.CertificateExtensions.Add($Extension)
}

$CertificateWithKey = $CertRequest.CreateSelfSigned($NotBefore, $NotAfter)

$Pfx = $CertificateWithKey.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pfx, "PLACEHOLDER");

Set-Content $OutputFile -Value $Pfx -AsByteStream

Write-Output "Generated $OutputFile"

10 changes: 9 additions & 1 deletion scripts/run-gtest.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ param (
[Parameter(Mandatory = $false)]
[switch]$CodeCoverage = $false,

[Parameter(Mandatory = $false)]
[String]$PfxPath = "",

[Parameter(Mandatory = $false)]
[switch]$AZP = $false
)
Expand Down Expand Up @@ -335,6 +338,9 @@ function Start-TestCase([String]$Name) {
if ($Kernel -ne "") {
$Arguments += " --kernelPriv"
}
if ($PfxPath -ne "") {
$Arguments += " -PfxPath $PfxPath"
}

# Start the test process and return some information about the test case.
[pscustomobject]@{
Expand Down Expand Up @@ -369,7 +375,9 @@ function Start-AllTestCases {
if ($Kernel -ne "") {
$Arguments += " --kernelPriv"
}

if ($PfxPath -ne "") {
$Arguments += " -PfxPath $PfxPath"
}
# Start the test process and return some information about the test case.
[pscustomobject]@{
Name = $Name
Expand Down
10 changes: 9 additions & 1 deletion scripts/test.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,16 @@ if ($Kernel) {
}
}

$PfxFile = Join-Path $RootArtifactDir "selfsignedservercert.pfx"
if (!(Test-Path $PfxFile)) {
$MyPath = Split-Path -Path $PSCommandPath -Parent
$ScriptPath = Join-Path $MyPath install-test-certificates.ps1

&$ScriptPath -OutputFile $PfxFile
}

# Build up all the arguments to pass to the Powershell script.
$TestArguments = "-ExecutionMode $ExecutionMode -IsolationMode $IsolationMode"
$TestArguments = "-ExecutionMode $ExecutionMode -IsolationMode $IsolationMode -PfxPath $PfxFile"

if ($Kernel) {
$TestArguments += " -Kernel $KernelPath"
Expand Down
8 changes: 8 additions & 0 deletions src/inc/msquic.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ typedef enum QUIC_CREDENTIAL_TYPE {
QUIC_CREDENTIAL_TYPE_CERTIFICATE_CONTEXT,
QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE,
QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE_PROTECTED,
QUIC_CREDENTIAL_TYPE_CERTIFICATE_PKCS12,
} QUIC_CREDENTIAL_TYPE;

typedef enum QUIC_CREDENTIAL_FLAGS {
Expand Down Expand Up @@ -246,6 +247,12 @@ typedef struct QUIC_CERTIFICATE_FILE_PROTECTED {
const char *PrivateKeyPassword;
} QUIC_CERTIFICATE_FILE_PROTECTED;

typedef struct QUIC_CERTIFICATE_PKCS12 {
const uint8_t *Asn1Blob;
uint32_t Asn1BlobLength;
const char *PrivateKeyPassword; // Optional: used if provided. Ignored if NULL
} QUIC_CERTIFICATE_PKCS12;

typedef void QUIC_CERTIFICATE; // Platform specific certificate context object

typedef struct QUIC_CREDENTIAL_CONFIG {
Expand All @@ -257,6 +264,7 @@ typedef struct QUIC_CREDENTIAL_CONFIG {
QUIC_CERTIFICATE* CertificateContext;
QUIC_CERTIFICATE_FILE* CertificateFile;
QUIC_CERTIFICATE_FILE_PROTECTED* CertificateFileProtected;
QUIC_CERTIFICATE_PKCS12* CertificatePkcs12;
};
const char* Principal;
void* Reserved; // Currently unused
Expand Down
101 changes: 96 additions & 5 deletions src/platform/tls_openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
#pragma warning(push)
#pragma warning(disable:4100) // Unreferenced parameter errcode in inline function
#endif
#include "openssl/bio.h"
#include "openssl/err.h"
#include "openssl/hmac.h"
#include "openssl/kdf.h"
#include "openssl/pem.h"
#include "openssl/pkcs12.h"
#include "openssl/rsa.h"
#include "openssl/ssl.h"
#include "openssl/x509.h"
Expand Down Expand Up @@ -620,6 +622,12 @@ CxPlatTlsSecConfigCreate(
CredConfig->CertificateFileProtected->PrivateKeyPassword == NULL) {
return QUIC_STATUS_INVALID_PARAMETER;
}
} else if(CredConfig->Type == QUIC_CREDENTIAL_TYPE_CERTIFICATE_PKCS12) {
if (CredConfig->CertificatePkcs12 == NULL ||
CredConfig->CertificatePkcs12->Asn1Blob == NULL ||
CredConfig->CertificatePkcs12->Asn1BlobLength == 0) {
return QUIC_STATUS_INVALID_PARAMETER;
}
} else if (CredConfig->Type == QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH ||
CredConfig->Type == QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH_STORE ||
CredConfig->Type == QUIC_CREDENTIAL_TYPE_CERTIFICATE_CONTEXT) { // NOLINT bugprone-branch-clone
Expand All @@ -637,6 +645,7 @@ CxPlatTlsSecConfigCreate(
CXPLAT_SEC_CONFIG* SecurityConfig = NULL;
RSA* RsaKey = NULL;
X509* X509Cert = NULL;
EVP_PKEY * PrivateKey = NULL;

//
// Create a security config.
Expand Down Expand Up @@ -806,13 +815,14 @@ CxPlatTlsSecConfigCreate(
// Set the server certs.
//

if (CredConfig->Type == QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE_PROTECTED) {
SSL_CTX_set_default_passwd_cb_userdata(
SecurityConfig->SSLCtx, (void*)CredConfig->CertificateFileProtected->PrivateKeyPassword);
}

if (CredConfig->Type == QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE ||
CredConfig->Type == QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE_PROTECTED) {

if (CredConfig->Type == QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE_PROTECTED) {
SSL_CTX_set_default_passwd_cb_userdata(
SecurityConfig->SSLCtx, (void*)CredConfig->CertificateFileProtected->PrivateKeyPassword);
}

Ret =
SSL_CTX_use_PrivateKey_file(
SecurityConfig->SSLCtx,
Expand Down Expand Up @@ -841,6 +851,83 @@ CxPlatTlsSecConfigCreate(
Status = QUIC_STATUS_TLS_ERROR;
goto Exit;
}
} else if (CredConfig->Type == QUIC_CREDENTIAL_TYPE_CERTIFICATE_PKCS12) {
BIO* Bio = BIO_new(BIO_s_mem());
PKCS12 *Pkcs12 = NULL;

if (!Bio) {
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
ERR_get_error(),
"BIO_new failed");
Status = QUIC_STATUS_TLS_ERROR;
goto Exit;
}

BIO_set_mem_eof_return(Bio, 0);
BIO_write(Bio, CredConfig->CertificatePkcs12->Asn1Blob, CredConfig->CertificatePkcs12->Asn1BlobLength);
Pkcs12 = d2i_PKCS12_bio(Bio, NULL);
BIO_free(Bio);
Bio = NULL;

if (!Pkcs12) {
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
ERR_get_error(),
"d2i_PKCS12_bio failed");
Status = QUIC_STATUS_TLS_ERROR;
goto Exit;
}

STACK_OF(X509) *Ca = NULL;
Ret =
PKCS12_parse(Pkcs12, CredConfig->CertificatePkcs12->PrivateKeyPassword, &PrivateKey, &X509Cert, &Ca);
if (Ca) {
sk_X509_pop_free(Ca, X509_free); // no handling for custom certificate chains yet.
}
if (Pkcs12) {
PKCS12_free(Pkcs12);
}

if (Ret != 1) {
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
ERR_get_error(),
"PKCS12_parse failed");
Status = QUIC_STATUS_TLS_ERROR;
goto Exit;
}

Ret =
SSL_CTX_use_PrivateKey(
SecurityConfig->SSLCtx,
PrivateKey);
if (Ret != 1) {
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
ERR_get_error(),
"SSL_CTX_use_PrivateKey_file failed");
Status = QUIC_STATUS_TLS_ERROR;
goto Exit;
}

Ret =
SSL_CTX_use_certificate(
SecurityConfig->SSLCtx,
X509Cert);
if (Ret != 1) {
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
ERR_get_error(),
"SSL_CTX_use_certificate failed");
Status = QUIC_STATUS_TLS_ERROR;
goto Exit;
}
} else {
Status =
CxPlatTlsExtractPrivateKey(
Expand Down Expand Up @@ -922,6 +1009,10 @@ CxPlatTlsSecConfigCreate(
RSA_free(RsaKey);
}

if (PrivateKey != NULL) {
EVP_PKEY_free(PrivateKey);
}

return Status;
}

Expand Down
Loading

0 comments on commit a1644f9

Please sign in to comment.