tls: fix encoding in certificate-related functions
authorAdam Lippai <adam.lippai@tresorit.com>
Sat, 13 Sep 2014 22:37:34 +0000 (00:37 +0200)
committerFedor Indutny <fedor@indutny.com>
Mon, 15 Sep 2014 13:42:20 +0000 (17:42 +0400)
Strings are treated as UTF8 instead of one-byte strings when
names are processed and when OpenSSL's ..._print functions are used.

This commit fixes simple/test-tls-peer-certificate-encoding test.

fix #8366

src/node_crypto.cc
test/fixtures/keys/Makefile
test/fixtures/keys/agent5-cert.pem [new file with mode: 0644]
test/fixtures/keys/agent5-csr.pem [new file with mode: 0644]
test/fixtures/keys/agent5-key.pem [new file with mode: 0644]
test/fixtures/keys/agent5.cnf [new file with mode: 0644]
test/fixtures/keys/ca2-cert.srl
test/simple/test-tls-peer-certificate-encoding.js [new file with mode: 0644]

index 1be2f2e..85f18bc 100644 (file)
@@ -69,7 +69,7 @@ static const char CERTIFICATE_PFX[] =  "-----BEGIN CERTIFICATE-----";
 static const int CERTIFICATE_PFX_LEN = sizeof(CERTIFICATE_PFX) - 1;
 
 static const int X509_NAME_FLAGS = ASN1_STRFLGS_ESC_CTRL
-                                 | ASN1_STRFLGS_ESC_MSB
+                                 | ASN1_STRFLGS_UTF8_CONVERT
                                  | XN_FLAG_SEP_MULTILINE
                                  | XN_FLAG_FN_SN;
 
@@ -1130,7 +1130,8 @@ static Local<Object> X509ToObject(Environment* env, X509* cert) {
                          X509_NAME_FLAGS) > 0) {
     BIO_get_mem_ptr(bio, &mem);
     info->Set(env->subject_string(),
-              OneByteString(env->isolate(), mem->data, mem->length));
+              String::NewFromUtf8(env->isolate(), mem->data,
+                                  String::kNormalString, mem->length));
   }
   (void) BIO_reset(bio);
 
@@ -1138,7 +1139,8 @@ static Local<Object> X509ToObject(Environment* env, X509* cert) {
   if (X509_NAME_print_ex(bio, issuer_name, 0, X509_NAME_FLAGS) > 0) {
     BIO_get_mem_ptr(bio, &mem);
     info->Set(env->issuer_string(),
-              OneByteString(env->isolate(), mem->data, mem->length));
+              String::NewFromUtf8(env->isolate(), mem->data,
+                                  String::kNormalString, mem->length));
   }
   (void) BIO_reset(bio);
 
@@ -1162,7 +1164,8 @@ static Local<Object> X509ToObject(Environment* env, X509* cert) {
 
     BIO_get_mem_ptr(bio, &mem);
     info->Set(keys[i],
-              OneByteString(env->isolate(), mem->data, mem->length));
+              String::NewFromUtf8(env->isolate(), mem->data,
+                                  String::kNormalString, mem->length));
 
     (void) BIO_reset(bio);
   }
@@ -1176,13 +1179,15 @@ static Local<Object> X509ToObject(Environment* env, X509* cert) {
       BN_print(bio, rsa->n);
       BIO_get_mem_ptr(bio, &mem);
       info->Set(env->modulus_string(),
-                OneByteString(env->isolate(), mem->data, mem->length));
+                String::NewFromUtf8(env->isolate(), mem->data,
+                                    String::kNormalString, mem->length));
       (void) BIO_reset(bio);
 
       BN_print(bio, rsa->e);
       BIO_get_mem_ptr(bio, &mem);
       info->Set(env->exponent_string(),
-                OneByteString(env->isolate(), mem->data, mem->length));
+                String::NewFromUtf8(env->isolate(), mem->data,
+                                    String::kNormalString, mem->length));
       (void) BIO_reset(bio);
   }
 
@@ -1198,13 +1203,15 @@ static Local<Object> X509ToObject(Environment* env, X509* cert) {
   ASN1_TIME_print(bio, X509_get_notBefore(cert));
   BIO_get_mem_ptr(bio, &mem);
   info->Set(env->valid_from_string(),
-            OneByteString(env->isolate(), mem->data, mem->length));
+            String::NewFromUtf8(env->isolate(), mem->data,
+                                String::kNormalString, mem->length));
   (void) BIO_reset(bio);
 
   ASN1_TIME_print(bio, X509_get_notAfter(cert));
   BIO_get_mem_ptr(bio, &mem);
   info->Set(env->valid_to_string(),
-            OneByteString(env->isolate(), mem->data, mem->length));
+            String::NewFromUtf8(env->isolate(), mem->data,
+                                String::kNormalString, mem->length));
   BIO_free_all(bio);
 
   unsigned int md_size, i;
index 0793329..ee667c6 100644 (file)
@@ -1,4 +1,4 @@
-all: agent1-cert.pem agent2-cert.pem agent3-cert.pem agent4-cert.pem ca2-crl.pem ec-cert.pem dh512.pem dh1024.pem dh2048.pem
+all: agent1-cert.pem agent2-cert.pem agent3-cert.pem agent4-cert.pem agent5-cert.pem ca2-crl.pem ec-cert.pem dh512.pem dh1024.pem dh2048.pem
 
 
 #
@@ -132,6 +132,31 @@ ca2-crl.pem: ca2-key.pem ca2-cert.pem ca2.cnf
                -out ca2-crl.pem \
                -passin 'pass:password'
 
+#
+# agent5 is signed by ca2 (client cert)
+#
+
+agent5-key.pem:
+       openssl genrsa -out agent5-key.pem 1024
+
+agent5-csr.pem: agent5.cnf agent5-key.pem
+       openssl req -new -config agent5.cnf -key agent5-key.pem -out agent5-csr.pem
+
+agent5-cert.pem: agent5-csr.pem ca2-cert.pem ca2-key.pem
+       openssl x509 -req \
+               -days 9999 \
+               -passin "pass:password" \
+               -in agent5-csr.pem \
+               -CA ca2-cert.pem \
+               -CAkey ca2-key.pem \
+               -CAcreateserial \
+               -extfile agent5.cnf \
+               -extensions ext_key_usage \
+               -out agent5-cert.pem
+
+agent5-verify: agent5-cert.pem ca2-cert.pem
+       openssl verify -CAfile ca2-cert.pem agent5-cert.pem
+
 ec-key.pem:
        openssl ecparam -genkey -out ec-key.pem -name prime256v1
 
@@ -157,7 +182,7 @@ dh2048.pem:
 clean:
        rm -f *.pem *.srl ca2-database.txt ca2-serial
 
-test: agent1-verify agent2-verify agent3-verify agent4-verify
+test: agent1-verify agent2-verify agent3-verify agent4-verify agent5-verify
 
 
-.PHONY: all clean test agent1-verify agent2-verify agent3-verify agent4-verify
+.PHONY: all clean test agent1-verify agent2-verify agent3-verify agent4-verify agent5-verify
diff --git a/test/fixtures/keys/agent5-cert.pem b/test/fixtures/keys/agent5-cert.pem
new file mode 100644 (file)
index 0000000..636aed0
--- /dev/null
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICgzCCAeygAwIBAgIJAO6+LOUhGhL4MA0GCSqGSIb3DQEBBQUAMHoxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIEwJDQTELMAkGA1UEBxMCU0YxDzANBgNVBAoTBkpveWVu
+dDEQMA4GA1UECxMHTm9kZS5qczEMMAoGA1UEAxMDY2EyMSAwHgYJKoZIhvcNAQkB
+FhFyeUB0aW55Y2xvdWRzLm9yZzAeFw0xNDA5MTMyMjM0MThaFw00MjAxMjgyMjM0
+MThaMHQxCzAJBgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDERMA8GA1UECgwI
+VHJlc29yaXQxFjAUBgNVBAMMDcOBZMOhbSBMaXBwYWkxJzAlBgkqhkiG9w0BCQEW
+GGFkYW0ubGlwcGFpQHRyZXNvcml0LmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAtrYJnvw24liDRWrfRDp/aBRwAK3xoaJ99YBCj7U8955GJvsoN21q6ZiD
+gT+/7K+HA5gxLXTngrSCTzbk8qfGTD+Gco5WoOK7ubm5R4ePlGrT+yCMaUQBKzX7
+3s3f0rxuAI5F2qCtIJAS/K6Nk3v6C60DyK/rudnY/+d8dFQf2gECAwEAAaMXMBUw
+EwYDVR0lBAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADgYEAC+QBFRXhCWq3
+BLogUKBPl9TWeu13aPkhMFo29ZZB4G2KCoKWUgHZyJ3Q/Dx40QA+PCrqmKxNHyUx
+oEzol97MwB8Q4puv4BC3m8Zkgu/7z7CFH5LMh/shIjDT+kveGFUscqPzjHykeBhP
+2/4042bED6KYhNw+f3DlN+Y1mBYKEuk=
+-----END CERTIFICATE-----
diff --git a/test/fixtures/keys/agent5-csr.pem b/test/fixtures/keys/agent5-csr.pem
new file mode 100644 (file)
index 0000000..a40b220
--- /dev/null
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIB2TCCAUICAQAwdDELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MREw
+DwYDVQQKDAhUcmVzb3JpdDEWMBQGA1UEAwwNw4Fkw6FtIExpcHBhaTEnMCUGCSqG
+SIb3DQEJARYYYWRhbS5saXBwYWlAdHJlc29yaXQuY29tMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQC2tgme/DbiWINFat9EOn9oFHAArfGhon31gEKPtTz3nkYm
++yg3bWrpmIOBP7/sr4cDmDEtdOeCtIJPNuTyp8ZMP4Zyjlag4ru5ublHh4+UatP7
+IIxpRAErNfvezd/SvG4AjkXaoK0gkBL8ro2Te/oLrQPIr+u52dj/53x0VB/aAQID
+AQABoCUwIwYJKoZIhvcNAQkHMRYMFEEgY2hhbGxlbmdlIHBhc3N3b3JkMA0GCSqG
+SIb3DQEBBQUAA4GBAAoVh5wdSi58RJrwy4xaXeZwrRUeCEfNf66AhAr16fa7AxMZ
+7XCMGVYTCcPxsFaagYptWYigYOP3vC89i1dm29PjUwRvyTvkSQ+o/8Cjs+BESeG2
+HrmK7b7xQjXCUwUXfHW7bnqVsTXcX1QfSztWKZANgETITD0MsGjh6Cdv+6ze
+-----END CERTIFICATE REQUEST-----
diff --git a/test/fixtures/keys/agent5-key.pem b/test/fixtures/keys/agent5-key.pem
new file mode 100644 (file)
index 0000000..d9ee12e
--- /dev/null
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQC2tgme/DbiWINFat9EOn9oFHAArfGhon31gEKPtTz3nkYm+yg3
+bWrpmIOBP7/sr4cDmDEtdOeCtIJPNuTyp8ZMP4Zyjlag4ru5ublHh4+UatP7IIxp
+RAErNfvezd/SvG4AjkXaoK0gkBL8ro2Te/oLrQPIr+u52dj/53x0VB/aAQIDAQAB
+AoGAbB+X2/THifT1YhwXmenAQdhuW4iUSKG/RowrV53aQXLxctoId5yRu0Ec+Vy/
+eBJ7pJ3o5EydQFUQFE6Y+BxfFPogncoTu7U8I5S38aBDaL5teX8DzaDqLvcqU7GF
+s+nOACcCErQ2BcpasTkKBFzzrpJtAes2jVzpsfa48JZtc70CQQDe0uUtlKR7tatL
+sugU7OfRoeV1c/tHWp/5HODY0ZeMYvbNw6SqebKeBts26rJNGn4b4LgJs/TTT3qz
+ux6a0ex3AkEA0eo22zaBVjZcygfIfEW9tyfGT1eHgfE/DHcaPHekwgwltoo2gEkU
+hzWy7n09MTkM2Zw6RBz6yvbdJ80/T8UjRwJBALfPJPqauazLSgjiBozseLb3ZD+l
+c02DNp/a8KgrDWbjZFCM6VMvnOa7JS6CIJ92ET2R/H8UkguWbtPAshhovzUCQQC8
+uU8SbQGBKiToOnEkUWtMhMUFRlN9HxEpOtdqr8J/933cjIyNb6a2HTA+vHhMjdcg
+uhWkcU2FNscEZsJaDIo3AkAOnbQTW1w4WjkV92B+EH6dQfS3wdCFVDUYM+POcwfQ
+7HNtjmk1XeMTkGLlyinyFe2nARfXXzMmyRYP8o2m9uCf
+-----END RSA PRIVATE KEY-----
diff --git a/test/fixtures/keys/agent5.cnf b/test/fixtures/keys/agent5.cnf
new file mode 100644 (file)
index 0000000..1958e21
--- /dev/null
@@ -0,0 +1,21 @@
+[ req ]
+string_mask            = utf8only
+utf8                   = yes
+default_bits           = 1024
+days                   = 999
+distinguished_name     = req_distinguished_name
+attributes             = req_attributes
+prompt                 = no
+
+[ req_distinguished_name ]
+C                      = HU
+L                      = Budapest
+O                      = Tresorit
+CN                     = Ádám Lippai
+emailAddress           = adam.lippai@tresorit.com
+
+[ req_attributes ]
+challengePassword              = A challenge password
+
+[ ext_key_usage ]
+extendedKeyUsage       = clientAuth
diff --git a/test/simple/test-tls-peer-certificate-encoding.js b/test/simple/test-tls-peer-certificate-encoding.js
new file mode 100644 (file)
index 0000000..288236a
--- /dev/null
@@ -0,0 +1,62 @@
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+if (!process.versions.openssl) {
+  console.error('Skipping because node compiled without OpenSSL.');
+  process.exit(0);
+}
+
+var common = require('../common');
+var assert = require('assert');
+var tls = require('tls');
+var fs = require('fs');
+var util = require('util');
+var join = require('path').join;
+var spawn = require('child_process').spawn;
+
+var options = {
+  key: fs.readFileSync(join(common.fixturesDir, 'keys', 'agent5-key.pem')),
+  cert: fs.readFileSync(join(common.fixturesDir, 'keys', 'agent5-cert.pem')),
+  ca: [ fs.readFileSync(join(common.fixturesDir, 'keys', 'ca2-cert.pem')) ]
+};
+var verified = false;
+
+var server = tls.createServer(options, function(cleartext) {
+  cleartext.end('World');
+});
+server.listen(common.PORT, function() {
+  var socket = tls.connect({
+    port: common.PORT,
+    rejectUnauthorized: false
+  }, function() {
+    var peerCert = socket.getPeerCertificate();
+
+    common.debug(util.inspect(peerCert));
+    assert.equal(peerCert.subject.CN, 'Ádám Lippai');
+    verified = true;
+    server.close();
+  });
+  socket.end('Hello');
+});
+
+process.on('exit', function() {
+  assert.ok(verified);
+});