[dart:io] Adds X509Certificate.der and X509Certificate.pem

fixes #33115

Change-Id: I7ccf5998b23e936040fe65792824f09d3f494cf7
Reviewed-on: https://dart-review.googlesource.com/55505
Commit-Queue: Zach Anderson <zra@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
This commit is contained in:
Zach Anderson 2018-05-17 16:20:06 +00:00 committed by commit-bot@chromium.org
parent 62ffedefdd
commit e7495e427c
8 changed files with 185 additions and 0 deletions

View file

@ -15,6 +15,10 @@
* Marked `MirrorsUsed` as deprecated. The mirrors library is no longer
supported by dart2js, and `MirrorsUsed` only affected dart2js.
* `dart:io`
* Added `X509Certificate.der`, `X509Certificate.pem`, and
`X509Certificate.sha1`.
### Dart VM
### Tool Changes

View file

@ -169,6 +169,9 @@ namespace bin {
V(SynchronousSocket_ReadList, 4) \
V(SynchronousSocket_WriteList, 4) \
V(SystemEncodingToString, 1) \
V(X509_Der, 1) \
V(X509_Pem, 1) \
V(X509_Sha1, 1) \
V(X509_Subject, 1) \
V(X509_Issuer, 1) \
V(X509_StartValidity, 1) \

View file

@ -202,6 +202,33 @@ class _X509CertificateImpl extends NativeFieldWrapperClass1
// This is done by WrappedX509 in secure_socket.cc.
_X509CertificateImpl();
Uint8List _cachedDer;
Uint8List get _der native "X509_Der";
Uint8List get der {
if (_cachedDer == null) {
_cachedDer = _der;
}
return _cachedDer;
}
String _cachedPem;
String get _pem native "X509_Pem";
String get pem {
if (_cachedPem == null) {
_cachedPem = _pem;
}
return _cachedPem;
}
Uint8List _cachedSha1;
Uint8List get _sha1 native "X509_Sha1";
Uint8List get sha1 {
if (_cachedSha1 == null) {
_cachedSha1 = _sha1;
}
return _cachedSha1;
}
String get subject native "X509_Subject";
String get issuer native "X509_Issuer";
DateTime get startValidity {

View file

@ -125,6 +125,16 @@ void FUNCTION_NAME(SecurityContext_UseCertificateChainBytes)(
"Secure Sockets unsupported on this platform"));
}
void FUNCTION_NAME(X509_Der)(Dart_NativeArguments args) {
Dart_ThrowException(DartUtils::NewDartArgumentError(
"Secure Sockets unsupported on this platform"));
}
void FUNCTION_NAME(X509_Sha1)(Dart_NativeArguments args) {
Dart_ThrowException(DartUtils::NewDartArgumentError(
"Secure Sockets unsupported on this platform"));
}
void FUNCTION_NAME(X509_Subject)(Dart_NativeArguments args) {
Dart_ThrowException(DartUtils::NewDartArgumentError(
"Secure Sockets unsupported on this platform"));
@ -145,6 +155,11 @@ void FUNCTION_NAME(X509_EndValidity)(Dart_NativeArguments args) {
"Secure Sockets unsupported on this platform"));
}
void FUNCTION_NAME(X509_Pem)(Dart_NativeArguments args) {
Dart_ThrowException(DartUtils::NewDartArgumentError(
"Secure Sockets unsupported on this platform"));
}
class SSLFilter {
public:
static CObject* ProcessFilterRequest(const CObjectArray& request);

View file

@ -647,6 +647,101 @@ static X509* GetX509Certificate(Dart_NativeArguments args) {
return certificate;
}
Dart_Handle X509Helper::GetDer(Dart_NativeArguments args) {
X509* certificate = GetX509Certificate(args);
// When the second argument is NULL, i2d_X509() returns the length of the
// DER encoded cert in bytes.
intptr_t length = i2d_X509(certificate, NULL);
Dart_Handle cert_handle = Dart_NewTypedData(Dart_TypedData_kUint8, length);
if (Dart_IsError(cert_handle)) {
Dart_PropagateError(cert_handle);
}
Dart_TypedData_Type typ;
void* dart_cert_bytes = NULL;
Dart_Handle status =
Dart_TypedDataAcquireData(cert_handle, &typ, &dart_cert_bytes, &length);
if (Dart_IsError(status)) {
Dart_PropagateError(status);
}
// When the the second argument points to a non-NULL buffer address,
// i2d_X509 fills that buffer with the DER encoded cert data and increments
// the buffer pointer.
unsigned char* tmp = static_cast<unsigned char*>(dart_cert_bytes);
length = i2d_X509(certificate, &tmp);
if (length < 0) {
Dart_TypedDataReleaseData(cert_handle);
SecureSocketUtils::ThrowIOException(
-1, "TlsException", "Failed to get certificate bytes", NULL);
// SecureSocketUtils::ThrowIOException() does not return.
}
status = Dart_TypedDataReleaseData(cert_handle);
if (Dart_IsError(status)) {
Dart_PropagateError(status);
}
return cert_handle;
}
Dart_Handle X509Helper::GetPem(Dart_NativeArguments args) {
X509* certificate = GetX509Certificate(args);
BIO* cert_bio = BIO_new(BIO_s_mem());
intptr_t status = PEM_write_bio_X509(cert_bio, certificate);
if (status == 0) {
BIO_free(cert_bio);
SecureSocketUtils::ThrowIOException(
-1, "TlsException", "Failed to write certificate to PEM", NULL);
// SecureSocketUtils::ThrowIOException() does not return.
}
BUF_MEM* mem = NULL;
BIO_get_mem_ptr(cert_bio, &mem);
Dart_Handle pem_string = Dart_NewStringFromUTF8(
reinterpret_cast<const uint8_t*>(mem->data), mem->length);
BIO_free(cert_bio);
if (Dart_IsError(pem_string)) {
Dart_PropagateError(pem_string);
}
return pem_string;
}
Dart_Handle X509Helper::GetSha1(Dart_NativeArguments args) {
unsigned char sha1_bytes[EVP_MAX_MD_SIZE];
X509* certificate = GetX509Certificate(args);
const EVP_MD* hash_type = EVP_sha1();
unsigned int sha1_size;
intptr_t status = X509_digest(certificate, hash_type, sha1_bytes, &sha1_size);
if (status == 0) {
SecureSocketUtils::ThrowIOException(
-1, "TlsException", "Failed to compute certificate's sha1", NULL);
// SecureSocketUtils::ThrowIOException() does not return.
}
Dart_Handle sha1_handle = Dart_NewTypedData(Dart_TypedData_kUint8, sha1_size);
if (Dart_IsError(sha1_handle)) {
Dart_PropagateError(sha1_handle);
}
Dart_TypedData_Type typ;
void* dart_sha1_bytes;
intptr_t length;
Dart_Handle result =
Dart_TypedDataAcquireData(sha1_handle, &typ, &dart_sha1_bytes, &length);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
memmove(dart_sha1_bytes, sha1_bytes, length);
result = Dart_TypedDataReleaseData(sha1_handle);
if (Dart_IsError(result)) {
Dart_PropagateError(result);
}
return sha1_handle;
}
Dart_Handle X509Helper::GetSubject(Dart_NativeArguments args) {
X509* certificate = GetX509Certificate(args);
X509_NAME* subject = X509_get_subject_name(certificate);
@ -784,6 +879,18 @@ void FUNCTION_NAME(SecurityContext_TrustBuiltinRoots)(
context->TrustBuiltinRoots();
}
void FUNCTION_NAME(X509_Der)(Dart_NativeArguments args) {
Dart_SetReturnValue(args, X509Helper::GetDer(args));
}
void FUNCTION_NAME(X509_Pem)(Dart_NativeArguments args) {
Dart_SetReturnValue(args, X509Helper::GetPem(args));
}
void FUNCTION_NAME(X509_Sha1)(Dart_NativeArguments args) {
Dart_SetReturnValue(args, X509Helper::GetSha1(args));
}
void FUNCTION_NAME(X509_Subject)(Dart_NativeArguments args) {
Dart_SetReturnValue(args, X509Helper::GetSubject(args));
}

View file

@ -102,6 +102,9 @@ class SSLCertContext : public ReferenceCounted<SSLCertContext> {
class X509Helper : public AllStatic {
public:
static Dart_Handle GetDer(Dart_NativeArguments args);
static Dart_Handle GetPem(Dart_NativeArguments args);
static Dart_Handle GetSha1(Dart_NativeArguments args);
static Dart_Handle GetSubject(Dart_NativeArguments args);
static Dart_Handle GetIssuer(Dart_NativeArguments args);
static Dart_Handle GetStartValidity(Dart_NativeArguments args);

View file

@ -342,6 +342,15 @@ abstract class RawSecureSocket implements RawSocket {
abstract class X509Certificate {
external factory X509Certificate._();
/// The DER encoded bytes of the certificate.
Uint8List get der;
/// The PEM encoded String of the certificate.
String get pem;
/// The SHA1 hash of the certificate.
Uint8List get sha1;
String get subject;
String get issuer;
DateTime get startValidity;

View file

@ -41,10 +41,27 @@ Future<SecureServerSocket> startEchoServer() {
});
}
void checkServerCertificate(X509Certificate serverCert) {
String serverCertString = serverCert.pem;
String certFile =
new File(localFile('certificates/server_chain.pem')).readAsStringSync();
Expect.isTrue(certFile.contains(serverCertString));
// Computed with:
// openssl x509 -noout -sha1 -fingerprint -in certificates/server_chain.pem
List<int> serverSha1 = <int>[
0xB3, 0x01, 0xCB, 0x7E, 0x6F, 0xEF, 0xBE, 0xEF, //
0x75, 0x6D, 0xA8, 0x80, 0x60, 0xA8, 0x5D, 0x6F, //
0xC4, 0xED, 0xCD, 0x48, //
];
Expect.listEquals(serverSha1, serverCert.sha1);
}
Future testClient(server) {
return SecureSocket
.connect(HOST, server.port, context: clientContext)
.then((socket) {
checkServerCertificate(socket.peerCertificate);
socket.write("Hello server.");
socket.close();
return socket.fold(<int>[], (message, data) => message..addAll(data)).then(