Add SecureSocket.addCertificate.

BUG=
R=sgjesse@google.com

Review URL: https://codereview.chromium.org//18097007

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@25610 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
whesse@google.com 2013-07-30 10:01:07 +00:00
parent a38ea2076d
commit 9e27f1b98b
8 changed files with 224 additions and 40 deletions

View file

@ -50,6 +50,7 @@ namespace bin {
V(SecureSocket_RegisterHandshakeCompleteCallback, 2) \
V(SecureSocket_Renegotiate, 4) \
V(SecureSocket_InitializeLibrary, 3) \
V(SecureSocket_AddCertificate, 2) \
V(SecureSocket_NewServicePort, 0) \
V(SecureSocket_FilterPointer, 1) \
V(ServerSocket_CreateBindListen, 5) \

View file

@ -278,6 +278,92 @@ void FUNCTION_NAME(SecureSocket_InitializeLibrary)
}
static Dart_Handle X509FromCertificate(CERTCertificate* certificate) {
PRTime start_validity;
PRTime end_validity;
SECStatus status =
CERT_GetCertTimes(certificate, &start_validity, &end_validity);
if (status != SECSuccess) {
ThrowPRException("CertificateException",
"Cannot get validity times from certificate");
}
int64_t start_epoch_ms = start_validity / PR_USEC_PER_MSEC;
int64_t end_epoch_ms = end_validity / PR_USEC_PER_MSEC;
Dart_Handle subject_name_object =
DartUtils::NewString(certificate->subjectName);
Dart_Handle issuer_name_object =
DartUtils::NewString(certificate->issuerName);
Dart_Handle start_epoch_ms_int = Dart_NewInteger(start_epoch_ms);
Dart_Handle end_epoch_ms_int = Dart_NewInteger(end_epoch_ms);
Dart_Handle date_type =
DartUtils::GetDartType(DartUtils::kCoreLibURL, "DateTime");
Dart_Handle from_milliseconds =
DartUtils::NewString("fromMillisecondsSinceEpoch");
Dart_Handle start_validity_date =
Dart_New(date_type, from_milliseconds, 1, &start_epoch_ms_int);
Dart_Handle end_validity_date =
Dart_New(date_type, from_milliseconds, 1, &end_epoch_ms_int);
Dart_Handle x509_type =
DartUtils::GetDartType(DartUtils::kIOLibURL, "X509Certificate");
Dart_Handle arguments[] = { subject_name_object,
issuer_name_object,
start_validity_date,
end_validity_date };
return Dart_New(x509_type, Dart_Null(), 4, arguments);
}
void FUNCTION_NAME(SecureSocket_AddCertificate)
(Dart_NativeArguments args) {
Dart_EnterScope();
Dart_Handle certificate_object =
ThrowIfError(Dart_GetNativeArgument(args, 0));
Dart_Handle trust_object = ThrowIfError(Dart_GetNativeArgument(args, 1));
if (!Dart_IsList(certificate_object) || !Dart_IsString(trust_object)) {
Dart_ThrowException(DartUtils::NewDartArgumentError(
"Bad argument to SecureSocket.addCertificate"));
}
intptr_t length;
ThrowIfError(Dart_ListLength(certificate_object, &length));
uint8_t* certificate = reinterpret_cast<uint8_t*>(malloc(length + 1));
if (certificate == NULL) {
FATAL("Out of memory in SecureSocket.addCertificate");
}
ThrowIfError(Dart_ListGetAsBytes(
certificate_object, 0, certificate, length));
const char* trust_string;
ThrowIfError(Dart_StringToCString(trust_object,
&trust_string));
CERTCertificate* cert = CERT_DecodeCertFromPackage(
reinterpret_cast<char*>(certificate), length);
if (cert == NULL) {
ThrowPRException("CertificateException", "Certificate cannot be decoded");
}
CERTCertTrust trust;
SECStatus status = CERT_DecodeTrustString(&trust, trust_string);
if (status != SECSuccess) {
ThrowPRException("CertificateException", "Trust string cannot be decoded");
}
status = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert, &trust);
if (status != SECSuccess) {
ThrowPRException("CertificateException", "Cannot set trust attributes");
}
Dart_SetReturnValue(args, X509FromCertificate(cert));
Dart_ExitScope();
return;
}
void FUNCTION_NAME(SecureSocket_PeerCertificate)
(Dart_NativeArguments args) {
Dart_EnterScope();
@ -428,44 +514,6 @@ bool SSLFilter::ProcessAllBuffers(int starts[kNumBuffers],
}
static Dart_Handle X509FromCertificate(CERTCertificate* certificate) {
PRTime start_validity;
PRTime end_validity;
SECStatus status =
CERT_GetCertTimes(certificate, &start_validity, &end_validity);
if (status != SECSuccess) {
ThrowPRException("CertificateException",
"Cannot get validity times from certificate");
}
int64_t start_epoch_ms = start_validity / PR_USEC_PER_MSEC;
int64_t end_epoch_ms = end_validity / PR_USEC_PER_MSEC;
Dart_Handle subject_name_object =
DartUtils::NewString(certificate->subjectName);
Dart_Handle issuer_name_object =
DartUtils::NewString(certificate->issuerName);
Dart_Handle start_epoch_ms_int = Dart_NewInteger(start_epoch_ms);
Dart_Handle end_epoch_ms_int = Dart_NewInteger(end_epoch_ms);
Dart_Handle date_type =
DartUtils::GetDartType(DartUtils::kCoreLibURL, "DateTime");
Dart_Handle from_milliseconds =
DartUtils::NewString("fromMillisecondsSinceEpoch");
Dart_Handle start_validity_date =
Dart_New(date_type, from_milliseconds, 1, &start_epoch_ms_int);
Dart_Handle end_validity_date =
Dart_New(date_type, from_milliseconds, 1, &end_epoch_ms_int);
Dart_Handle x509_type =
DartUtils::GetDartType(DartUtils::kIOLibURL, "X509Certificate");
Dart_Handle arguments[] = { subject_name_object,
issuer_name_object,
start_validity_date,
end_validity_date };
return Dart_New(x509_type, Dart_Null(), 4, arguments);
}
void SSLFilter::Init(Dart_Handle dart_this) {
if (!library_initialized_) {
InitializeLibrary(NULL, "", true, false);

View file

@ -9,7 +9,11 @@ patch class SecureSocket {
/* patch */ static void initialize({String database,
String password,
bool useBuiltinRoots: true})
native "SecureSocket_InitializeLibrary";
native "SecureSocket_InitializeLibrary";
/* patch */ static X509Certificate addCertificate(List<int> certificate,
String trust)
native "SecureSocket_AddCertificate";
}

View file

@ -277,6 +277,11 @@ patch class SecureSocket {
bool useBuiltinRoots: true}) {
throw new UnsupportedError("SecureSocket.initialize");
}
patch static X509Certificate addCertificate(List<int> certificate,
String trust) {
throw new UnsupportedError("SecureSocket.addCertificate");
}
}
patch class _SecureFilter {

View file

@ -96,6 +96,7 @@ abstract class SecureSocket implements Socket {
return completer.future;
}
/**
* Takes an already connected [socket] and starts server side TLS
* handshake to make the communication secure. When the returned
@ -205,6 +206,40 @@ abstract class SecureSocket implements Socket {
external static void initialize({String database,
String password,
bool useBuiltinRoots: true});
/**
* Trust strings for use in [addCertificate].
*/
static const String TRUST_ISSUE_SERVER_CERTIFICATES = 'C,,';
static const String TRUST_ISSUE_CLIENT_CERTIFICATES = 'T,,';
static const String TRUST_ISSUE_CLIENT_SERVER_CERTIFICATES = 'TC,,';
static const String TRUST_CERTIFICATE = 'P,,';
/**
* Adds a X509 certificate (for SSL and TLS secure networking) to the
* in-memory certificate database. Returns an X509Certificate object
* with information about the added certificate.
*
* [certificate] must be a list of bytes encoding a certificate in
* PEM format: a base64 encoded DER certificate, enclosed between
* "-----BEGIN CERTIFICATE-----" and "-----END CERTIFICATE-----".
*
* [trust] is a string specifying the allowed uses of this certificate.
* For example, 'TC,,' specifies that the certificate is for a certificate
* authority that is trusted to issue server and client certificates, so
* that a server or client certificate signed by this authority will be
* accepted.
*
* See the documentation of NSS certutil at
* http://developer.mozilla.org/en-US/docs/NSS_reference/NSS_tools_:_certutil
* or
* http://blogs.oracle.com/meena/entry/notes_about_trust_flags
* for more information about trust attributes.
*/
external static X509Certificate addCertificate(List<int> certificate,
String trust);
}
@ -412,7 +447,8 @@ class _RawSecureSocket extends Stream<RawSocketEvent>
static final int NUM_BUFFERS = 4;
// Is a buffer identifier for an encrypted buffer?
static bool _isBufferEncrypted(int identifier) => identifier >= READ_ENCRYPTED;
static bool _isBufferEncrypted(int identifier) =>
identifier >= READ_ENCRYPTED;
RawSocket _socket;
final Completer<_RawSecureSocket> _handshakeComplete =

View file

@ -0,0 +1,38 @@
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// This test verifies that a server certificate can be verified by a client
// that loads the certificate authority certificate it depends on at runtime.
import "package:path/path.dart";
import "dart:io";
import "dart:async";
String scriptDir = dirname(new Options().script);
void main() {
SecureSocket.initialize(database: join(scriptDir, 'pkcert'),
password: 'dartdart');
runServer().then((SecureServerSocket server) {
return Process.run(new Options().executable,
['--checked',
join(scriptDir, 'certificate_test_client.dart'),
server.port.toString(),
join(scriptDir, 'pkcert', 'myauthority.pem')]);
}).then((ProcessResult result) {
if (result.exitCode != 0) {
print("Client failed with exit code ${result.exitCode}");
print(" stdout:");
print(result.stdout);
print(" stderr:");
print(result.stderr);
throw new AssertionError();
}
});
}
Future<SecureServerSocket> runServer() =>
SecureServerSocket.bind("localhost", 0, "localhost_cert")
.then((server) => server..listen(
(socket) => socket.pipe(socket).then((_) => server.close())));

View file

@ -0,0 +1,41 @@
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
//
// Client that tests that a certificate authority certificate loaded
// at runtime can be used to verify a certificate chain. The server it
// connects to uses localhost_cert, signed by myauthority_cert, to connect
// securely.
import 'dart:io';
void main() {
int port = int.parse(new Options().arguments[0]);
String certificate = new Options().arguments[1];
SecureSocket.initialize();
var mycert = new File(certificate).readAsBytesSync();
bool threw = false;
try {
SecureSocket.addCertificate("I am not a cert".codeUnits,
SecureSocket.TRUST_ISSUE_SERVER_CERTIFICATES);
} on CertificateException catch (e) {
threw = true;
}
if (!threw) throw new AssertException("Expected bad certificate to throw");
threw = false;
try {
SecureSocket.addCertificate(mycert, "Trust me, I'm a string");
} on CertificateException catch (e) {
threw = true;
}
if (!threw) throw new AssertException("Expected bad trust string to throw");
SecureSocket.addCertificate(mycert,
SecureSocket.TRUST_ISSUE_SERVER_CERTIFICATES);
SecureSocket.connect('localhost', port).then((SecureSocket socket) {
socket.writeln("hello world");
socket.listen((data) { });
socket.close();
});
}

View file

@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBpDCCAQ2gAwIBAgIFAJq4IS0wDQYJKoZIhvcNAQEFBQAwFjEUMBIGA1UEAxML
bXlhdXRob3JpdHkwHhcNMTMwMjE1MTA0MzA5WhcNMTgwMjE1MTA0MzA5WjAWMRQw
EgYDVQQDEwtteWF1dGhvcml0eTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
tnrkyrXF1SEUeOdIiULWs0dOEUlX6t73UDVbbTorF6R66fkjkEK3vW9ekZFUWq5+
HVku4LUViJR140+F+CzUYtN73Ur28GqLa6LY4XtzHfPSfgecgayI1mEU+0f/2l8B
4RiE9V8mW9RqPM6Lb69QrwXSYdzStl6ltuLJhgPGqAMCAwEAATANBgkqhkiG9w0B
AQUFAAOBgQBdBUQTUR5oIRdqBGR87qW7caLAuPoVmzikOrSBNoyamVF0lwFFxgNw
sj5VWdMn0SJhXd3EUMVlHr+4B/c3jUy1PlvBQGURn2cp5c4tj3FMOqkemuA0ywOF
gbt2lqi7/RW4bHITqfPi7CDzE36n25vXc64Ylk7vEi3hUfjYfIqNcA==
-----END CERTIFICATE-----