/* * Copyright 2018 Scytl Secure Electronic Voting SA * * All rights reserved * * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package */ /* jshint node:true */ 'use strict'; var certificate = require('../lib/index'); var constants = require('../lib/constants'); var forge = require('node-forge'); var BigInteger = forge.jsbn.BigInteger; describe('The certificate module should be able to ...', function() { var EPOCH_TIME_LENGTH = 10; var rootCertificatePem_ = '-----BEGIN CERTIFICATE-----MIIEKjCCAxKgAwIBAgIQQtWFdPN4NAvUIWyyJyUlbTANBgkqhkiG9w0BAQsFADBUMRAwDgYDVQQDDAdSb290IENOMRYwFAYDVQQLDA1Sb290IE9yZyBVbml0MREwDwYDVQQKDAhSb290IE9yZzEVMBMGA1UEBhMMUm9vdCBDb3VudHJ5MB4XDTE0MDYxODEwMjMyOFoXDTE1MDYxODEwMjMyOFowVDEQMA4GA1UEAwwHUm9vdCBDTjEWMBQGA1UECwwNUm9vdCBPcmcgVW5pdDERMA8GA1UECgwIUm9vdCBPcmcxFTATBgNVBAYTDFJvb3QgQ291bnRyeTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ4AidkWId1zr4IZgItjE9uv38eB8fIGJGEts2n/XNh/EJ7NYZyBzvKSJR83v7LVJjc4pup5crWyS0B5lQ7uuD3/XZ9QpQFtnGiqlKOKH45zsw3ekaHEAco07L2dZznBuQTGLTlhrmvkCFRa8b8WYC+k90oUPvSd/9S4kA1Jlo9JDKHLer0SbjZCcVRXoLSBKmGWE0xSNfmNuZNONDRbtHvSA8A10AOtxKii9w464MXYzmPil7uM1Og1HC+FXCmkzLNfqQ31Om0jra3nLmrCpBJjRPX7svVnoxajRqpazVQnmJjjpzV7yNLwnR9W8OPwqanXcbxmTkrXMxfLxiVXDFUCAwEAAaOB9zCB9DAPBgNVHRMBAf8EBTADAQH/MDUGCCsGAQUFBwEBAQH/BCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL29jc3AudGhhd3RlLmNvbTA0BgNVHR8BAf8EKjAoMCagJKAihiBodHRwOi8vY3JsLnZlcmlzaWduLmNvbS9wY2EzLmNybDArBgNVHREBAf8EITAfpB0wGzEZMBcGA1UEAwwQUHJpdmF0ZUxhYmVsMy0xNTAOBgNVHQ8BAf8EBAMCAQYwNwYDVR0lAQH/BC0wKwYIKwYBBQUHAwEGCCsGAQUFBwMCBgpghkgBhvhFAQgBBglghkgBhvhCBAEwDQYJKoZIhvcNAQELBQADggEBADmtmjApZAXIkGLaZCdkRnhel53BtEdQnG990Oo/tBBboqy2ipum9ByTj3hNWJB3zuPN77rkrek9rbookNcCgVWhHtTk1lUpUK6ZohDsZh8k0MqIhkz+X+HiWGRsEOptjsCaknyWcWb4aXAevMAQMPm/ktkpQ8AOxAq+gtieewWQZP3kGPhBBCfn8TGjdrn9+ymf8EIbAUFXQ8m+oWeNlrdWhqzRXwQbj4EDds1kZdTo0nCYUdH+XEBF9nMyhAxSQWzCKQQTRFWv1dr3dKapzfgrdH8wEgvptiBYCY62O5+3DxiNK/VWquHz6S5GqIwkmSPDPMUU/qK3SNG3xIL1U1k=-----END CERTIFICATE-----'; var rootRsaPublicKeyPem_ = '-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAngCJ2RYh3XOvghmAi2MT26/fx4Hx8gYkYS2zaf9c2H8Qns1hnIHO8pIlHze/stUmNzim6nlytbJLQHmVDu64Pf9dn1ClAW2caKqUo4ofjnOzDd6RocQByjTsvZ1nOcG5BMYtOWGua+QIVFrxvxZgL6T3ShQ+9J3/1LiQDUmWj0kMoct6vRJuNkJxVFegtIEqYZYTTFI1+Y25k040NFu0e9IDwDXQA63EqKL3DjrgxdjOY+KXu4zU6DUcL4VcKaTMs1+pDfU6bSOtrecuasKkEmNE9fuy9WejFqNGqlrNVCeYmOOnNXvI0vCdH1bw4/CpqddxvGZOStczF8vGJVcMVQIDAQAB-----END PUBLIC KEY-----'; var rootSerialNumber_ = '88837713778966677489555326888277517677'; var rootNotBefore_ = '1403087008'; var rootNotAfter_ = '1434623008'; var rootCn_ = 'Root CN'; var rootOrgUnit_ = 'Root Org Unit'; var rootOrg_ = 'Root Org'; var rootCountry_ = 'Root Country'; var leafCertificatePem_ = '-----BEGIN CERTIFICATE-----MIIEMzCCAxugAwIBAgIQRbaPaToIM+VS/d6etgYZ4jANBgkqhkiG9w0BAQsFADBUMRAwDgYDVQQDDAdSb290IENOMRYwFAYDVQQLDA1Sb290IE9yZyBVbml0MREwDwYDVQQKDAhSb290IE9yZzEVMBMGA1UEBhMMUm9vdCBDb3VudHJ5MB4XDTE0MDYxODEwMjMyOFoXDTE1MDYxODEwMjMyOFowYDETMBEGA1UEAwwKU3ViamVjdCBDTjEZMBcGA1UECwwQU3ViamVjdCBPcmcgVW5pdDEUMBIGA1UECgwLU3ViamVjdCBPcmcxGDAWBgNVBAYTD1N1YmplY3QgQ291bnRyeTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIJcqWVOOW539qKZ0SPmOdVaqLgabw0998SAfzrW8Cs8FaYvia4wRbX5N97RPbf3UkbO/QiveB0YQlnDoi2tqlj643mfUgYhMknK4SL0WVQReNcwYMEDbkbyQrCpgKpWWhTSQ2cRD+K3qZpQZ9Qn1RZn615jiqsD+Re5fWbbnL3kc7H5hdclTxZFvEzLgd2KjIsspsqh5UvLowrLuSPLaYD28LsXTDmPeHzYUl62JGPCLl9YNc3av2dY5bjmkf1KiuFKZ27iWh/xdFYAzYloenEw9AxRhJNG+9IucFOENy/0ul2UEb0rgA6Am4cASrhS+aVuZ/OuaC1W+Ut8LlXVmhsCAwEAAaOB9DCB8TAMBgNVHRMBAf8EAjAAMDUGCCsGAQUFBwEBAQH/BCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL29jc3AudGhhd3RlLmNvbTA0BgNVHR8BAf8EKjAoMCagJKAihiBodHRwOi8vY3JsLnZlcmlzaWduLmNvbS9wY2EzLmNybDArBgNVHREBAf8EITAfpB0wGzEZMBcGA1UEAwwQUHJpdmF0ZUxhYmVsMy0xNTAOBgNVHQ8BAf8EBAMCBsAwNwYDVR0lAQH/BC0wKwYIKwYBBQUHAwEGCCsGAQUFBwMCBgpghkgBhvhFAQgBBglghkgBhvhCBAEwDQYJKoZIhvcNAQELBQADggEBAAWZDJD6bg4ohHewszrAbL2tdUNxhrwCgNaHUhwNK43kiLGH0U9innhL1i0jP1VHNkL1G/+ZCo1qzh/Usji/jtlurfAWtrXku6VRF9NP+itKOY5jJ91Ijkc7t4dgoeJq6iMHn6JbDKIQ88r/Ikd0GdF04o5Qjqq1HlUVmqyIOHeHFla4i4tOxTyUBj34eE1No/xmaKYV1QtR1dqSHblR7OagEo7Dd3fXp7iSrKrXaN0Ef/6zeF3zjU5SMKcUcU9d3CbhS/CrGb+UGlqTXgzPXQWESH9AqBNl67+HF3mYktDQOZYPT5WRO5IKSko2cy9pP9UCsLk4oU3xyOxacWDpk1k=-----END CERTIFICATE-----'; var leafRsaPublicKeyPem_ = '-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAglypZU45bnf2opnRI+Y51VqouBpvDT33xIB/OtbwKzwVpi+JrjBFtfk33tE9t/dSRs79CK94HRhCWcOiLa2qWPrjeZ9SBiEyScrhIvRZVBF41zBgwQNuRvJCsKmAqlZaFNJDZxEP4repmlBn1CfVFmfrXmOKqwP5F7l9ZtucveRzsfmF1yVPFkW8TMuB3YqMiyymyqHlS8ujCsu5I8tpgPbwuxdMOY94fNhSXrYkY8IuX1g1zdq/Z1jluOaR/UqK4UpnbuJaH/F0VgDNiWh6cTD0DFGEk0b70i5wU4Q3L/S6XZQRvSuADoCbhwBKuFL5pW5n865oLVb5S3wuVdWaGwIDAQAB-----END PUBLIC KEY-----'; var leafSerialNumber_ = '92664638458902967092551440174596626914'; var leafNotBefore_ = '1403087008'; var leafNotAfter_ = '1434623008'; var leafCn_ = 'Subject CN'; var leafOrgUnit_ = 'Subject Org Unit'; var leafOrg_ = 'Subject Org'; var leafCountry_ = 'Subject Country'; var certificateService_; beforeAll(function() { certificateService_ = certificate.newService(); }); describe('create a certificate service that should be able to ..', function() { it('load a root X.509 certificate into a new X509Certificate object and retrieve its contents', function() { // Load root certificate object. var rootCertificate = certificateService_.newX509Certificate(rootCertificatePem_); // Check that expected fields can be retrieved from root certificate. expect(new BigInteger(rootCertificate.serialNumber, 16)) .toEqual(new BigInteger(rootSerialNumber_)); var notBefore = rootCertificate.notBefore.getTime().toString().substring( 0, EPOCH_TIME_LENGTH); expect(notBefore).toBe(rootNotBefore_); var notAfter = rootCertificate.notAfter.getTime().toString().substring( 0, EPOCH_TIME_LENGTH); expect(notAfter).toBe(rootNotAfter_); expect(rootCertificate.subjectCommonName).toBe(rootCn_); expect(rootCertificate.subjectOrganizationalUnit).toBe(rootOrgUnit_); expect(rootCertificate.subjectOrganization).toBe(rootOrg_); expect(rootCertificate.subjectCountry).toBe(rootCountry_); expect(rootCertificate.issuerCommonName).toBe(rootCn_); expect(rootCertificate.issuerOrganizationalUnit).toBe(rootOrgUnit_); expect(rootCertificate.issuerOrganization).toBe(rootOrg_); expect(rootCertificate.issuerCountry).toBe(rootCountry_); // Check that public key retrieved from root certificate is same as // original. var rootPublicKeyPem = removeNewLineChars(rootCertificate.publicKey); expect(rootPublicKeyPem).toBe(rootRsaPublicKeyPem_); var rootPublicKey = forge.pki.publicKeyFromPem(rootPublicKeyPem); var rootPublicKeyOrig = forge.pki.publicKeyFromPem(rootRsaPublicKeyPem_); expect(rootPublicKey.e).toEqual(rootPublicKeyOrig.e); expect(rootPublicKey.n).toEqual(rootPublicKeyOrig.n); // Check that expected basic constraints flags can be retrieved from // certificate. var basicConstraints = rootCertificate.basicConstraints; expect((basicConstraints.ca)).toBe(true); // Check that expected key usage flags can be retrieved from // certificate. var keyUsageExtension = rootCertificate.keyUsageExtension; expect(keyUsageExtension.digitalSignature).toBe(false); expect(keyUsageExtension.nonRepudiation).toBe(false); expect(keyUsageExtension.keyEncipherment).toBe(false); expect(keyUsageExtension.dataEncipherment).toBe(false); expect(keyUsageExtension.keyAgreement).toBe(false); expect(keyUsageExtension.keyCertSign).toBe(true); expect(keyUsageExtension.crlSign).toBe(true); expect(keyUsageExtension.encipherOnly).toBe(false); expect(keyUsageExtension.decipherOnly).toBe(false); // Check that root certificate is self-signed. expect(rootCertificate.verify(rootCertificatePem_)).toBe(true); // Serialize root certificate to PEM format. var rootCertificatePem = rootCertificate.toPem(); var offset = rootCertificatePem.indexOf('\n') + 1; expect(rootCertificatePem.indexOf('\n', offset)) .toBe(offset + constants.PEM_LINE_LENGTH + 1); expect(removeNewLineChars(rootCertificatePem)) .toBe(rootCertificatePem_); }); it('load a leaf X.509 certificate into a new X509Certificate object and retrieve its contents', function() { // Load leaf certificate. var leafCertificate = certificateService_.newX509Certificate(leafCertificatePem_); // Check that expected fields can be retrieved from leaf certificate. expect(new BigInteger(leafCertificate.serialNumber, 16)) .toEqual(new forge.jsbn.BigInteger(leafSerialNumber_)); var notBefore = leafCertificate.notBefore.getTime().toString().substring( 0, EPOCH_TIME_LENGTH); expect(notBefore).toBe(leafNotBefore_); var notAfter = leafCertificate.notAfter.getTime().toString().substring( 0, EPOCH_TIME_LENGTH); expect(notAfter).toBe(leafNotAfter_); expect(leafCertificate.subjectCommonName).toBe(leafCn_); expect(leafCertificate.subjectOrganizationalUnit).toBe(leafOrgUnit_); expect(leafCertificate.subjectOrganization).toBe(leafOrg_); expect(leafCertificate.subjectCountry).toBe(leafCountry_); expect(leafCertificate.issuerCommonName).toBe(rootCn_); expect(leafCertificate.issuerOrganizationalUnit).toBe(rootOrgUnit_); expect(leafCertificate.issuerOrganization).toBe(rootOrg_); expect(leafCertificate.issuerCountry).toBe(rootCountry_); // Check that public key retrieved from certificate is same as // original. var leafPublicKeyPem = removeNewLineChars(leafCertificate.publicKey); expect(leafPublicKeyPem).toBe(leafRsaPublicKeyPem_); var leafPublicKey = forge.pki.publicKeyFromPem(leafPublicKeyPem); var leafPublicKeyOrig = forge.pki.publicKeyFromPem(leafRsaPublicKeyPem_); expect(leafPublicKey.e).toEqual(leafPublicKeyOrig.e); expect(leafPublicKey.n).toEqual(leafPublicKeyOrig.n); // Check that expected basic constraints flags can be retrieved from // certificate. var basicConstraints = leafCertificate.basicConstraints; expect((basicConstraints.ca)).toBe(false); // Check that expected key usage flags can be retrieved from // certificate. var keyUsageExtension = leafCertificate.keyUsageExtension; expect(keyUsageExtension.digitalSignature).toBe(true); expect(keyUsageExtension.nonRepudiation).toBe(true); expect(keyUsageExtension.keyEncipherment).toBe(false); expect(keyUsageExtension.dataEncipherment).toBe(false); expect(keyUsageExtension.keyAgreement).toBe(false); expect(keyUsageExtension.keyCertSign).toBe(false); expect(keyUsageExtension.crlSign).toBe(false); expect(keyUsageExtension.encipherOnly).toBe(false); expect(keyUsageExtension.decipherOnly).toBe(false); // Verify that leaf certificate was signed by root certificate. var rootCertificate = certificateService_.newX509Certificate(rootCertificatePem_); expect(rootCertificate.verify(leafCertificatePem_)).toBe(true); // Serialize leaf certificate to PEM format. var leafCertificatePem = leafCertificate.toPem(); var offset = leafCertificatePem.indexOf('\n') + 1; expect(leafCertificatePem.indexOf('\n', offset)) .toBe(offset + constants.PEM_LINE_LENGTH + 1); expect(removeNewLineChars(leafCertificatePem)) .toBe(leafCertificatePem_); }); }); function removeNewLineChars(str) { return str.replace(/(\r\n|\n|\r)/gm, ''); } });