I have the following Javascript code which works fine when I run it with nodejs. However, I would like to write something similar that works with Dart. I\'ve gone through th
Sending a client certificate in your HTTPS request can now be done in Dart as easily as in node.js, starting with version 1.13.0. The client certificate and key, in PEM format, can be added to the default SecurityContext object, before you make your HTTPS request.
Dart has now switched to using BoringSSL, a fork of OpenSSL maintained by Google. BoringSSL uses X509 certificates (the certificates used by SSL and TLS) stored in files in PEM format. The older versions of Dart used NSS, which had its own database of certificates and keys, that was maintained with command-line tools. Dart has changed some parameters of SecureSocket methods, and added a SecurityContext class.
SecurityContext.defaultContext is an object that contains the built-in trusted roots of well-known certificate authorities, taken from Mozilla's database that they use for Firefox and NSS. So your client should use this object, and add the client certificate and private key to it, so they will be used to authenticate with the server that requests them:
Future postWithClientCertificate() async {
var context = SecurityContext.defaultContext;
context.useCertificateChain('client-2048.crt');
context.usePrivateKey('client-2048.key',
password:'keyfile_password');
HttpClient client = new HttpClient(context: context);
// The rest of this code comes from your question.
var uri = "https://identitysso-api.betfair.com:443/api/certlogin";
var data = 'username=xxxxxxxx&password=xxxxxxxx';
var appKey = 'xxxxxxxxxxxx';
var method = 'POST';
var request = await client.openUrl(method,Uri.parse(uri))
request.headers.set(HttpHeaders.CONTENT_TYPE,
'application/x-www-form-urlencoded');
request.headers.set('X-Application', appKey);
request.write(data);
var response = await request.close();
// Process the response.
}
The functions SecurityContext.useCertificateChain and SecurityContext.usePrivateKey are the same ones that are used to set the server certificate and key, for a SecureServerSocket, but when the context is used for a client connection, they specify the client certificate to be sent upon request.
It is possible to use a client certificates with dart:io
HTTPS requests. However it is a bit complicated due to the way SSL/TLS is implemented in Dart.
Dart uses the Mozilla NSS library for SSL/TLS. NSS stores all its keys and certificates in a database. The SecureSocket.initialize
call is used to select the NSS database to use. If no database is specified the builtin database will be used. The builtin database contains only trusted root certificates.
To work with client certificates you need to create a NSS database with your certificates and keys. The database is manipulated using the NSS certutil
tool, which is documented here:
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Reference/NSS_tools_:_certutil
This example creates a database with three certificates, a self signed CA, a server certificate for localhost and a client certificate. Use testtest
as the password.
$ mkdir certdb
$ certutil -N -d sql:certdb
$ certutil -S -s "CN=Example CA" -n my-ca-cert -x -t "TCu,u,u" -5 sslCA -m 1234 -d sql:certdb
Answer 9 and N
$ certutil -S -s "CN=localhost" -n my-server-cert -c "my-ca-cert" -t "u,u,u" -m 730 -d sql:certdb
$ certutil -S -s "CN=sgjesse" -n my-client-cert -c "my-ca-cert" -t "Pu,u,u" -m 731 -d sql:certdb
Then you can run this server:
import "dart:async";
import "dart:io";
void serve() {
HttpServer.bindSecure('localhost',
8080,
certificateName: 'my-server-cert',
requestClientCertificate: true).then((server) {
server.listen((HttpRequest request) {
if (request.certificate != null) {
print('Client certificate ${request.certificate.subject}');
} else {
print('No client certificate');
}
request.response.write("Hello");
request.response.close();
});
});
}
void InitializeSSL() {
var testPkcertDatabase = Platform.script.resolve('certdb').toFilePath();
SecureSocket.initialize(database: testPkcertDatabase,
password: 'testtest');
}
void main() {
InitializeSSL();
serve();
}
And this client:
import "dart:async";
import "dart:io";
void request() {
HttpClient client = new HttpClient();
client.getUrl(Uri.parse("https://localhost:8080/"))
.then((request) => request.close())
.then((response) {
print('Subject: ${response.certificate.subject}');
print('Issuer: ${response.certificate.issuer}');
return response.transform(UTF8.decoder).join('');
})
.then(print);
}
void InitializeSSL() {
var testPkcertDatabase = Platform.script.resolve('certdb').toFilePath();
SecureSocket.initialize(database: testPkcertDatabase,
password: 'testtest');
}
void main() {
InitializeSSL();
request();
}
And hopefully the client will present the client certificate. However this relies on NSS picking the client certificate, and I am not sure of the rules for this. If you use a SecureSocket
you can chose the client certificate explicitly, but that is currently not possible for HTTPS, see https://code.google.com/p/dart/issues/detail?id=8872.
Here is a client which uses SecureSocket
:
import "dart:async";
import "dart:io";
void request() {
SecureSocket.connect('localhost',
8080,
sendClientCertificate: true,
certificateName: 'my-client-cert').then((socket) {
socket.write("GET / HTTP/1.0\r\n\r\n");
socket.listen(print);
});
}
void InitializeSSL() {
var testPkcertDatabase = Platform.script.resolve('certdb').toFilePath();
SecureSocket.initialize(database: testPkcertDatabase,
password: 'testtest');
}
void main() {
InitializeSSL();
request();
}
Hope that helps.