More SecurityContext calls accept a password.

Any PKCS12 container, not just those containing a private key, can be
protected by a password. This change adds an optional named `password`
parameter to other SecurityContext calls, and plumbs it through to
the calls reading PKCS12 data.

R=whesse@google.com

Review URL: https://codereview.chromium.org/1699163002 .
This commit is contained in:
Zachary Anderson 2016-02-17 10:58:19 -08:00
parent db588ec441
commit 38786e9566
15 changed files with 250 additions and 145 deletions

View file

@ -17,6 +17,10 @@
The method now only supports one argument for the PEM file name containing
the trusted certificates.
* Added support to SecurityContext for PKCS12 certificate and key containers.
* All calls in `SecurityContext` that accept certificate data now accept an
optional named parameter `password`, similar to
`SecurityContext.usePrivateKeyBytes`, for use as the password for PKCS12
data.
* `dart:async`
* Made `StreamView` class a `const` class.

View file

@ -109,10 +109,10 @@ namespace bin {
V(SecurityContext_Allocate, 1) \
V(SecurityContext_UsePrivateKeyBytes, 3) \
V(SecurityContext_SetAlpnProtocols, 3) \
V(SecurityContext_SetClientAuthoritiesBytes, 2) \
V(SecurityContext_SetTrustedCertificatesBytes, 2) \
V(SecurityContext_SetClientAuthoritiesBytes, 3) \
V(SecurityContext_SetTrustedCertificatesBytes, 3) \
V(SecurityContext_TrustBuiltinRoots, 1) \
V(SecurityContext_UseCertificateChainBytes, 2) \
V(SecurityContext_UseCertificateChainBytes, 3) \
V(ServerSocket_Accept, 2) \
V(ServerSocket_CreateBindListen, 6) \
V(Socket_CreateConnect, 3) \

View file

@ -282,7 +282,9 @@ void FUNCTION_NAME(SecureSocket_FilterPointer)(Dart_NativeArguments args) {
static Dart_Handle WrappedX509Certificate(X509* certificate) {
if (certificate == NULL) return Dart_Null();
if (certificate == NULL) {
return Dart_Null();
}
Dart_Handle x509_type =
DartUtils::GetDartType(DartUtils::kIOLibURL, "X509Certificate");
if (Dart_IsError(x509_type)) {
@ -307,7 +309,9 @@ static Dart_Handle WrappedX509Certificate(X509* certificate) {
int CertificateCallback(int preverify_ok, X509_STORE_CTX* store_ctx) {
if (preverify_ok == 1) return 1;
if (preverify_ok == 1) {
return 1;
}
Dart_Isolate isolate = Dart_CurrentIsolate();
if (isolate == NULL) {
FATAL("CertificateCallback called with no current isolate\n");
@ -319,7 +323,9 @@ int CertificateCallback(int preverify_ok, X509_STORE_CTX* store_ctx) {
SSLFilter* filter = static_cast<SSLFilter*>(
SSL_get_ex_data(ssl, SSLFilter::filter_ssl_index));
Dart_Handle callback = filter->bad_certificate_callback();
if (Dart_IsNull(callback)) return 0;
if (Dart_IsNull(callback)) {
return 0;
}
Dart_Handle args[1];
args[0] = WrappedX509Certificate(certificate);
if (Dart_IsError(args[0])) {
@ -360,12 +366,12 @@ int PasswordCallback(char* buf, int size, int rwflag, void* userdata) {
}
void CheckStatus(int status,
const char* type,
const char* message) {
void CheckStatus(int status, const char* type, const char* message) {
// TODO(24183): Take appropriate action on failed calls,
// throw exception that includes all messages from the error stack.
if (status == 1) return;
if (status == 1) {
return;
}
if (SSL_LOG_STATUS) {
int error = ERR_get_error();
Log::PrintErr("Failed: %s status %d", message, status);
@ -556,25 +562,31 @@ static EVP_PKEY* GetPrivateKey(BIO* bio, const char* password) {
}
void FUNCTION_NAME(SecurityContext_UsePrivateKeyBytes)(
Dart_NativeArguments args) {
SSL_CTX* context = GetSecurityContext(args);
Dart_Handle password_object = ThrowIfError(Dart_GetNativeArgument(args, 2));
static const char* GetPasswordArgument(Dart_NativeArguments args,
intptr_t index) {
Dart_Handle password_object =
ThrowIfError(Dart_GetNativeArgument(args, index));
const char* password = NULL;
if (Dart_IsString(password_object)) {
ThrowIfError(Dart_StringToCString(password_object, &password));
if (strlen(password) > PEM_BUFSIZE - 1) {
Dart_ThrowException(DartUtils::NewDartArgumentError(
"SecurityContext.usePrivateKey password length is greater than"
" 1023 (PEM_BUFSIZE)"));
"Password length is greater than 1023 (PEM_BUFSIZE)"));
}
} else if (Dart_IsNull(password_object)) {
password = "";
} else {
Dart_ThrowException(DartUtils::NewDartArgumentError(
"SecurityContext.usePrivateKey password is not a String or null"));
"Password is not a String or null"));
}
return password;
}
void FUNCTION_NAME(SecurityContext_UsePrivateKeyBytes)(
Dart_NativeArguments args) {
SSL_CTX* context = GetSecurityContext(args);
const char* password = GetPasswordArgument(args, 2);
int status;
{
@ -590,7 +602,9 @@ void FUNCTION_NAME(SecurityContext_UsePrivateKeyBytes)(
}
static int SetTrustedCertificatesBytesPKCS12(SSL_CTX* context, BIO* bio) {
static int SetTrustedCertificatesBytesPKCS12(SSL_CTX* context,
BIO* bio,
const char* password) {
ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL));
if (p12.get() == NULL) {
return NULL;
@ -599,22 +613,12 @@ static int SetTrustedCertificatesBytesPKCS12(SSL_CTX* context, BIO* bio) {
EVP_PKEY* key = NULL;
X509 *cert = NULL;
STACK_OF(X509) *ca_certs = NULL;
// There should be no private keys in this file, so we hardcode the password
// to "".
// TODO(zra): Allow passing a password anyway.
int status = PKCS12_parse(p12.get(), "", &key, &cert, &ca_certs);
int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs);
if (status == 0) {
return status;
}
ScopedX509Stack cert_stack(ca_certs);
// There should be no private key.
if (key != NULL) {
X509_free(cert);
return 0;
}
X509_STORE* store = SSL_CTX_get_cert_store(context);
status = X509_STORE_add_cert(store, cert);
if (status == 0) {
@ -662,12 +666,14 @@ static int SetTrustedCertificatesBytesPEM(SSL_CTX* context, BIO* bio) {
}
static int SetTrustedCertificatesBytes(SSL_CTX* context, BIO* bio) {
static int SetTrustedCertificatesBytes(SSL_CTX* context,
BIO* bio,
const char* password) {
int status = SetTrustedCertificatesBytesPEM(context, bio);
if (TryPKCS12(status != 0)) {
ERR_clear_error();
BIO_reset(bio);
status = SetTrustedCertificatesBytesPKCS12(context, bio);
status = SetTrustedCertificatesBytesPKCS12(context, bio, password);
} else if (status != 0) {
// The PEM file was successfully parsed.
ERR_clear_error();
@ -679,10 +685,11 @@ static int SetTrustedCertificatesBytes(SSL_CTX* context, BIO* bio) {
void FUNCTION_NAME(SecurityContext_SetTrustedCertificatesBytes)(
Dart_NativeArguments args) {
SSL_CTX* context = GetSecurityContext(args);
const char* password = GetPasswordArgument(args, 2);
int status;
{
ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1)));
status = SetTrustedCertificatesBytes(context, bio.bio());
status = SetTrustedCertificatesBytes(context, bio.bio(), password);
}
CheckStatus(status,
"TlsException",
@ -708,7 +715,9 @@ void FUNCTION_NAME(SecurityContext_TrustBuiltinRoots)(
}
static int UseChainBytesPKCS12(SSL_CTX* context, BIO* bio) {
static int UseChainBytesPKCS12(SSL_CTX* context,
BIO* bio,
const char* password) {
ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL));
if (p12.get() == NULL) {
return NULL;
@ -717,22 +726,13 @@ static int UseChainBytesPKCS12(SSL_CTX* context, BIO* bio) {
EVP_PKEY* key = NULL;
X509 *cert = NULL;
STACK_OF(X509) *ca_certs = NULL;
// There should be no private keys in this file, so we hardcode the password
// to "".
// TODO(zra): Allow passing a password anyway.
int status = PKCS12_parse(p12.get(), "", &key, &cert, &ca_certs);
int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs);
if (status == 0) {
return status;
}
ScopedX509 x509(cert);
ScopedX509Stack certs(ca_certs);
// There should be no private key.
if (key != NULL) {
return 0;
}
status = SSL_CTX_use_certificate(context, x509.get());
if (ERR_peek_error() != 0) {
// Key/certificate mismatch doesn't imply status is 0.
@ -801,12 +801,12 @@ static int UseChainBytesPEM(SSL_CTX* context, BIO* bio) {
}
static int UseChainBytes(SSL_CTX* context, BIO* bio) {
static int UseChainBytes(SSL_CTX* context, BIO* bio, const char* password) {
int status = UseChainBytesPEM(context, bio);
if (TryPKCS12(status != 0)) {
ERR_clear_error();
BIO_reset(bio);
status = UseChainBytesPKCS12(context, bio);
status = UseChainBytesPKCS12(context, bio, password);
} else if (status != 0) {
// The PEM file was successfully read.
ERR_clear_error();
@ -818,10 +818,11 @@ static int UseChainBytes(SSL_CTX* context, BIO* bio) {
void FUNCTION_NAME(SecurityContext_UseCertificateChainBytes)(
Dart_NativeArguments args) {
SSL_CTX* context = GetSecurityContext(args);
const char* password = GetPasswordArgument(args, 2);
int status;
{
ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1)));
status = UseChainBytes(context, bio.bio());
status = UseChainBytes(context, bio.bio(), password);
}
CheckStatus(status,
"TlsException",
@ -829,7 +830,8 @@ void FUNCTION_NAME(SecurityContext_UseCertificateChainBytes)(
}
static STACK_OF(X509_NAME)* GetCertificateNamesPKCS12(BIO* bio) {
static STACK_OF(X509_NAME)* GetCertificateNamesPKCS12(BIO* bio,
const char* password) {
ScopedPKCS12 p12(d2i_PKCS12_bio(bio, NULL));
if (p12.get() == NULL) {
return NULL;
@ -843,22 +845,13 @@ static STACK_OF(X509_NAME)* GetCertificateNamesPKCS12(BIO* bio) {
EVP_PKEY* key = NULL;
X509 *cert = NULL;
STACK_OF(X509) *ca_certs = NULL;
// There should be no private keys in this file, so we hardcode the password
// to "".
// TODO(zra): Allow passing a password anyway.
int status = PKCS12_parse(p12.get(), "", &key, &cert, &ca_certs);
int status = PKCS12_parse(p12.get(), password, &key, &cert, &ca_certs);
if (status == 0) {
return NULL;
}
ScopedX509 x509(cert);
ScopedX509Stack certs(ca_certs);
// There should be no private key.
if (key != NULL) {
return NULL;
}
X509_NAME* x509_name = X509_get_subject_name(x509.get());
if (x509_name == NULL) {
return NULL;
@ -935,12 +928,13 @@ static STACK_OF(X509_NAME)* GetCertificateNamesPEM(BIO* bio) {
}
static STACK_OF(X509_NAME)* GetCertificateNames(BIO* bio) {
static STACK_OF(X509_NAME)* GetCertificateNames(BIO* bio,
const char* password) {
STACK_OF(X509_NAME)* result = GetCertificateNamesPEM(bio);
if (TryPKCS12(result != NULL)) {
ERR_clear_error();
BIO_reset(bio);
result = GetCertificateNamesPKCS12(bio);
result = GetCertificateNamesPKCS12(bio, password);
} else if (result != NULL) {
// The PEM file was successfully parsed.
ERR_clear_error();
@ -952,11 +946,12 @@ static STACK_OF(X509_NAME)* GetCertificateNames(BIO* bio) {
void FUNCTION_NAME(SecurityContext_SetClientAuthoritiesBytes)(
Dart_NativeArguments args) {
SSL_CTX* context = GetSecurityContext(args);
const char* password = GetPasswordArgument(args, 2);
STACK_OF(X509_NAME)* certificate_names;
{
ScopedMemBIO bio(ThrowIfError(Dart_GetNativeArgument(args, 1)));
certificate_names = GetCertificateNames(bio.bio());
certificate_names = GetCertificateNames(bio.bio(), password);
}
if (certificate_names != NULL) {

View file

@ -147,38 +147,38 @@ class _SecurityContext
void usePrivateKeyBytes(List<int> keyBytes, {String password})
native "SecurityContext_UsePrivateKeyBytes";
void setTrustedCertificates(String file) {
setTrustedCertificatesSync(file);
void setTrustedCertificates(String file, {String password}) {
setTrustedCertificatesSync(file, password: password);
}
void setTrustedCertificatesSync(String file) {
void setTrustedCertificatesSync(String file, {String password}) {
List<int> bytes = (new File(file)).readAsBytesSync();
setTrustedCertificatesBytes(bytes);
setTrustedCertificatesBytes(bytes, password: password);
}
void setTrustedCertificatesBytes(List<int> certBytes)
void setTrustedCertificatesBytes(List<int> certBytes, {String password})
native "SecurityContext_SetTrustedCertificatesBytes";
void useCertificateChain({String file, String directory}) {
void useCertificateChain({String file, String directory, String password}) {
if (directory != null) {
throw new UnsupportedError(
"The directory argument to useCertificateChain is not supported.");
}
useCertificateChainSync(file);
useCertificateChainSync(file, password: password);
}
void useCertificateChainSync(String chainFile) {
void useCertificateChainSync(String chainFile, {String password}) {
List<int> bytes = (new File(chainFile)).readAsBytesSync();
useCertificateChainBytes(bytes);
useCertificateChainBytes(bytes, password: password);
}
void useCertificateChainBytes(List<int> chainBytes)
void useCertificateChainBytes(List<int> chainBytes, {String password})
native "SecurityContext_UseCertificateChainBytes";
void setClientAuthorities(String file) {
setClientAuthoritiesSync(file);
void setClientAuthorities(String file, {String password}) {
setClientAuthoritiesSync(file, password: password);
}
void setClientAuthoritiesSync(String file) {
void setClientAuthoritiesSync(String file, {String password}) {
List<int> bytes = (new File(file)).readAsBytesSync();
setClientAuthoritiesBytes(bytes);
setClientAuthoritiesBytes(bytes, password: password);
}
void setClientAuthoritiesBytes(List<int> authCertBytes)
void setClientAuthoritiesBytes(List<int> authCertBytes, {String password})
native "SecurityContext_SetClientAuthoritiesBytes";
void setAlpnProtocols(List<String> protocols, bool isServer) {
Uint8List encodedProtocols =

View file

@ -12,12 +12,8 @@ part of dart.io;
* The [SecureSocket] and [SecureServer] classes take a SecurityContext
* as an argument to their connect and bind methods.
*
* Certificates and keys can be added to a SecurityContext from PEM files
* on the disk. A PEM file contains one or more base-64 encoded DER-serialized
* ASN1 objects, surrounded with delimiter strings like
* "-----BEGIN CERTIFICATE -----" and "-----END CERTIFICATE-----".
* Distinguished encoding rules (DER) is a canonical binary serialization
* of ASN1 objects into an octet string.
* Certificates and keys can be added to a SecurityContext from either PEM
* or PKCS12 containers.
*
* [usePrivateKey], [setTrustedCertificates], [useCertificateChain], and
* [setClientAuthorities] are deprecated. They have been renamed
@ -46,7 +42,7 @@ abstract class SecurityContext {
*
* A secure connection using this SecurityContext will use this key with
* the server or client certificate to sign and decrypt messages.
* [keyFile] is a PEM or PKCS12 file containing an encrypted
* [keyFile] is the path to a PEM or PKCS12 file containing an encrypted
* private key, encrypted with [password]. An unencrypted file can be
* used, but this is not usual.
*/
@ -71,18 +67,18 @@ abstract class SecurityContext {
* client connections, when connecting to a secure server.
*
* [file] is the path to a PEM or PKCS12 file containing X509 certificates,
* usually root certificates from certificate authorities. When using a
* PKCS12 file, it should not contain a private key, and the password should
* be the empty string.
* usually root certificates from certificate authorities. For PKCS12 files,
* [password] is the password for the file. For PEM files, [password] is
* ignored.
*/
void setTrustedCertificatesSync(String file);
void setTrustedCertificatesSync(String file, {String password});
/**
* [setTrustedCertificates] is deprecated. Use [setTrustedCertificatesSync]
* or [setTrustedCertificatesBytes].
*/
@deprecated
void setTrustedCertificates(String file);
void setTrustedCertificates(String file, {String password});
/**
* Sets the set of trusted X509 certificates used by [SecureSocket]
@ -90,7 +86,7 @@ abstract class SecurityContext {
*
* Like [setTrustedCertificatesSync] but takes the contents of the file.
*/
void setTrustedCertificatesBytes(List<int> certBytes);
void setTrustedCertificatesBytes(List<int> certBytes,{String password});
/**
* Sets the chain of X509 certificates served by [SecureServer]
@ -99,18 +95,18 @@ abstract class SecurityContext {
* [file] is a PEM or PKCS12 file containing X509 certificates, starting with
* the root authority and intermediate authorities forming the signed
* chain to the server certificate, and ending with the server certificate.
* The private key for the server certificate is set by [usePrivateKey]. When
* using a PKCS12 file, it should not contain a private key, and the password
* should be the empty string.
* The private key for the server certificate is set by [usePrivateKey]. For
* PKCS12 files, [password] is the password for the file. For PEM files,
* [password] is ignored.
*/
void useCertificateChainSync(String file);
void useCertificateChainSync(String file, {String password});
/**
* [useCertificateChain] is deprecated. Use [useCertificateChainSync]
* or [useCertificateChainBytes].
*/
@deprecated
void useCertificateChain({String file, String directory});
void useCertificateChain({String file, String directory, String password});
/**
* Sets the chain of X509 certificates served by [SecureServer]
@ -118,7 +114,7 @@ abstract class SecurityContext {
*
* Like [useCertificateChainSync] but takes the contents of the file.
*/
void useCertificateChainBytes(List<int> chainBytes);
void useCertificateChainBytes(List<int> chainBytes, {String password});
/**
* Sets the list of authority names that a [SecureServer] will advertise
@ -127,17 +123,17 @@ abstract class SecurityContext {
*
* [file] is a PEM or PKCS12 file containing the accepted signing
* authority certificates - the authority names are extracted from the
* certificates. When using a PKCS12 file, it should not contain a private
* key, and the password should be the empty string.
* certificates. For PKCS12 files, [password] is the password for the file.
* For PEM files, [password] is ignored.
*/
void setClientAuthoritiesSync(String file);
void setClientAuthoritiesSync(String file, {String password});
/**
* [setClientAuthorities] is deprecated. Use [setClientAuthoritiesSync]
* or [setClientAuthoritiesBytes].
*/
@deprecated
void setClientAuthorities(String file);
void setClientAuthorities(String file, {String password});
/**
* Sets the list of authority names that a [SecureServer] will advertise
@ -146,7 +142,7 @@ abstract class SecurityContext {
*
* Like [setClientAuthoritySync] but takes the contents of the file.
*/
void setClientAuthoritiesBytes(List<int> authCertBytes);
void setClientAuthoritiesBytes(List<int> authCertBytes, {String password});
/**
* Sets the list of application-level protocols supported by a client

View file

@ -8,6 +8,8 @@ The certificates are created by running ../create_sample_certificates.sh
in a bash or sh shell, with the openssl tools installed. Run the script
twice to create the untrusted_* files.
PEM files:
server_chain.pem:
Contains the chain of certificates, from the self-signed
test certificate authority, through the intermediate CA, to the server
@ -29,6 +31,40 @@ certificate, used on the server side of a test connection that is intended
to fail because the client does not accept this certificate authority
untrusted_server_key.pem:
Contains the private key for the untrusted server certificate
Contains the private key for the untrusted server certificate
in untrusted_server_chain.pem
*_malformed.pem:
Truncated PEM formatted certificates used to test error handling.
PKCS12 files:
server_key.12:
Created with:
$ openssl pkcs12 -export -inkey server_key.pem -out server_key.p12 -nocerts
with password 'dartdart'
server_chain.p12:
Created with:
$ openssl pkcs12 -export -in server_chain.pem -out server_chain.p12 -nokeys
with password 'dartdart'
client1_key.p12:
Created with:
$ openssl pkcs12 -export -inkey client1_key.pem -out client1_key.p12 -nocerts
with password 'dartdart'
client1.p12:
Created with:
$ openssl pkcs12 -export -in client1.pem -out client1.p12 -nokeys
with password 'dartdart'
trusted_certs.p12:
Created with:
$ openssl pkcs12 -export -in trusted_certs.pem -out trusted_certs.p12 -nokeys
with password 'dartdart'
client_authority.p12:
Created with:
$ openssl pkcs12 -export -in client_authority.pem -out client_authority.p12 -nokeys
with password 'dartdart'

View file

@ -12,32 +12,40 @@ InternetAddress HOST;
String localFile(path) => Platform.script.resolve(path).toFilePath();
SecurityContext serverContext(String certType) => new SecurityContext()
..useCertificateChainSync(localFile('certificates/server_chain.$certType'))
..usePrivateKeySync(localFile('certificates/server_key.$certType'),
password: 'dartdart')
SecurityContext serverContext(String certType, String password) =>
new SecurityContext()
..useCertificateChainSync(
localFile('certificates/server_chain.$certType'), password: password)
..usePrivateKeySync(
localFile('certificates/server_key.$certType'), password: password)
..setTrustedCertificatesSync(localFile(
'certificates/client_authority.$certType'))
'certificates/client_authority.$certType'), password: password)
..setClientAuthoritiesSync(localFile(
'certificates/client_authority.$certType'));
'certificates/client_authority.$certType'), password: password);
SecurityContext clientCertContext(String certType) => new SecurityContext()
..setTrustedCertificatesSync(localFile(
'certificates/trusted_certs.$certType'))
..useCertificateChainSync(localFile('certificates/client1.$certType'))
..usePrivateKeySync(localFile('certificates/client1_key.$certType'),
password: 'dartdart');
SecurityContext clientCertContext(String certType, String password) =>
new SecurityContext()
..setTrustedCertificatesSync(
localFile('certificates/trusted_certs.$certType'), password: password)
..useCertificateChainSync(
localFile('certificates/client1.$certType'), password: password)
..usePrivateKeySync(
localFile('certificates/client1_key.$certType'), password: password);
SecurityContext clientNoCertContext(String certType) => new SecurityContext()
SecurityContext clientNoCertContext(String certType, String password) =>
new SecurityContext()
..setTrustedCertificatesSync(localFile(
'certificates/trusted_certs.$certType'));
'certificates/trusted_certs.$certType'), password: password);
Future testClientCertificate(
{bool required, bool sendCert, String certType}) async {
var server = await SecureServerSocket.bind(HOST, 0, serverContext(certType),
requestClientCertificate: true, requireClientCertificate: required);
var clientContext =
sendCert ? clientCertContext(certType) : clientNoCertContext(certType);
{bool required, bool sendCert, String certType, String password}) async {
var server = await SecureServerSocket.bind(HOST, 0,
serverContext(certType, password),
requestClientCertificate: true,
requireClientCertificate: required);
var clientContext = sendCert ?
clientCertContext(certType, password) :
clientNoCertContext(certType, password);
var clientEndFuture =
SecureSocket.connect(HOST, server.port, context: clientContext);
if (required && !sendCert) {
@ -74,16 +82,22 @@ Future testClientCertificate(
main() async {
asyncStart();
HOST = (await InternetAddress.lookup("localhost")).first;
await testClientCertificate(required: false, sendCert: true, certType: 'pem');
await testClientCertificate(required: true, sendCert: true, certType: 'pem');
await testClientCertificate(
required: false, sendCert: false, certType: 'pem');
await testClientCertificate(required: true, sendCert: false, certType: 'pem');
required: false, sendCert: true, certType: 'pem', password: 'dartdart');
await testClientCertificate(
required: true, sendCert: true, certType: 'pem', password: 'dartdart');
await testClientCertificate(
required: false, sendCert: false, certType: 'pem', password: 'dartdart');
await testClientCertificate(
required: true, sendCert: false, certType: 'pem', password: 'dartdart');
await testClientCertificate(required: false, sendCert: true, certType: 'p12');
await testClientCertificate(required: true, sendCert: true, certType: 'p12');
await testClientCertificate(
required: false, sendCert: false, certType: 'p12');
await testClientCertificate(required: true, sendCert: false, certType: 'p12');
required: false, sendCert: true, certType: 'p12', password: 'dartdart');
await testClientCertificate(
required: true, sendCert: true, certType: 'p12', password: 'dartdart');
await testClientCertificate(
required: false, sendCert: false, certType: 'p12', password: 'dartdart');
await testClientCertificate(
required: true, sendCert: false, certType: 'p12', password: 'dartdart');
asyncEnd();
}

View file

@ -15,20 +15,23 @@ import "dart:io";
String localFile(path) => Platform.script.resolve(path).toFilePath();
SecurityContext serverContext(String certType) => new SecurityContext()
..useCertificateChainSync(localFile('certificates/server_chain.$certType'))
..usePrivateKeySync(localFile('certificates/server_key.$certType'),
password: 'dartdart');
SecurityContext serverContext(String certType, String password) =>
new SecurityContext()
..useCertificateChainSync(localFile('certificates/server_chain.$certType'),
password: password)
..usePrivateKeySync(localFile('certificates/server_key.$certType'),
password: password);
SecurityContext clientContext(String certType) => new SecurityContext()
..setTrustedCertificatesSync(localFile(
'certificates/trusted_certs.$certType'));
SecurityContext clientContext(String certType, String password) =>
new SecurityContext()
..setTrustedCertificatesSync(localFile(
'certificates/trusted_certs.$certType'), password: password);
Future<HttpServer> startServer(String certType) {
Future<HttpServer> startServer(String certType, String password) {
return HttpServer.bindSecure(
"localhost",
0,
serverContext(certType),
serverContext(certType, password),
backlog: 5).then((server) {
server.listen((HttpRequest request) {
request.listen(
@ -45,11 +48,11 @@ Future<HttpServer> startServer(String certType) {
});
}
Future test(String certType) {
Future test(String certType, String password) {
List<int> body = <int>[];
startServer(certType).then((server) {
startServer(certType, password).then((server) {
SecureSocket.connect(
"localhost", server.port, context: clientContext(certType))
"localhost", server.port, context: clientContext(certType, password))
.then((socket) {
socket.write("GET / HTTP/1.0\r\nHost: localhost\r\n\r\n");
socket.close();
@ -74,7 +77,7 @@ Future test(String certType) {
main() async {
asyncStart();
await test('pem');
await test('p12');
await test('pem', 'dartdart');
await test('p12', 'dartdart');
asyncEnd();
}

View file

@ -16,22 +16,79 @@ bool tlsException(e) => e is TlsException;
void testUsePrivateKeyArguments() {
var c = new SecurityContext();
c.useCertificateChainSync(localFile('certificates/server_chain.pem'));
Expect.throws(() => c.usePrivateKeySync(
localFile('certificates/server_key.pem'), password: "dart" * 1000),
argumentError);
// Wrong password.
Expect.throws(() => c.usePrivateKeySync(
localFile('certificates/server_key.pem')),
tlsException);
Expect.throws(() => c.usePrivateKeySync(
localFile('certificates/server_key.pem'), password: "iHackSites"),
tlsException);
Expect.throws(() => c.usePrivateKeySync(
localFile('certificates/server_key.p12')),
tlsException);
Expect.throws(() => c.usePrivateKeySync(
localFile('certificates/server_key.p12'), password: "iHackSites"),
tlsException);
Expect.throws(() => c.setTrustedCertificatesSync(
localFile('certificates/server_key.p12')),
tlsException);
Expect.throws(() => c.setTrustedCertificatesSync(
localFile('certificates/server_key.p12'), password: "iHackSites"),
tlsException);
Expect.throws(() => c.useCertificateChainSync(
localFile('certificates/server_key.p12')),
tlsException);
Expect.throws(() => c.useCertificateChainSync(
localFile('certificates/server_key.p12'), password: "iHackSites"),
tlsException);
Expect.throws(() => c.setClientAuthoritiesSync(
localFile('certificates/server_key.p12')),
argumentError);
Expect.throws(() => c.setClientAuthoritiesSync(
localFile('certificates/server_key.p12'), password: "iHackSites"),
argumentError);
// File does not exist
Expect.throws(() => c.usePrivateKeySync(
localFile('certificates/server_key_oops.pem'),
password: "dartdart"),
fileSystemException);
// Wrong type for file name or data
Expect.throws(() => c.usePrivateKeySync(1), argumentOrTypeError);
Expect.throws(() => c.usePrivateKeySync(null), argumentError);
Expect.throws(() => c.usePrivateKeyBytes(1), argumentOrTypeError);
Expect.throws(() => c.usePrivateKeyBytes(null), argumentError);
// Too-long passwords.
Expect.throws(() => c.usePrivateKeySync(
localFile('certificates/server_key.pem'), password: "dart" * 1000),
argumentError);
Expect.throws(() => c.usePrivateKeySync(
localFile('certificates/server_key.p12'), password: "dart" * 1000),
argumentOrTypeError);
Expect.throws(() => c.setTrustedCertificatesSync(
localFile('certificates/server_key.p12'), password: "dart" * 1000),
argumentOrTypeError);
Expect.throws(() => c.useCertificateChainSync(
localFile('certificates/server_key.p12'), password: "dart" * 1000),
argumentOrTypeError);
Expect.throws(() => c.setClientAuthoritiesSync(
localFile('certificates/server_key.p12'), password: "dart" * 1000),
argumentOrTypeError);
// Bad password type.
Expect.throws(() => c.usePrivateKeySync(
localFile('certificates/server_key.pem'), password: 3),
argumentOrTypeError);
Expect.throws(() => c.setTrustedCertificatesBytes(
localFile('certificates/server_key.pem'), password: 3),
argumentOrTypeError);
Expect.throws(() => c.useCertificateChainBytes(
localFile('certificates/server_key.pem'), password: 3),
argumentOrTypeError);
Expect.throws(() => c.setClientAuthoritiesBytes(
localFile('certificates/server_key.pem'), password: 3),
argumentOrTypeError);