mirror of
https://github.com/dart-lang/sdk
synced 2024-09-22 18:23:41 +00:00
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:
parent
a38ea2076d
commit
9e27f1b98b
|
@ -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) \
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 =
|
||||
|
|
38
tests/standalone/io/certificate_test.dart
Normal file
38
tests/standalone/io/certificate_test.dart
Normal 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())));
|
41
tests/standalone/io/certificate_test_client.dart
Normal file
41
tests/standalone/io/certificate_test_client.dart
Normal 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();
|
||||
});
|
||||
}
|
11
tests/standalone/io/pkcert/myauthority.pem
Normal file
11
tests/standalone/io/pkcert/myauthority.pem
Normal 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-----
|
Loading…
Reference in a new issue