From ebea506c341c086eb5915078dbbdda7af732e985 Mon Sep 17 00:00:00 2001 From: YuryStrozhevsky Date: Sun, 22 Mar 2015 12:58:00 +0300 Subject: [PATCH] New release 1.1.0 --- README.md | 16 +- .../CMSSigned_complex_example.html | 68 +- .../CRL_complex_example.html | 50 +- .../X509_cert_complex_example.html | 61 +- .../CreateCMSSignedData.html | 87 +- .../CreateNewX509Certificate.html | 46 +- .../OCSP_resp_complex_example.html | 53 +- .../PKCS10_complex_example.html | 107 ++- .../TSP_resp_complex_example.html | 68 +- examples/Use WebCrypto/HowToUseWebCrypto.js | 39 +- org/pkijs/cms_schema.js | 77 +- org/pkijs/cms_simpl.js | 457 +++++++-- org/pkijs/common.js | 866 ++++++++++++++++++ org/pkijs/ocsp_tsp_schema.js | 41 + org/pkijs/ocsp_tsp_simpl.js | 292 ++++-- org/pkijs/x509_schema.js | 60 +- org/pkijs/x509_simpl.js | 771 +++++++++++++--- package.json | 2 +- 18 files changed, 2678 insertions(+), 483 deletions(-) diff --git a/README.md b/README.md index 66165e965..4896de127 100644 --- a/README.md +++ b/README.md @@ -15,24 +15,28 @@ Public Key Infrastructure (PKI) is the basis of how identity and key management ## Features of the library -* Fully object-oriented library. Inhiritence is using everywhere inside the lib;. -* Working with HTML5 data objects (ArrayBuffer, Uint8Array, Promises, WebCrypto, etc.). +* Fully object-oriented library. Inhiritence is using everywhere inside the lib; +* Working with HTML5 data objects (ArrayBuffer, Uint8Array, Promises, WebCrypto, etc.); * Has a complete set of helpers for working with types like: * GeneralName; * RelativeDistinguishedName; * Time; * AlgorithmIdentifier; * All types of ASN.1 strings, including "international" like UniversalString, UTF8String and BMPString (with help from [ASN1js][]); - * All extension types of X.509 certificates (BasicConstraints, CertificatePolicies, AuthorityKeyIdentifier etc.) + * All extension types of X.509 certificates (BasicConstraints, CertificatePolicies, AuthorityKeyIdentifier etc.); * All "support types" for OCSP requests and responces; - * All "support types" for Time-Stamping Protocol (TSP) requests and responces. -* Has own certification chain verification engine, purelly made on JavaScript with help from Promises and WebCrypto latest standard implementation; + * All "support types" for Time-Stamping Protocol (TSP) requests and responces; +* **Has own certification chain verification engine, purelly made on JavaScript with help from Promises and WebCrypto latest standard implementation;** +* Working with **all** WebCrypto signature algorithms: + * RSASSA-PKCS1-v1_5; + * RSA-PSS; + * ECDSA; * Working with all major PKI-related types ("minor" types are not mentioned here but there are huge number of such "minor types"): * X.509 certificates: * Parsing internal values; * Getting/setting any internal values; * Creatiion of a new X.509 certificate "from scratch"; - * Internal certificate chain validation engine. + * **Internal certificate chain validation engine**; * X.509 "certificate revocation lists" (CRLs): * Parsing internal values; * Getting/setting any internal values; diff --git a/examples/CMS Signed complex example/CMSSigned_complex_example.html b/examples/CMS Signed complex example/CMSSigned_complex_example.html index 0d765204c..920b0e914 100644 --- a/examples/CMS Signed complex example/CMSSigned_complex_example.html +++ b/examples/CMS Signed complex example/CMSSigned_complex_example.html @@ -248,32 +248,37 @@ var publicKey; var privateKey; - var hash_algorithm = "sha-1"; - var hash_algorithm_oid = "1.3.14.3.2.26"; - var signature_algorithm = "1.2.840.113549.1.1.5"; - + var hash_algorithm; var hash_option = document.getElementById("hash_alg").value; switch(hash_option) { case "alg_SHA1": hash_algorithm = "sha-1"; - hash_algorithm_oid = "1.3.14.3.2.26"; - signature_algorithm = "1.2.840.113549.1.1.5"; break; case "alg_SHA256": hash_algorithm = "sha-256"; - hash_algorithm_oid = "2.16.840.1.101.3.4.2.1"; - signature_algorithm = "1.2.840.113549.1.1.11"; break; case "alg_SHA384": hash_algorithm = "sha-384"; - hash_algorithm_oid = "2.16.840.1.101.3.4.2.2"; - signature_algorithm = "1.2.840.113549.1.1.12"; break; case "alg_SHA512": hash_algorithm = "sha-512"; - hash_algorithm_oid = "2.16.840.1.101.3.4.2.3"; - signature_algorithm = "1.2.840.113549.1.1.13"; + break; + default:; + } + + var signature_algorithm_name; + var sign_option = document.getElementById("sign_alg").value; + switch(sign_option) + { + case "alg_RSA15": + signature_algorithm_name = "RSASSA-PKCS1-V1_5"; + break; + case "alg_RSA2": + signature_algorithm_name = "RSA-PSS"; + break; + case "alg_ECDSA": + signature_algorithm_name = "ECDSA"; break; default:; } @@ -343,16 +348,19 @@ parsedValue: key_usage // Parsed value for well-known extensions })); // #endregion - - cert_simpl.signatureAlgorithm.algorithm_id = signature_algorithm; - cert_simpl.signature.algorithm_id = cert_simpl.signatureAlgorithm.algorithm_id; // Must be the same value // #endregion // #region Create a new key pair sequence = sequence.then( function() { - return crypto.generateKey({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: hash_algorithm } }, true, ["sign", "verify"]); + // #region Get default algorithm parameters for key generation + var algorithm = org.pkijs.getAlgorithmParameters(signature_algorithm_name, "generatekey"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = hash_algorithm; + // #endregion + + return crypto.generateKey(algorithm.algorithm, true, algorithm.usages); } ); // #endregion @@ -384,7 +392,7 @@ sequence = sequence.then( function() { - return cert_simpl.sign(privateKey); + return cert_simpl.sign(privateKey, hash_algorithm); }, function(error) { @@ -498,12 +506,6 @@ { cms_signed_simpl = new org.pkijs.simpl.CMS_SIGNED_DATA({ version: 1, - digestAlgorithms: [ - new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ - algorithm_id: hash_algorithm_oid, - algorithm_params: new org.pkijs.asn1.NULL() - }) // SHA-1 - ], encapContentInfo: new org.pkijs.simpl.cms.EncapsulatedContentInfo({ eContentType: "1.2.840.113549.1.7.1", // "data" content type eContent: new org.pkijs.asn1.OCTETSTRING({ value_hex: buffer }) @@ -514,15 +516,7 @@ sid: new org.pkijs.simpl.cms.IssuerAndSerialNumber({ issuer: cert_simpl.issuer, serialNumber: cert_simpl.serialNumber - }), - digestAlgorithm: new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ - algorithm_id: hash_algorithm_oid, - algorithm_params: new org.pkijs.asn1.NULL() - }), // SHA-1 - signatureAlgorithm: new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ - algorithm_id: "1.2.840.113549.1.1.1", - algorithm_params: new org.pkijs.asn1.NULL() - }), // RSA (PKCS #1 v1.5) key transport algorithm + }) }) ], certificates: [cert_simpl] @@ -536,7 +530,7 @@ }); } - return cms_signed_simpl.sign(privateKey, 0); + return cms_signed_simpl.sign(privateKey, 0, hash_algorithm); } ); // #endregion @@ -932,6 +926,14 @@

+

+ + +

diff --git a/examples/CRL complex example/CRL_complex_example.html b/examples/CRL complex example/CRL_complex_example.html index c8b5ca6e9..10b0fc274 100644 --- a/examples/CRL complex example/CRL_complex_example.html +++ b/examples/CRL complex example/CRL_complex_example.html @@ -241,32 +241,37 @@ var publicKey; var privateKey; - var hash_algorithm = "sha-1"; - var hash_algorithm_oid = "1.3.14.3.2.26"; - var signature_algorithm = "1.2.840.113549.1.1.5"; - + var hash_algorithm; var hash_option = document.getElementById("hash_alg").value; switch(hash_option) { case "alg_SHA1": hash_algorithm = "sha-1"; - hash_algorithm_oid = "1.3.14.3.2.26"; - signature_algorithm = "1.2.840.113549.1.1.5"; break; case "alg_SHA256": hash_algorithm = "sha-256"; - hash_algorithm_oid = "2.16.840.1.101.3.4.2.1"; - signature_algorithm = "1.2.840.113549.1.1.11"; break; case "alg_SHA384": hash_algorithm = "sha-384"; - hash_algorithm_oid = "2.16.840.1.101.3.4.2.2"; - signature_algorithm = "1.2.840.113549.1.1.12"; break; case "alg_SHA512": hash_algorithm = "sha-512"; - hash_algorithm_oid = "2.16.840.1.101.3.4.2.3"; - signature_algorithm = "1.2.840.113549.1.1.13"; + break; + default:; + } + + var signature_algorithm_name; + var sign_option = document.getElementById("sign_alg").value; + switch(sign_option) + { + case "alg_RSA15": + signature_algorithm_name = "RSASSA-PKCS1-V1_5"; + break; + case "alg_RSA2": + signature_algorithm_name = "RSA-PSS"; + break; + case "alg_ECDSA": + signature_algorithm_name = "ECDSA"; break; default:; } @@ -318,16 +323,19 @@ extnID: "2.5.29.20", // cRLNumber extnValue: (new org.pkijs.asn1.INTEGER({ value: 2 })).toBER(false) })]; - - crl_simpl.signatureAlgorithm.algorithm_id = signature_algorithm; - crl_simpl.signature.algorithm_id = signature_algorithm; // #endregion // #region Create a new key pair sequence = sequence.then( function() { - return crypto.generateKey({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: hash_algorithm } }, true, ["sign", "verify"]); + // #region Get default algorithm parameters for key generation + var algorithm = org.pkijs.getAlgorithmParameters(signature_algorithm_name, "generatekey"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = hash_algorithm; + // #endregion + + return crypto.generateKey(algorithm.algorithm, true, algorithm.usages); } ); // #endregion @@ -353,7 +361,7 @@ sequence = sequence.then( function() { - return crl_simpl.sign(privateKey); + return crl_simpl.sign(privateKey, hash_algorithm); }, function(error) { @@ -630,6 +638,14 @@

+

+ + +

Create diff --git a/examples/Certificate complex example/X509_cert_complex_example.html b/examples/Certificate complex example/X509_cert_complex_example.html index 2644336ab..b2c517b6e 100644 --- a/examples/Certificate complex example/X509_cert_complex_example.html +++ b/examples/Certificate complex example/X509_cert_complex_example.html @@ -340,32 +340,37 @@ var publicKey; var privateKey; - var hash_algorithm = "sha-1"; - var hash_algorithm_oid = "1.3.14.3.2.26"; - var signature_algorithm = "1.2.840.113549.1.1.5"; - + var hash_algorithm; var hash_option = document.getElementById("hash_alg").value; switch(hash_option) { case "alg_SHA1": hash_algorithm = "sha-1"; - hash_algorithm_oid = "1.3.14.3.2.26"; - signature_algorithm = "1.2.840.113549.1.1.5"; break; case "alg_SHA256": hash_algorithm = "sha-256"; - hash_algorithm_oid = "2.16.840.1.101.3.4.2.1"; - signature_algorithm = "1.2.840.113549.1.1.11"; break; case "alg_SHA384": hash_algorithm = "sha-384"; - hash_algorithm_oid = "2.16.840.1.101.3.4.2.2"; - signature_algorithm = "1.2.840.113549.1.1.12"; break; case "alg_SHA512": hash_algorithm = "sha-512"; - hash_algorithm_oid = "2.16.840.1.101.3.4.2.3"; - signature_algorithm = "1.2.840.113549.1.1.13"; + break; + default:; + } + + var signature_algorithm_name; + var sign_option = document.getElementById("sign_alg").value; + switch(sign_option) + { + case "alg_RSA15": + signature_algorithm_name = "RSASSA-PKCS1-V1_5"; + break; + case "alg_RSA2": + signature_algorithm_name = "RSA-PSS"; + break; + case "alg_ECDSA": + signature_algorithm_name = "ECDSA"; break; default:; } @@ -435,16 +440,19 @@ parsedValue: key_usage // Parsed value for well-known extensions })); // #endregion - - cert_simpl.signatureAlgorithm.algorithm_id = signature_algorithm; - cert_simpl.signature.algorithm_id = cert_simpl.signatureAlgorithm.algorithm_id; // Must be the same value // #endregion // #region Create a new key pair sequence = sequence.then( function() { - return crypto.generateKey({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: hash_algorithm } }, true, ["sign", "verify"]); + // #region Get default algorithm parameters for key generation + var algorithm = org.pkijs.getAlgorithmParameters(signature_algorithm_name, "generatekey"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = hash_algorithm; + // #endregion + + return crypto.generateKey(algorithm.algorithm, true, algorithm.usages); } ); // #endregion @@ -476,7 +484,7 @@ sequence = sequence.then( function() { - return cert_simpl.sign(privateKey); + return cert_simpl.sign(privateKey, hash_algorithm); }, function(error) { @@ -668,21 +676,18 @@ // #region Put information about signature algorithm var algomap = { - "1.2.840.113549.2.1": "MD2", "1.2.840.113549.1.1.2": "MD2 with RSA", - "1.2.840.113549.2.5": "MD5", "1.2.840.113549.1.1.4": "MD5 with RSA", - "1.3.14.3.2.26": "SHA1", "1.2.840.10040.4.3": "SHA1 with DSA", "1.2.840.10045.4.1": "SHA1 with ECDSA", + "1.2.840.10045.4.3.2": "SHA256 with ECDSA", + "1.2.840.10045.4.3.3": "SHA384 with ECDSA", + "1.2.840.10045.4.3.4": "SHA512 with ECDSA", + "1.2.840.113549.1.1.10": "RSA-PSS", "1.2.840.113549.1.1.5": "SHA1 with RSA", - "2.16.840.1.101.3.4.2.4": "SHA224", "1.2.840.113549.1.1.14": "SHA224 with RSA", - "2.16.840.1.101.3.4.2.1": "SHA256", "1.2.840.113549.1.1.11": "SHA256 with RSA", - "2.16.840.1.101.3.4.2.2": "SHA384", "1.2.840.113549.1.1.12": "SHA384 with RSA", - "2.16.840.1.101.3.4.2.3": "SHA512", "1.2.840.113549.1.1.13": "SHA512 with RSA" }; // array mapping of common algorithm OIDs and corresponding types @@ -905,6 +910,14 @@

+

+ + +

Create diff --git a/examples/Create new CMS Signed data/CreateCMSSignedData.html b/examples/Create new CMS Signed data/CreateCMSSignedData.html index 3aa973609..2f9ea2c24 100644 --- a/examples/Create new CMS Signed data/CreateCMSSignedData.html +++ b/examples/Create new CMS Signed data/CreateCMSSignedData.html @@ -86,32 +86,37 @@ var publicKey; var privateKey; - var hash_algorithm = "sha-1"; - var hash_algorithm_oid = "1.3.14.3.2.26"; - var signature_algorithm = "1.2.840.113549.1.1.5"; - + var hash_algorithm; var hash_option = document.getElementById("hash_alg").value; switch(hash_option) { case "alg_SHA1": hash_algorithm = "sha-1"; - hash_algorithm_oid = "1.3.14.3.2.26"; - signature_algorithm = "1.2.840.113549.1.1.5"; break; case "alg_SHA256": hash_algorithm = "sha-256"; - hash_algorithm_oid = "2.16.840.1.101.3.4.2.1"; - signature_algorithm = "1.2.840.113549.1.1.11"; break; case "alg_SHA384": hash_algorithm = "sha-384"; - hash_algorithm_oid = "2.16.840.1.101.3.4.2.2"; - signature_algorithm = "1.2.840.113549.1.1.12"; break; case "alg_SHA512": hash_algorithm = "sha-512"; - hash_algorithm_oid = "2.16.840.1.101.3.4.2.3"; - signature_algorithm = "1.2.840.113549.1.1.13"; + break; + default:; + } + + var signature_algorithm_name; + var sign_option = document.getElementById("sign_alg").value; + switch(sign_option) + { + case "alg_RSA15": + signature_algorithm_name = "RSASSA-PKCS1-V1_5"; + break; + case "alg_RSA2": + signature_algorithm_name = "RSA-PSS"; + break; + case "alg_ECDSA": + signature_algorithm_name = "ECDSA"; break; default:; } @@ -126,7 +131,7 @@ } // #endregion - // #region Put a static values + // #region Put a static values into certificate "helper" object cert_simpl.version = 2; cert_simpl.serialNumber = new org.pkijs.asn1.INTEGER({ value: 1 }); cert_simpl.issuer.types_and_values.push(new org.pkijs.simpl.ATTR_TYPE_AND_VALUE({ @@ -151,26 +156,11 @@ cert_simpl.extensions = new Array(); // Extensions are not a part of certificate by default, it's an optional array - // #region "BasicConstraints" extension - var basic_constr = new org.pkijs.simpl.x509.BasicConstraints({ - cA: true, - pathLenConstraint: 3 - }); - - cert_simpl.extensions.push(new org.pkijs.simpl.EXTENSION({ - extnID: "2.5.29.19", - critical: false, - extnValue: basic_constr.toSchema().toBER(false), - parsedValue: basic_constr // Parsed value for well-known extensions - })); - // #endregion - // #region "KeyUsage" extension var bit_array = new ArrayBuffer(1); var bit_view = new Uint8Array(bit_array); bit_view[0] = bit_view[0] | 0x02; // Key usage "cRLSign" flag - bit_view[0] = bit_view[0] | 0x04; // Key usage "keyCertSign" flag var key_usage = new org.pkijs.asn1.BITSTRING({ value_hex: bit_array }); @@ -181,16 +171,19 @@ parsedValue: key_usage // Parsed value for well-known extensions })); // #endregion - - cert_simpl.signatureAlgorithm.algorithm_id = signature_algorithm; - cert_simpl.signature.algorithm_id = cert_simpl.signatureAlgorithm.algorithm_id; // Must be the same value // #endregion // #region Create a new key pair sequence = sequence.then( function() { - return crypto.generateKey({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: hash_algorithm } }, true, ["sign", "verify"]); + // #region Get default algorithm parameters for key generation + var algorithm = org.pkijs.getAlgorithmParameters(signature_algorithm_name, "generatekey"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = hash_algorithm; + // #endregion + + return crypto.generateKey(algorithm.algorithm, true, algorithm.usages); } ); // #endregion @@ -222,7 +215,7 @@ sequence = sequence.then( function() { - return cert_simpl.sign(privateKey); + return cert_simpl.sign(privateKey, hash_algorithm); }, function(error) { @@ -336,12 +329,6 @@ { cms_signed_simpl = new org.pkijs.simpl.CMS_SIGNED_DATA({ version: 1, - digestAlgorithms: [ - new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ - algorithm_id: hash_algorithm_oid, - algorithm_params: new org.pkijs.asn1.NULL() - }) // SHA-1 - ], encapContentInfo: new org.pkijs.simpl.cms.EncapsulatedContentInfo({ eContentType: "1.2.840.113549.1.7.1", // "data" content type eContent: new org.pkijs.asn1.OCTETSTRING({ value_hex: buffer }) @@ -352,15 +339,7 @@ sid: new org.pkijs.simpl.cms.IssuerAndSerialNumber({ issuer: cert_simpl.issuer, serialNumber: cert_simpl.serialNumber - }), - digestAlgorithm: new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ - algorithm_id: hash_algorithm_oid, - algorithm_params: new org.pkijs.asn1.NULL() - }), // SHA-1 - signatureAlgorithm: new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ - algorithm_id: "1.2.840.113549.1.1.1", - algorithm_params: new org.pkijs.asn1.NULL() - }), // RSA (PKCS #1 v1.5) key transport algorithm + }) }) ], certificates: [cert_simpl] @@ -374,7 +353,7 @@ }); } - return cms_signed_simpl.sign(privateKey, 0); + return cms_signed_simpl.sign(privateKey, 0, hash_algorithm); } ); // #endregion @@ -385,7 +364,7 @@ var cms_signed_schema = cms_signed_simpl.toSchema(true); var cms_content_simp = new org.pkijs.simpl.CMS_CONTENT_INFO({ - contentType: "1.2.840.113549.1.7.2", + contentType: "1.2.840.113549.1.7.2", // CMS Signed Data content: cms_signed_schema }); @@ -470,6 +449,14 @@

+

+ + +

diff --git a/examples/Create new X.509 certificate/CreateNewX509Certificate.html b/examples/Create new X.509 certificate/CreateNewX509Certificate.html index 7bd2df7d2..993caa799 100644 --- a/examples/Create new X.509 certificate/CreateNewX509Certificate.html +++ b/examples/Create new X.509 certificate/CreateNewX509Certificate.html @@ -1,35 +1,4 @@ - - + @@ -179,16 +148,19 @@ parsedValue: key_usage // Parsed value for well-known extensions })); // #endregion - - cert_simpl.signatureAlgorithm.algorithm_id = "1.2.840.113549.1.1.5"; // RSA + SHA-1 - cert_simpl.signature.algorithm_id = cert_simpl.signatureAlgorithm.algorithm_id; // Must be the same value // #endregion // #region Create a new key pair sequence = sequence.then( function() { - return crypto.generateKey({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: "sha-1" } }, true, ["encrypt", "decrypt", "sign", "verify"]); + // #region Get default algorithm parameters for key generation + var algorithm = org.pkijs.getAlgorithmParameters("RSASSA-PKCS1-v1_5", "generatekey"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = "SHA-1"; + // #endregion + + return crypto.generateKey(algorithm.algorithm, true, algorithm.usages); } ); // #endregion @@ -220,7 +192,7 @@ sequence = sequence.then( function() { - return cert_simpl.sign(privateKey); + return cert_simpl.sign(privateKey, "SHA-1"); }, function(error) { diff --git a/examples/OCSP response complex example/OCSP_resp_complex_example.html b/examples/OCSP response complex example/OCSP_resp_complex_example.html index f899d6dc2..ca0f6bb63 100644 --- a/examples/OCSP response complex example/OCSP_resp_complex_example.html +++ b/examples/OCSP response complex example/OCSP_resp_complex_example.html @@ -237,32 +237,37 @@ var publicKey; var privateKey; - var hash_algorithm = "sha-1"; - var hash_algorithm_oid = "1.3.14.3.2.26"; - var signature_algorithm = "1.2.840.113549.1.1.5"; - + var hash_algorithm; var hash_option = document.getElementById("hash_alg").value; switch(hash_option) { case "alg_SHA1": hash_algorithm = "sha-1"; - hash_algorithm_oid = "1.3.14.3.2.26"; - signature_algorithm = "1.2.840.113549.1.1.5"; break; case "alg_SHA256": hash_algorithm = "sha-256"; - hash_algorithm_oid = "2.16.840.1.101.3.4.2.1"; - signature_algorithm = "1.2.840.113549.1.1.11"; break; case "alg_SHA384": hash_algorithm = "sha-384"; - hash_algorithm_oid = "2.16.840.1.101.3.4.2.2"; - signature_algorithm = "1.2.840.113549.1.1.12"; break; case "alg_SHA512": hash_algorithm = "sha-512"; - hash_algorithm_oid = "2.16.840.1.101.3.4.2.3"; - signature_algorithm = "1.2.840.113549.1.1.13"; + break; + default:; + } + + var signature_algorithm_name; + var sign_option = document.getElementById("sign_alg").value; + switch(sign_option) + { + case "alg_RSA15": + signature_algorithm_name = "RSASSA-PKCS1-V1_5"; + break; + case "alg_RSA2": + signature_algorithm_name = "RSA-PSS"; + break; + case "alg_ECDSA": + signature_algorithm_name = "ECDSA"; break; default:; } @@ -332,16 +337,19 @@ parsedValue: key_usage // Parsed value for well-known extensions })); // #endregion - - cert_simpl.signatureAlgorithm.algorithm_id = signature_algorithm; - cert_simpl.signature.algorithm_id = cert_simpl.signatureAlgorithm.algorithm_id; // Must be the same value // #endregion // #region Create a new key pair sequence = sequence.then( function() { - return crypto.generateKey({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: hash_algorithm } }, true, ["sign", "verify"]); + // #region Get default algorithm parameters for key generation + var algorithm = org.pkijs.getAlgorithmParameters(signature_algorithm_name, "generatekey"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = hash_algorithm; + // #endregion + + return crypto.generateKey(algorithm.algorithm, true, algorithm.usages); } ); // #endregion @@ -373,7 +381,7 @@ sequence = sequence.then( function() { - return cert_simpl.sign(privateKey); + return cert_simpl.sign(privateKey, hash_algorithm); }, function(error) { @@ -468,10 +476,9 @@ ocsp_basic_resp.tbsResponseData.responses.push(response); - ocsp_basic_resp.signatureAlgorithm.algorithm_id = "1.2.840.113549.1.1.5"; // RSA + SHA-1 ocsp_basic_resp.certs = [cert_simpl]; - return ocsp_basic_resp.sign(privateKey); + return ocsp_basic_resp.sign(privateKey, hash_algorithm); } ); // #endregion @@ -908,6 +915,14 @@

+

+ + +

Create diff --git a/examples/PKCS#10 complex example/PKCS10_complex_example.html b/examples/PKCS#10 complex example/PKCS10_complex_example.html index 1b11b1228..645c37760 100644 --- a/examples/PKCS#10 complex example/PKCS10_complex_example.html +++ b/examples/PKCS#10 complex example/PKCS10_complex_example.html @@ -178,6 +178,41 @@ var publicKey; var privateKey; + + var hash_algorithm; + var hash_option = document.getElementById("hash_alg").value; + switch(hash_option) + { + case "alg_SHA1": + hash_algorithm = "sha-1"; + break; + case "alg_SHA256": + hash_algorithm = "sha-256"; + break; + case "alg_SHA384": + hash_algorithm = "sha-384"; + break; + case "alg_SHA512": + hash_algorithm = "sha-512"; + break; + default:; + } + + var signature_algorithm_name; + var sign_option = document.getElementById("sign_alg").value; + switch(sign_option) + { + case "alg_RSA15": + signature_algorithm_name = "RSASSA-PKCS1-V1_5"; + break; + case "alg_RSA2": + signature_algorithm_name = "RSA-PSS"; + break; + case "alg_ECDSA": + signature_algorithm_name = "ECDSA"; + break; + default:; + } // #endregion // #region Get a "crypto" extension @@ -193,16 +228,19 @@ pkcs10_simpl.version = 0; pkcs10_simpl.subject.types_and_values.push(new org.pkijs.simpl.ATTR_TYPE_AND_VALUE({ type: "2.5.4.6", value: new org.pkijs.asn1.PRINTABLESTRING({ value: "RU" }) })); pkcs10_simpl.subject.types_and_values.push(new org.pkijs.simpl.ATTR_TYPE_AND_VALUE({ type: "2.5.4.3", value: new org.pkijs.asn1.UTF8STRING({ value: "Simple test (простой тест)" }) })); - - pkcs10_simpl.signatureAlgorithm.algorithm_id = "1.2.840.113549.1.1.5"; // RSA + SHA-1 // #endregion // #region Create a new key pair sequence = sequence.then( function() { - return crypto.generateKey({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: "sha-1" } }, true, ["sign", "verify"]); + // #region Get default algorithm parameters for key generation + var algorithm = org.pkijs.getAlgorithmParameters(signature_algorithm_name, "generatekey"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = hash_algorithm; + // #endregion + return crypto.generateKey(algorithm.algorithm, true, algorithm.usages); } ); // #endregion @@ -234,7 +272,7 @@ sequence = sequence.then( function() { - return pkcs10_simpl.sign(privateKey); + return pkcs10_simpl.sign(privateKey, hash_algorithm); }, function(error) { @@ -314,42 +352,50 @@ // #endregion // #region Put information about public key size - var asn1_publicKey = org.pkijs.fromBER(pkcs10_simpl.subjectPublicKeyInfo.subjectPublicKey.value_block.value_hex), - rsa_publicKey_simple = new org.pkijs.simpl.x509.RSAPublicKey({ schema: asn1_publicKey.result }), - modulus_view = new Uint8Array(rsa_publicKey_simple.modulus.value_block.value_hex), - modulus_bit_length = 0; + var publicKeySize = "< unknown >"; - if(modulus_view[0] === 0x00) - modulus_bit_length = (rsa_publicKey_simple.modulus.value_block.value_hex.byteLength - 1) * 8; - else - modulus_bit_length = rsa_publicKey_simple.modulus.value_block.value_hex.byteLength * 8; + if(pkcs10_simpl.subjectPublicKeyInfo.algorithm.algorithm_id.indexOf("1.2.840.113549") !== (-1)) + { + var asn1_publicKey = org.pkijs.fromBER(pkcs10_simpl.subjectPublicKeyInfo.subjectPublicKey.value_block.value_hex); + var rsa_publicKey_simple = new org.pkijs.simpl.x509.RSAPublicKey({ schema: asn1_publicKey.result }); + var modulus_view = new Uint8Array(rsa_publicKey_simple.modulus.value_block.value_hex); + var modulus_bit_length = 0; + + if(modulus_view[0] === 0x00) + modulus_bit_length = (rsa_publicKey_simple.modulus.value_block.value_hex.byteLength - 1) * 8; + else + modulus_bit_length = rsa_publicKey_simple.modulus.value_block.value_hex.byteLength * 8; - var publicExponent_bit_length = rsa_publicKey_simple.publicExponent.value_block.value_hex.byteLength * 8; + publicKeySize = modulus_bit_length.toString(); + } - document.getElementById("keysize").innerHTML = modulus_bit_length; + document.getElementById("keysize").innerHTML = publicKeySize; // #endregion // #region Put information about signature algorithm var algomap = { - "1.2.840.113549.2.1": "MD2", "1.2.840.113549.1.1.2": "MD2 with RSA", - "1.2.840.113549.2.5": "MD5", "1.2.840.113549.1.1.4": "MD5 with RSA", - "1.3.14.3.2.26": "SHA1", "1.2.840.10040.4.3": "SHA1 with DSA", "1.2.840.10045.4.1": "SHA1 with ECDSA", + "1.2.840.10045.4.3.2": "SHA256 with ECDSA", + "1.2.840.10045.4.3.3": "SHA384 with ECDSA", + "1.2.840.10045.4.3.4": "SHA512 with ECDSA", + "1.2.840.113549.1.1.10": "RSA-PSS", "1.2.840.113549.1.1.5": "SHA1 with RSA", - "2.16.840.1.101.3.4.2.4": "SHA224", "1.2.840.113549.1.1.14": "SHA224 with RSA", - "2.16.840.1.101.3.4.2.1": "SHA256", "1.2.840.113549.1.1.11": "SHA256 with RSA", - "2.16.840.1.101.3.4.2.2": "SHA384", "1.2.840.113549.1.1.12": "SHA384 with RSA", - "2.16.840.1.101.3.4.2.3": "SHA512", "1.2.840.113549.1.1.13": "SHA512 with RSA" }; - document.getElementById("sig-algo").innerHTML = algomap[pkcs10_simpl.signatureAlgorithm.algorithm_id]; + var signatureAlgorithm = algomap[pkcs10_simpl.signatureAlgorithm.algorithm_id]; + if(typeof signatureAlgorithm === "undefined") + signatureAlgorithm = pkcs10_simpl.signatureAlgorithm.algorithm_id; + else + signatureAlgorithm = signatureAlgorithm + " (" + pkcs10_simpl.signatureAlgorithm.algorithm_id + ")"; + + document.getElementById("sig-algo").innerHTML = signatureAlgorithm; // #endregion // #region Put information about PKCS#10 attributes @@ -430,6 +476,23 @@
+

+ + +

+

+ + +

Create diff --git a/examples/TSP response complex example/TSP_resp_complex_example.html b/examples/TSP response complex example/TSP_resp_complex_example.html index 9fd230bea..8f1321ae1 100644 --- a/examples/TSP response complex example/TSP_resp_complex_example.html +++ b/examples/TSP response complex example/TSP_resp_complex_example.html @@ -234,32 +234,37 @@ var publicKey; var privateKey; - var hash_algorithm = "sha-1"; - var hash_algorithm_oid = "1.3.14.3.2.26"; - var signature_algorithm = "1.2.840.113549.1.1.5"; - + var hash_algorithm; var hash_option = document.getElementById("hash_alg").value; switch(hash_option) { case "alg_SHA1": hash_algorithm = "sha-1"; - hash_algorithm_oid = "1.3.14.3.2.26"; - signature_algorithm = "1.2.840.113549.1.1.5"; break; case "alg_SHA256": hash_algorithm = "sha-256"; - hash_algorithm_oid = "2.16.840.1.101.3.4.2.1"; - signature_algorithm = "1.2.840.113549.1.1.11"; break; case "alg_SHA384": hash_algorithm = "sha-384"; - hash_algorithm_oid = "2.16.840.1.101.3.4.2.2"; - signature_algorithm = "1.2.840.113549.1.1.12"; break; case "alg_SHA512": hash_algorithm = "sha-512"; - hash_algorithm_oid = "2.16.840.1.101.3.4.2.3"; - signature_algorithm = "1.2.840.113549.1.1.13"; + break; + default:; + } + + var signature_algorithm_name; + var sign_option = document.getElementById("sign_alg").value; + switch(sign_option) + { + case "alg_RSA15": + signature_algorithm_name = "RSASSA-PKCS1-V1_5"; + break; + case "alg_RSA2": + signature_algorithm_name = "RSA-PSS"; + break; + case "alg_ECDSA": + signature_algorithm_name = "ECDSA"; break; default:; } @@ -329,16 +334,19 @@ parsedValue: key_usage // Parsed value for well-known extensions })); // #endregion - - cert_simpl.signatureAlgorithm.algorithm_id = signature_algorithm; - cert_simpl.signature.algorithm_id = cert_simpl.signatureAlgorithm.algorithm_id; // Must be the same value // #endregion // #region Create a new key pair sequence = sequence.then( function() { - return crypto.generateKey({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: hash_algorithm } }, true, ["sign", "verify"]); + // #region Get default algorithm parameters for key generation + var algorithm = org.pkijs.getAlgorithmParameters(signature_algorithm_name, "generatekey"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = hash_algorithm; + // #endregion + + return crypto.generateKey(algorithm.algorithm, true, algorithm.usages); } ); // #endregion @@ -370,7 +378,7 @@ sequence = sequence.then( function() { - return cert_simpl.sign(privateKey); + return cert_simpl.sign(privateKey, hash_algorithm); }, function(error) { @@ -478,12 +486,6 @@ cms_signed_simpl = new org.pkijs.simpl.CMS_SIGNED_DATA({ version: 3, - digestAlgorithms: [ - new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ - algorithm_id: hash_algorithm_oid, - algorithm_params: new org.pkijs.asn1.NULL() - }) // SHA-1 - ], encapContentInfo: encapContent, signerInfos: [ new org.pkijs.simpl.CMS_SIGNER_INFO({ @@ -491,21 +493,13 @@ sid: new org.pkijs.simpl.cms.IssuerAndSerialNumber({ issuer: cert_simpl.issuer, serialNumber: cert_simpl.serialNumber - }), - digestAlgorithm: new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ - algorithm_id: hash_algorithm_oid, - algorithm_params: new org.pkijs.asn1.NULL() - }), // SHA-1 - signatureAlgorithm: new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ - algorithm_id: "1.2.840.113549.1.1.1", - algorithm_params: new org.pkijs.asn1.NULL() - }), // RSA (PKCS #1 v1.5) key transport algorithm + }) }) ], certificates: [cert_simpl] }); - return cms_signed_simpl.sign(privateKey, 0); + return cms_signed_simpl.sign(privateKey, 0, hash_algorithm); } ); // #endregion @@ -963,6 +957,14 @@

+

+ + +

Create diff --git a/examples/Use WebCrypto/HowToUseWebCrypto.js b/examples/Use WebCrypto/HowToUseWebCrypto.js index 94bb7d0ec..fb8292526 100644 --- a/examples/Use WebCrypto/HowToUseWebCrypto.js +++ b/examples/Use WebCrypto/HowToUseWebCrypto.js @@ -58,9 +58,9 @@ function test_web_crypto() var crypto = window.crypto.subtle; // #endregion - crypto.generateKey({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: "sha-1" }}, + crypto.generateKey({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: { name: "sha-1" } }, true, - ["encrypt", "decrypt", "sign", "verify"]). + ["sign", "verify"]). then( function(result) // result of generating key pair { @@ -68,11 +68,20 @@ function test_web_crypto() privateKey = result.privateKey; return crypto.exportKey("spki", publicKey); - }). + }, + function(error) + { + console.log("ERROR #1: " + error); + } + ). then( function(result) // result of exporting public key { - return crypto.importKey("spki", new Uint8Array(result), { name: "RSASSA-PKCS1-v1_5", hash: { name: "sha-1" } }, true, ["sign", "verify"]); + return crypto.importKey("spki", new Uint8Array(result), { name: "RSASSA-PKCS1-v1_5", hash: { name: "sha-1" } }, true, ["verify"]); + }, + function(error) + { + console.log("ERROR #2: " + error); } ). then( @@ -80,12 +89,20 @@ function test_web_crypto() { publicKey = result; return crypto.exportKey("pkcs8", privateKey); + }, + function(error) + { + console.log("ERROR #3: " + error); } ). then( function(result) // result of exporting private key { - return crypto.importKey("pkcs8", new Uint8Array(result), { name: "RSASSA-PKCS1-v1_5", hash: { name: "sha-1" } }, true, ["sign", "verify"]); + return crypto.importKey("pkcs8", new Uint8Array(result), { name: "RSASSA-PKCS1-v1_5", hash: { name: "sha-1" } }, true, ["sign"]); + }, + function(error) + { + console.log("ERROR #4: " + error); } ). then( @@ -93,18 +110,30 @@ function test_web_crypto() { privateKey = result; return crypto.sign({ name: "RSASSA-PKCS1-v1_5", hash: { name: "sha-512" } }, result, value_hex_view); + }, + function(error) + { + console.log("ERROR #5: " + error); } ). then( function(result) // result of signing test data { return crypto.verify({ name: "RSASSA-PKCS1-v1_5", hash: { name: "sha-512" } }, publicKey, new Uint8Array(result), value_hex_view); + }, + function(error) + { + console.log("ERROR #6: " + error); } ). then( function(result) // result of verifying test signature { alert("Message verified: " + result); + }, + function(error) + { + console.log("ERROR #7: " + error); } ); } diff --git a/org/pkijs/cms_schema.js b/org/pkijs/cms_schema.js index c4e764e38..4d8504c1e 100644 --- a/org/pkijs/cms_schema.js +++ b/org/pkijs/cms_schema.js @@ -286,6 +286,52 @@ function(in_window) //************************************************************************************** // #endregion //************************************************************************************** + // #region ASN.1 schema definition for "RSAES-OAEP-params" type (RFC3447) + //************************************************************************************** + in_window.org.pkijs.schema.x509.RSAES_OAEP_params = + function() + { + //RSAES-OAEP-params ::= SEQUENCE { + // hashAlgorithm [0] HashAlgorithm DEFAULT sha1, + // maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1, + // pSourceAlgorithm [2] PSourceAlgorithm DEFAULT pSpecifiedEmpty + //} + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + optional: true, + value: [in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.hashAlgorithm || {})] + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + optional: true, + value: [in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.maskGenAlgorithm || {})] + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 2 // [2] + }, + optional: true, + value: [in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.pSourceAlgorithm || {})] + }) + ] + })); + } + //************************************************************************************** + // #endregion + //************************************************************************************** // #region ASN.1 schema for CMS "SignedAttributes" and "UnsignedAttributes" types //************************************************************************************** in_window.org.pkijs.schema.cms.SignedUnsignedAttributes = @@ -964,13 +1010,30 @@ function(in_window) value: [ new in_window.org.pkijs.asn1.OID({ name: (names.contentType || "") }), in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.contentEncryptionAlgorithm || {}), - new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ - name: (names.encryptedContent || ""), - id_block: { - tag_class: 3, // CONTEXT-SPECIFIC - tag_number: 0 // [0] - }, - value: [new in_window.org.pkijs.asn1.OCTETSTRING()] + // The CHOICE we need because "EncryptedContent" could have either "constructive" + // or "primitive" form of encoding and we need to handle both variants + new in_window.org.pkijs.asn1.CHOICE({ + value: [ + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + name: (names.encryptedContent || ""), + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [ + new in_window.org.pkijs.asn1.REPEATED({ + value: new in_window.org.pkijs.asn1.OCTETSTRING() + }) + ] + }), + new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({ + name: (names.encryptedContent || ""), + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + } + }) + ] }) ] })); diff --git a/org/pkijs/cms_simpl.js b/org/pkijs/cms_simpl.js index 34eac7b62..46921127a 100644 --- a/org/pkijs/cms_simpl.js +++ b/org/pkijs/cms_simpl.js @@ -638,6 +638,142 @@ function(in_window) //************************************************************************************** // #endregion //************************************************************************************** + // #region Simplified structure for "RSAES_OAEP_params" type (RFC3447) + //************************************************************************************** + in_window.org.pkijs.simpl.x509.RSAES_OAEP_params = + function() + { + // #region Internal properties of the object + // OPTIONAL this.hashAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); + // OPTIONAL this.maskGenAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); + // OPTIONAL this.pSourceAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.RSAES_OAEP_params.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + if("hashAlgorithm" in arguments[0]) + this.hashAlgorithm = arguments[0].hashAlgorithm; + + if("maskGenAlgorithm" in arguments[0]) + this.maskGenAlgorithm = arguments[0].maskGenAlgorithm; + + if("pSourceAlgorithm" in arguments[0]) + this.pSourceAlgorithm = arguments[0].pSourceAlgorithm; + } + } + // #endregion + } + //************************************************************************************** + in_window.org.pkijs.simpl.x509.RSAES_OAEP_params.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.RSAES_OAEP_params({ + names: { + hashAlgorithm: { + names: { + block_name: "hashAlgorithm" + } + }, + maskGenAlgorithm: { + names: { + block_name: "maskGenAlgorithm" + } + }, + pSourceAlgorithm: { + names: { + block_name: "pSourceAlgorithm" + } + } + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for RSAES_OAEP_params"); + // #endregion + + // #region Get internal properties from parsed schema + if("hashAlgorithm" in asn1.result) + this.hashAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["hashAlgorithm"] }); + + if("maskGenAlgorithm" in asn1.result) + this.maskGenAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["maskGenAlgorithm"] }); + + if("pSourceAlgorithm" in asn1.result) + this.pSourceAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["pSourceAlgorithm"] }); + // #endregion + } + //************************************************************************************** + in_window.org.pkijs.simpl.x509.RSAES_OAEP_params.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + if("hashAlgorithm" in this) + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [this.hashAlgorithm.toSchema()] + })); + + if("maskGenAlgorithm" in this) + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + value: [this.maskGenAlgorithm.toSchema()] + })); + + if("pSourceAlgorithm" in this) + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 2 // [2] + }, + value: [this.pSourceAlgorithm.toSchema()] + })); + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + } + //************************************************************************************** + in_window.org.pkijs.simpl.x509.RSAES_OAEP_params.prototype.toJSON = + function() + { + var _object = {}; + + if("hashAlgorithm" in this) + _object.hashAlgorithm = this.hashAlgorithm.toJSON(); + + if("maskGenAlgorithm" in this) + _object.maskGenAlgorithm = this.maskGenAlgorithm.toJSON(); + + if("pSourceAlgorithm" in this) + _object.pSourceAlgorithm = this.pSourceAlgorithm.toJSON(); + + return _object; + } + //************************************************************************************** + // #endregion + //************************************************************************************** // #region Simplified structure for "SignedUnsignedAttributes" type //************************************************************************************** in_window.org.pkijs.simpl.cms.SignedUnsignedAttributes = @@ -1360,23 +1496,9 @@ function(in_window) sequence = sequence.then( function() { - switch(signerInfos[signerIndex].digestAlgorithm.algorithm_id) - { - case "1.3.14.3.2.26": - sha_algorithm = "sha-1"; - break; - case "2.16.840.1.101.3.4.2.1": - sha_algorithm = "sha-256"; - break; - case "2.16.840.1.101.3.4.2.2": - sha_algorithm = "sha-384"; - break; - case "2.16.840.1.101.3.4.2.3": - sha_algorithm = "sha-512"; - break; - default: - return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + _this.signerInfos[signerIndex].digestAlgorithm.algorithm_id); }); - } + sha_algorithm = in_window.org.pkijs.getHashAlgorithmByOID(signerInfos[signerIndex].digestAlgorithm.algorithm_id); + if(sha_algorithm === "") + return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + _this.signerInfos[signerIndex].digestAlgorithm.algorithm_id); }); return new Promise(function(resolve, reject) { resolve(); }); } @@ -1421,11 +1543,21 @@ function(in_window) sequence = sequence.then( function() { + // #region Get information about public key algorithm and default parameters for import + var algorithm_name = in_window.org.pkijs.getAlgorithmNameBySignature(signer_cert.signatureAlgorithm.algorithm_id); + if(algorithm_name === "") + return new Promise(function(resolve, reject) { reject("Unsupported public key algorithm: " + signer_cert.signatureAlgorithm.algorithm_id); }); + + var algorithm = in_window.org.pkijs.getAlgorithmParameters(algorithm_name, "importkey"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = sha_algorithm; + // #endregion + var publicKeyInfo_schema = signer_cert.subjectPublicKeyInfo.toSchema(); var publicKeyInfo_buffer = publicKeyInfo_schema.toBER(false); var publicKeyInfo_view = new Uint8Array(publicKeyInfo_buffer); - return crypto.importKey("spki", publicKeyInfo_view, { name: "RSASSA-PKCS1-v1_5", hash: { name: sha_algorithm } }, true, ["verify"]); + return crypto.importKey("spki", publicKeyInfo_view, algorithm.algorithm, true, algorithm.usages); } ); // #endregion @@ -1434,10 +1566,62 @@ function(in_window) sequence = sequence.then( function(publicKey) { - return crypto.verify({ name: publicKey.algorithm.name, hash: { name: sha_algorithm } }, + // #region Get default algorithm parameters for verification + var algorithm = in_window.org.pkijs.getAlgorithmParameters(publicKey.algorithm.name, "verify"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = sha_algorithm; + // #endregion + + // #region Special case for ECDSA signatures + var signature_value = signerInfos[signerIndex].signature.value_block.value_hex; + + if(publicKey.algorithm.name === "ECDSA") + { + var asn1 = in_window.org.pkijs.fromBER(signature_value); + signature_value = in_window.org.pkijs.createECDSASignatureFromCMS(asn1.result); + } + // #endregion + + // #region Special case for RSA-PSS + if(publicKey.algorithm.name === "RSA-PSS") + { + var pssParameters; + + try + { + pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params({ schema: signerInfos[signerIndex].signatureAlgorithm.algorithm_params }); + } + catch(ex) + { + return new Promise(function(resolve, reject) { reject(ex); }); + } + + if("saltLength" in pssParameters) + algorithm.algorithm.saltLength = pssParameters.saltLength; + else + algorithm.algorithm.saltLength = 20; + + var hash_algo = "SHA-1"; + + if("hashAlgorithm" in pssParameters) + { + hash_algo = in_window.org.pkijs.getHashAlgorithmByOID(pssParameters.hashAlgorithm.algorithm_id); + if(hash_algo === "") + return new Promise(function(resolve, reject) { reject("Unrecognized hash algorithm: " + pssParameters.hashAlgorithm.algorithm_id); }); + } + + algorithm.algorithm.hash.name = hash_algo; + } + // #endregion + + return crypto.verify(algorithm.algorithm, publicKey, - new Uint8Array(signerInfos[signerIndex].signature.value_block.value_hex), + new Uint8Array(signature_value), new Uint8Array(data)); + }, + function(error) + { + return new Promise(function(resolve, reject) { reject(error); }); } ); // #endregion @@ -1446,15 +1630,16 @@ function(in_window) } //************************************************************************************** in_window.org.pkijs.simpl.CMS_SIGNED_DATA.prototype.sign = - function(privateKey, signerIndex) + function(privateKey, signerIndex, hashAlgorithm) { /// Private key for "subjectPublicKeyInfo" structure /// Index number (starting from 0) of signer index to make signature for + /// Hashing algorithm. Default SHA-1 // #region Initial variables var _this = this; var data = new ArrayBuffer(0); - var sha_algorithm = ""; + var hashAlgorithmOID = ""; // #endregion // #region Get a private key from function parameter @@ -1462,23 +1647,109 @@ function(in_window) return new Promise(function(resolve, reject) { reject("Need to provide a private key for signing"); }); // #endregion - // #region Find a correct hashing algorithm - switch(_this.signerInfos[signerIndex].digestAlgorithm.algorithm_id) + // #region Get hashing algorithm + if(typeof hashAlgorithm === "undefined") + hashAlgorithm = "SHA-1"; + + // #region Simple check for supported algorithm + hashAlgorithmOID = in_window.org.pkijs.getHashAlgorithmOID(hashAlgorithm); + if(hashAlgorithmOID === "") + return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); }); + // #endregion + // #endregion + + // #region Append information about hash algorithm + var found = false; + + for(var i = 0; i < _this.digestAlgorithms.length; i++) { - case "1.3.14.3.2.26": - sha_algorithm = "sha-1"; - break; - case "2.16.840.1.101.3.4.2.1": - sha_algorithm = "sha-256"; + if(_this.digestAlgorithms[i].algorithm_id === hashAlgorithmOID) + { + found = true; break; - case "2.16.840.1.101.3.4.2.2": - sha_algorithm = "sha-384"; + } + } + + if(found === false) + { + _this.digestAlgorithms.push(new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: hashAlgorithmOID, + algorithm_params: new org.pkijs.asn1.NULL() + })); + } + + _this.signerInfos[signerIndex].digestAlgorithm = new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: hashAlgorithmOID, + algorithm_params: new org.pkijs.asn1.NULL() + }); + // #endregion + + // #region Get a "default parameters" for current algorithm + var defParams = in_window.org.pkijs.getAlgorithmParameters(privateKey.algorithm.name, "sign"); + defParams.algorithm.hash.name = hashAlgorithm; + // #endregion + + // #region Fill internal structures base on "privateKey" and "hashAlgorithm" + switch(privateKey.algorithm.name.toUpperCase()) + { + case "RSASSA-PKCS1-V1_5": + case "ECDSA": + _this.signerInfos[signerIndex].signatureAlgorithm.algorithm_id = in_window.org.pkijs.getSignatureAlgorithm(defParams.algorithm); break; - case "2.16.840.1.101.3.4.2.3": - sha_algorithm = "sha-512"; + case "RSA-PSS": + { + // #region Set "saltLength" as a length (in octets) of hash function result + switch(hashAlgorithm.toUpperCase()) + { + case "SHA-256": + defParams.algorithm.saltLength = 32; + break; + case "SHA-384": + defParams.algorithm.saltLength = 48; + break; + case "SHA-512": + defParams.algorithm.saltLength = 64; + break; + default:; + } + // #endregion + + // #region Fill "RSASSA_PSS_params" object + var paramsObject = {}; + + if(hashAlgorithm.toUpperCase() !== "SHA-1") + { + hashAlgorithmOID = in_window.org.pkijs.getHashAlgorithmOID(hashAlgorithm); + if(hashAlgorithmOID === "") + return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); }); + + paramsObject.hashAlgorithm = new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: hashAlgorithmOID, + algorithm_params: new org.pkijs.asn1.NULL() + }); + + paramsObject.maskGenAlgorithm = new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: "1.2.840.113549.1.1.8", // MGF1 + algorithm_params: paramsObject.hashAlgorithm.toSchema() + }) + } + + if(defParams.algorithm.saltLength !== 20) + paramsObject.saltLength = defParams.algorithm.saltLength; + + var pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params(paramsObject); + // #endregion + + // #region Automatically set signature algorithm + _this.signerInfos[signerIndex].signatureAlgorithm = new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: "1.2.840.113549.1.1.10", + algorithm_params: pssParameters.toSchema() + }); + // #endregion + } break; default: - return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + this.signature.signatureAlgorithm.algorithm_id); }); + return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + privateKey.algorithm.name); }); } // #endregion @@ -1530,11 +1801,16 @@ function(in_window) // #endregion // #region Signing TBS data on provided private key - return crypto.sign({ name: privateKey.algorithm.name, hash: { name: sha_algorithm } }, + return crypto.sign(defParams.algorithm, privateKey, new Uint8Array(data)).then( function(result) { + // #region Special case for ECDSA algorithm + if(defParams.algorithm.name === "ECDSA") + result = in_window.org.pkijs.createCMSECDSASignature(result); + // #endregion + _this.signerInfos[signerIndex].signature = new in_window.org.pkijs.asn1.OCTETSTRING({ value_hex: result }); return new Promise(function(resolve, reject) { resolve(result); }); }, @@ -1693,12 +1969,7 @@ function(in_window) { // #region Internal properties of the object this.version = -1; - this.rid = { - toJSON: function() - { - return {}; - } - }; + this.rid = new in_window.org.pkijs.emptyObject(); this.keyEncryptionAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); this.encryptedKey = new in_window.org.pkijs.asn1.OCTETSTRING(); // #endregion @@ -1713,7 +1984,7 @@ function(in_window) if(arguments[0] instanceof Object) { this.version = arguments[0].version || -1; - this.rid = arguments[0].rid || {}; + this.rid = arguments[0].rid || new in_window.org.pkijs.emptyObject(); this.keyEncryptionAlgorithm = arguments[0].keyEncryptionAlgorithm || new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); this.encryptedKey = arguments[0].encryptedKey || new in_window.org.pkijs.asn1.OCTETSTRING(); } @@ -3168,7 +3439,7 @@ function(in_window) // #region Internal properties of the object this.contentType = ""; this.contentEncryptionAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); - // OPTIONAL this.encryptedContent + // OPTIONAL this.encryptedContent // new in_window.org.pkijs.asn1.OCTETSTRING - (!!!) could be contructive or primitive value (!!!) // #endregion // #region If input argument array contains "schema" for this object @@ -3184,7 +3455,43 @@ function(in_window) this.contentEncryptionAlgorithm = arguments[0].contentEncryptionAlgorithm || new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); if("encryptedContent" in arguments[0]) + { this.encryptedContent = arguments[0].encryptedContent; + + if((this.encryptedContent.id_block.tag_class === 1) && + (this.encryptedContent.id_block.tag_number === 4)) + { + // #region Divide OCTETSTRING value down to small pieces + if(this.encryptedContent.id_block.is_constructed === false) + { + var constr_string = new in_window.org.pkijs.asn1.OCTETSTRING({ + id_block: { is_constructed: true }, + is_constructed: true + }); + + var offset = 0; + var length = this.encryptedContent.value_block.value_hex.byteLength; + + while(length > 0) + { + var piece_view = new Uint8Array(this.encryptedContent.value_block.value_hex, offset, ((offset + 65536) > this.encryptedContent.value_block.value_hex.byteLength) ? (this.encryptedContent.value_block.value_hex.byteLength - offset) : 65536); + var _array = new ArrayBuffer(piece_view.length); + var _view = new Uint8Array(_array); + + for(var i = 0; i < _view.length; i++) + _view[i] = piece_view[i]; + + constr_string.value_block.value.push(new in_window.org.pkijs.asn1.OCTETSTRING({ value_hex: _array })); + + length -= piece_view.length; + offset += piece_view.length; + } + + this.encryptedContent = constr_string; + } + // #endregion + } + } } } // #endregion @@ -3218,7 +3525,12 @@ function(in_window) this.contentEncryptionAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["contentEncryptionAlgorithm"] }); if("encryptedContent" in asn1.result) - this.encryptedContent = asn1.result["encryptedContent"].value_block.value[0]; + { + this.encryptedContent = asn1.result["encryptedContent"]; + + this.encryptedContent.id_block.tag_class = 1; // UNIVERSAL + this.encryptedContent.id_block.tag_number = 4; // OCTETSTRING (!!!) The value still has instance of "in_window.org.pkijs.asn1.ASN1_CONSTRUCTED / ASN1_PRIMITIVE" + } // #endregion } //************************************************************************************** @@ -3232,13 +3544,14 @@ function(in_window) output_array.push(this.contentEncryptionAlgorithm.toSchema()); if("encryptedContent" in this) - output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ - id_block: { - tag_class: 3, // CONTEXT-SPECIFIC - tag_number: 0 // [0] - }, - value: [this.encryptedContent] - })); + { + var encryptedValue = this.encryptedContent; + + encryptedValue.id_block.tag_class = 3; // CONTEXT-SPECIFIC + encryptedValue.id_block.tag_number = 0; // [0] + + output_array.push(encryptedValue); + } // #endregion // #region Construct and return new ASN.1 schema for this object @@ -3437,6 +3750,48 @@ function(in_window) return _object; } //************************************************************************************** + in_window.org.pkijs.simpl.CMS_ENVELOPED_DATA.prototype.encrypt = + function() + { + /// Create a new CMS Enveloped Data content with encrypted data + + // #region Initial variables + var type; // 0 - KeyTransRecipientInfo, 1 - KeyAgreeRecipientInfo, 2 - KEKRecipientInfo, 3 - PasswordRecipientinfo + var typeInfo; + + // Input variables: + // ================= + // 1. Certificate for receiver; + // 2. Symmetric encryption algorithm; + // 3. "Key transport" algorithm; + // #endregion + + // #region Get input variable values + if(arguments[0] instanceof Object) + { + if("type" in arguments[0]) + { + type = arguments[0].type; + + if("typeInfo" in arguments[0]) + typeInfo = arguments[0].typeInfo; + else + return new Promise(function(resolve, reject) { reject("No \"typeInfo\" parameter provided to \"CMS_ENVELOPED_DATA.encrypt\" method"); }); + } + else + return new Promise(function(resolve, reject) { reject("No \"type\" parameter provided to \"CMS_ENVELOPED_DATA.encrypt\" method"); }); + } + else + return new Promise(function(resolve, reject) { reject("No input data provided to \"CMS_ENVELOPED_DATA.encrypt\" method"); }); + // #endregion + } + //************************************************************************************** + in_window.org.pkijs.simpl.CMS_ENVELOPED_DATA.prototype.decrypt = + function() + { + /// Decrypt existing CMS Enveloped Data content + } + //************************************************************************************** // #endregion //************************************************************************************** } diff --git a/org/pkijs/common.js b/org/pkijs/common.js index c69d99eac..2fd546820 100644 --- a/org/pkijs/common.js +++ b/org/pkijs/common.js @@ -64,6 +64,19 @@ function(in_window) //************************************************************************************** // #region Declaration of common functions //************************************************************************************** + in_window.org.pkijs.emptyObject = + function() + { + this.toJSON = function() + { + return {}; + }; + this.toSchema = function() + { + return {}; + }; + } + //************************************************************************************** in_window.org.pkijs.getNames = function(arg) { @@ -200,6 +213,859 @@ function(in_window) return result; } //************************************************************************************** + in_window.org.pkijs.getRandomValues = + function(view) + { + /// New array which gives a length for random value + + var crypto_temp = {}; + //crypto_temp = window.msCrypto; + //if(typeof crypto_temp === "undefined") + //crypto_temp = window.polycrypt; + //else + crypto_temp = window.crypto; + + return crypto_temp.getRandomValues(view); + } + //************************************************************************************** + in_window.org.pkijs.getAlgorithmParameters = + function(algorithmName, operation) + { + /// Algorithm name to get common parameters for + /// Kind of operation: "sign", "encrypt", "generatekey", "importkey", "exportkey", "verify" + + var result = { + algorithm: {}, + usages: [] + }; + + switch(algorithmName.toUpperCase()) + { + case "RSASSA-PKCS1-V1_5": + switch(operation.toLowerCase()) + { + case "generatekey": + result = { + algorithm: { + name: "RSASSA-PKCS1-v1_5", + modulusLength: 2048, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + hash: { + name: "SHA-256" + }, + }, + usages: ["sign", "verify"] + }; + break; + case "verify": + case "sign": + case "importkey": + result = { + algorithm: { + name: "RSASSA-PKCS1-v1_5", + hash: { + name: "SHA-256" + }, + }, + usages: ["verify"] // For importKey("pkcs8") usage must be "sign" only + }; + break; + case "exportkey": + default: + return { + algorithm: { + name: "RSASSA-PKCS1-v1_5" + }, + usages: [] + }; + } + break; + case "RSA-PSS": + switch(operation.toLowerCase()) + { + case "sign": + case "verify": + result = { + algorithm: { + name: "RSA-PSS", + hash: { + name: "SHA-1" + }, + saltLength: 20 + }, + usages: ["sign", "verify"] + }; + break; + case "generatekey": + result = { + algorithm: { + name: "RSA-PSS", + modulusLength: 2048, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + hash: { + name: "SHA-1" + } + }, + usages: ["sign", "verify"] + }; + break; + case "importkey": + result = { + algorithm: { + name: "RSA-PSS", + hash: { + name: "SHA-1" + }, + }, + usages: ["verify"] // For importKey("pkcs8") usage must be "sign" only + }; + break; + case "exportkey": + default: + return { + algorithm: { + name: "RSA-PSS" + }, + usages: [] + }; + } + break; + case "RSA-OAEP": + switch(operation.toLowerCase()) + { + case "encrypt": + case "decrypt": + result = { + algorithm: { + name: "RSA-OAEP", + }, + usages: ["encrypt", "decrypt"] + }; + break; + break; + case "generatekey": + result = { + algorithm: { + name: "RSA-OAEP", + modulusLength: 2048, + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + hash: { + name: "SHA-256" + } + }, + usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] + }; + break; + case "importkey": + result = { + algorithm: { + name: "RSA-OAEP", + hash: { + name: "SHA-256" + } + }, + usages: ["encrypt"] // encrypt for "spki" and decrypt for "pkcs8" + }; + break; + case "exportkey": + default: + return { + algorithm: { + name: "RSA-OAEP" + }, + usages: [] + }; + } + break; + case "ECDSA": + switch(operation.toLowerCase()) + { + case "generatekey": + result = { + algorithm: { + name: "ECDSA", + namedCurve: "P-256" + }, + usages: ["sign", "verify"] + }; + break; + case "importkey": + result = { + algorithm: { + name: "ECDSA", + namedCurve: "P-256" + }, + usages: ["verify"] // "sign" for "pkcs8" + }; + break; + case "verify": + case "sign": + result = { + algorithm: { + name: "ECDSA", + hash: { + name: "SHA-256" + } + }, + usages: ["sign"] + }; + break; + default: + return { + algorithm: { + name: "ECDSA" + }, + usages: [] + }; + } + break; + case "ECDH": + switch(operation.toLowerCase()) + { + case "exportkey": + case "importkey": + case "generatekey": + result = { + algorithm: { + name: "ECDH", + namedCurve: "P-256" + }, + usages: ["deriveKey", "deriveBits"] + }; + break; + case "derivekey": + case "derivebits": + result = { + algorithm: { + name: "ECDH", + namedCurve: "P-256", + public: [] // Must be a "publicKey" + }, + usages: ["encrypt", "decrypt"] + }; + break; + default: + return { + algorithm: { + name: "ECDH" + }, + usages: [] + }; + } + break; + case "AES-CTR": + switch(operation.toLowerCase()) + { + case "importkey": + case "exportkey": + case "generatekey": + result = { + algorithm: { + name: "AES-CTR", + length: 256 + }, + usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] + }; + break; + case "decrypt": + case "encrypt": + result = { + algorithm: { + name: "AES-CTR", + counter: new Uint8Array(16), + length: 10 + }, + usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] + }; + default: + return { + algorithm: { + name: "AES-CTR" + }, + usages: [] + }; + } + break; + case "AES-CBC": + switch(operation.toLowerCase()) + { + case "importkey": + case "exportkey": + case "generatekey": + result = { + algorithm: { + name: "AES-CBC", + length: 256 + }, + usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] + }; + break; + case "decrypt": + case "encrypt": + result = { + algorithm: { + name: "AES-CBC", + iv: in_window.org.pkijs.getRandomValues(new Uint8Array(16)) // For "decrypt" the value should be replaced with value got on "encrypt" step + }, + usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] + }; + default: + return { + algorithm: { + name: "AES-CBC" + }, + usages: [] + }; + } + break; + case "AES-GCM": + switch(operation.toLowerCase()) + { + case "importkey": + case "exportkey": + case "generatekey": + result = { + algorithm: { + name: "AES-GCM", + length: 256 + }, + usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] + }; + break; + case "decrypt": + case "encrypt": + result = { + algorithm: { + name: "AES-GCM", + iv: in_window.org.pkijs.getRandomValues(new Uint8Array(16)) // For "decrypt" the value should be replaced with value got on "encrypt" step + }, + usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"] + }; + default: + return { + algorithm: { + name: "AES-GCM" + }, + usages: [] + }; + } + break; + case "AES-KW": + switch(operation.toLowerCase()) + { + case "importkey": + case "exportkey": + case "generatekey": + case "wrapkey": + case "unwrapkey": + result = { + algorithm: { + name: "AES-KW", + length: 256 + }, + usages: ["wrapKey", "unwrapKey"] + }; + break; + default: + return { + algorithm: { + name: "AES-KW" + }, + usages: [] + }; + } + break; + case "HMAC": + switch(operation.toLowerCase()) + { + case "sign": + case "verify": + result = { + algorithm: { + name: "HMAC", + }, + usages: ["sign", "verify"] + }; + break; + case "importkey": + case "exportkey": + case "generatekey": + result = { + algorithm: { + name: "HMAC", + length: 10, + hash: { + name: "SHA-256" + } + }, + usages: ["sign", "verify"] + }; + break; + default: + return { + algorithm: { + name: "HMAC" + }, + usages: [] + }; + } + break; + case "HKDF": + switch(operation.toLowerCase()) + { + case "derivekey": + result = { + algorithm: { + name: "HKDF", + hash: "SHA-256", + salt: new Uint8Array(), + info: new Uint8Array() + }, + usages: ["encrypt", "decrypt"] + }; + break; + default: + return { + algorithm: { + name: "HKDF" + }, + usages: [] + }; + } + break; + case "PBKDF2": + switch(operation.toLowerCase()) + { + case "derivekey": + result = { + algorithm: { + name: "PBKDF2", + hash: { name: "SHA-256" }, + salt: new Uint8Array(), + iterations: 1000 + }, + usages: ["encrypt", "decrypt"] + }; + break; + default: + return { + algorithm: { + name: "PBKDF2" + }, + usages: [] + }; + } + break; + default: + ; + } + + return result; + } + //************************************************************************************** + in_window.org.pkijs.getHashAlgorithmByOID = + function(oid) + { + /// OID of hash algorithm + + var result = ""; + + switch(oid) + { + case "1.3.14.3.2.26": + result = "sha-1"; + break; + case "2.16.840.1.101.3.4.2.1": + result = "sha-256"; + break; + case "2.16.840.1.101.3.4.2.2": + result = "sha-384"; + break; + case "2.16.840.1.101.3.4.2.3": + result = "sha-512"; + break; + default: + } + + return result; + } + //************************************************************************************** + in_window.org.pkijs.getHashAlgorithmOID = + function(name) + { + /// Get hash algorithm OID by name + /// Common name of hash algorithm + + var result = ""; + + switch(name.toUpperCase()) + { + case "SHA-1": + result = "1.3.14.3.2.26"; + break; + case "SHA-256": + result = "2.16.840.1.101.3.4.2.1"; + break; + case "SHA-384": + result = "2.16.840.1.101.3.4.2.2"; + break; + case "SHA-512": + result = "2.16.840.1.101.3.4.2.3"; + break; + default:; + } + + return result; + } + //************************************************************************************** + in_window.org.pkijs.getHashAlgorithm = + function(signatureAlgorithm) + { + /// Getting hash algorithm by signature algorithm + /// Signature algorithm + + var result = ""; + + switch(signatureAlgorithm.algorithm_id) + { + case "1.2.840.10045.4.1": // ecdsa-with-SHA1 + case "1.2.840.113549.1.1.5": + result = "SHA-1"; + break; + case "1.2.840.10045.4.3.2": // ecdsa-with-SHA256 + case "1.2.840.113549.1.1.11": + result = "SHA-256"; + break; + case "1.2.840.10045.4.3.3": // ecdsa-with-SHA384 + case "1.2.840.113549.1.1.12": + result = "SHA-384"; + break; + case "1.2.840.10045.4.3.4": // ecdsa-with-SHA512 + case "1.2.840.113549.1.1.13": + result = "SHA-512"; + break; + case "1.2.840.113549.1.1.10": // RSA-PSS + { + var params; + + try + { + params = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params({ schema: signatureAlgorithm.algorithm_params }); + if("hashAlgorithm" in params) + result = in_window.org.pkijs.getHashAlgorithmByOID(params.hashAlgorithm.algorithm_id); + else + result = "SHA-1"; + } + catch(ex) + { + } + } + break; + default:; + } + + return result; + } + //************************************************************************************** + in_window.org.pkijs.getSignatureAlgorithm = + function(algorithm) + { + /// Get signature algorithm OID by algorithm name + /// WebCrypto algorithm object + + var result = ""; + + switch(algorithm.name.toUpperCase()) + { + case "RSASSA-PKCS1-V1_5": + switch(algorithm.hash.name.toUpperCase()) + { + case "SHA-1": + result = "1.2.840.113549.1.1.5"; + break; + case "SHA-256": + result = "1.2.840.113549.1.1.11"; + break; + case "SHA-384": + result = "1.2.840.113549.1.1.12"; + break; + case "SHA-512": + result = "1.2.840.113549.1.1.13"; + break; + default:; + } + break; + case "RSA-PSS": + result = "1.2.840.113549.1.1.10"; + break; + case "ECDSA": + switch(algorithm.hash.name.toUpperCase()) + { + case "SHA-1": + result = "1.2.840.10045.4.1"; + break; + case "SHA-256": + result = "1.2.840.10045.4.3.2"; + break; + case "SHA-384": + result = "1.2.840.10045.4.3.3"; + break; + case "SHA-512": + result = "1.2.840.10045.4.3.4"; + break; + default:; + } + break; + default:; + } + + return result; + } + //************************************************************************************** + in_window.org.pkijs.getAlgorithmNameBySignature = + function(oid) + { + /// Get WebCrypto algorithm name by signature algorithm OID + /// OID string of signature algorithm + + var result = ""; + + switch(oid) + { + case "1.2.840.113549.1.1.5": + case "1.2.840.113549.1.1.11": + case "1.2.840.113549.1.1.12": + case "1.2.840.113549.1.1.13": + result = "RSASSA-PKCS1-v1_5"; + break; + case "1.2.840.113549.1.1.10": + result = "RSA-PSS"; + break; + case "1.2.840.10045.4.1": + case "1.2.840.10045.4.3.2": + case "1.2.840.10045.4.3.3": + case "1.2.840.10045.4.3.4": + result = "ECDSA"; + break; + default:; + } + + return result; + } + //************************************************************************************** + in_window.org.pkijs.createCMSECDSASignature = + function(signatureBuffer) + { + /// Create CMS ECDSA signature from WebCrypto ECDSA signature + /// WebCrypto result of "sign" function + + // #region Initial check for correct length + if((signatureBuffer.byteLength % 2) != 0) + return new ArrayBuffer(0); + // #endregion + + // #region Initial variables + var i = 0; + var length = signatureBuffer.byteLength / 2; // There are two equal parts inside incoming ArrayBuffer + + var signatureView = new Uint8Array(signatureBuffer); + + var r_buffer = new ArrayBuffer(length); + var r_view = new Uint8Array(r_buffer); + var r_corrected_buffer; + var r_corrected_view; + + var s_buffer = new ArrayBuffer(length); + var s_view = new Uint8Array(s_buffer); + var s_corrected_buffer; + var s_corrected_view; + // #endregion + + // #region Get "r" part of ECDSA signature + for(; i < length; i++) + r_view[i] = signatureView[i]; + + if(r_view[0] & 0x80) + { + r_corrected_buffer = new ArrayBuffer(length + 1); + r_corrected_view = new Uint8Array(r_corrected_buffer); + + r_corrected_view[0] = 0x00; + + for(var j = 0; j < length; j++) + r_corrected_view[j + 1] = r_view[j]; + } + else + { + r_corrected_buffer = r_buffer; + r_corrected_view = r_view; + } + // #endregion + + // #region Get "s" part of ECDSA signature + for(; i < signatureBuffer.byteLength; i++) + s_view[i - length] = signatureView[i]; + + + if(s_view[0] & 0x80) + { + s_corrected_buffer = new ArrayBuffer(length + 1); + s_corrected_view = new Uint8Array(s_corrected_buffer); + + s_corrected_view[0] = 0x00; + + for(var j = 0; j < length; j++) + s_corrected_view[j + 1] = s_view[j]; + } + else + { + s_corrected_buffer = s_buffer; + s_corrected_view = s_view; + } + // #endregion + + // #region Create ASN.1 structure of CMS ECDSA signature + var r_integer = new in_window.org.pkijs.asn1.INTEGER(); + r_integer.value_block.is_hex_only = true; + r_integer.value_block.value_hex = in_window.org.pkijs.copyBuffer(r_corrected_buffer); + + var s_integer = new in_window.org.pkijs.asn1.INTEGER(); + s_integer.value_block.is_hex_only = true; + s_integer.value_block.value_hex = in_window.org.pkijs.copyBuffer(s_corrected_buffer); + + var asn1 = new in_window.org.pkijs.asn1.SEQUENCE({ + value: [ + r_integer, + s_integer + ] + }); + // #endregion + + return asn1.toBER(false); + } + //************************************************************************************** + in_window.org.pkijs.createECDSASignatureFromCMS = + function(cmsSignature) + { + /// Create a single ArrayBuffer from CMS ECDSA signature + /// ASN.1 SEQUENCE contains CMS ECDSA signature + + // #region Initial variables + var length = 0; + + var r_start = 0; + var s_start = 0; + + var r_length = cmsSignature.value_block.value[0].value_block.value_hex.byteLength; + var s_length = cmsSignature.value_block.value[1].value_block.value_hex.byteLength; + // #endregion + + // #region Get length of final "ArrayBuffer" + var r_view = new Uint8Array(cmsSignature.value_block.value[0].value_block.value_hex); + if((r_view[0] === 0x00) && (r_view[1] & 0x80)) + { + length = r_length - 1; + r_start = 1; + } + else + length = r_length; + + var s_view = new Uint8Array(cmsSignature.value_block.value[1].value_block.value_hex); + if((s_view[0] === 0x00) && (s_view[1] & 0x80)) + { + length += s_length - 1; + s_start = 1; + } + else + length += s_length; + // #endregion + + // #region Copy values from CMS ECDSA signature + var result = new ArrayBuffer(length); + var result_view = new Uint8Array(result); + + for(var i = r_start; i < r_length; i++) + result_view[i - r_start] = r_view[i]; + + for(var i = s_start; i < s_length; i++) + result_view[i - s_start + r_length - r_start] = s_view[i]; + // #endregion + + return result; + } + //************************************************************************************** + in_window.org.pkijs.getEncryptionAlgorithm = + function(algorithm) + { + /// Get encryption algorithm OID by WebCrypto algorithm's object + /// WebCrypto algorithm object + + var result = ""; + + switch(algorithm.name.toUpperCase()) + { + case "AES-CBC": + switch(algorithm.length) + { + case 128: + result = "2.16.840.1.101.3.4.1.2"; + break; + case 192: + result = "2.16.840.1.101.3.4.1.22"; + break; + case 256: + result = "2.16.840.1.101.3.4.1.42"; + break; + default:; + } + break; + case "AES-GCM": + switch(algorithm.length) + { + case 128: + result = "2.16.840.1.101.3.4.1.6"; + break; + case 192: + result = "2.16.840.1.101.3.4.1.26"; + break; + case 256: + result = "2.16.840.1.101.3.4.1.46"; + break; + default:; + } + break; + default:; + } + + return result; + } + //************************************************************************************** + in_window.org.pkijs.getAlgorithmByEncryptionOID = + function(oid) + { + /// Get encryption algorithm name by OID + /// OID of encryption algorithm + + var result = ""; + + switch(oid) + { + case "2.16.840.1.101.3.4.1.2": + case "2.16.840.1.101.3.4.1.22": + case "2.16.840.1.101.3.4.1.42": + result = "AES-CBC"; + break; + case "2.16.840.1.101.3.4.1.6": + case "2.16.840.1.101.3.4.1.26": + case "2.16.840.1.101.3.4.1.46": + result = "AES-GCM"; + break; + default:; + } + + return result; + } + //************************************************************************************** // #endregion //************************************************************************************** } diff --git a/org/pkijs/ocsp_tsp_schema.js b/org/pkijs/ocsp_tsp_schema.js index 388b4d596..5da612dc8 100644 --- a/org/pkijs/ocsp_tsp_schema.js +++ b/org/pkijs/ocsp_tsp_schema.js @@ -279,6 +279,47 @@ function(in_window) //************************************************************************************** // #endregion //************************************************************************************** + // #region ASN.1 schema definition for "ResponderID" type + //************************************************************************************** + in_window.org.pkijs.schema.ocsp.ResponderID = + function() + { + // KeyHash ::= OCTET STRING + // + // ResponderID ::= CHOICE { + // byName [1] Name, + // byKey [2] KeyHash } + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.CHOICE({ + value: [ + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + name: (names.block_name || ""), + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + value: [in_window.org.pkijs.schema.RDN(names.byName || { + names: { + block_name: "" + } + })] + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + name: (names.block_name || ""), + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 2 // [2] + }, + value: [new in_window.org.pkijs.asn1.OCTETSTRING({ name: (names.byKey || "") })] + }) + ] + })); + } + //************************************************************************************** + // #endregion + //************************************************************************************** // #region ASN.1 schema definition for OCSP response (RFC6960) //************************************************************************************** in_window.org.pkijs.schema.ocsp.ResponseBytes = diff --git a/org/pkijs/ocsp_tsp_simpl.js b/org/pkijs/ocsp_tsp_simpl.js index 591968f5c..913199d9b 100644 --- a/org/pkijs/ocsp_tsp_simpl.js +++ b/org/pkijs/ocsp_tsp_simpl.js @@ -758,9 +758,10 @@ function(in_window) } //************************************************************************************** in_window.org.pkijs.simpl.OCSP_REQUEST.prototype.sign = - function(privateKey) + function(privateKey, hashAlgorithm) { /// Private key for "subjectPublicKeyInfo" structure + /// Hashing algorithm. Default SHA-1 // #region Initial variables var _this = this; @@ -771,30 +772,90 @@ function(in_window) return new Promise(function(resolve, reject) { reject("Need to provide a private key for signing"); }); // #endregion + // #region Get hashing algorithm + if(typeof hashAlgorithm === "undefined") + hashAlgorithm = "SHA-1"; + else + { + // #region Simple check for supported algorithm + var oid = in_window.org.pkijs.getHashAlgorithmOID(hashAlgorithm); + if(oid === "") + return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); }); + // #endregion + } + // #endregion + // #region Check that "optionalSignature" exists in the current request if(("optionalSignature" in this) === false) return new Promise(function(resolve, reject) { reject("Need to create \"optionalSignature\" field before signing"); }); // #endregion - // #region Find a correct hashing algorithm - var sha_algorithm = ""; + // #region Get a "default parameters" for current algorithm + var defParams = in_window.org.pkijs.getAlgorithmParameters(privateKey.algorithm.name, "sign"); + defParams.algorithm.hash.name = hashAlgorithm; + // #endregion - switch(this.optionalSignature.signatureAlgorithm.algorithm_id) + // #region Fill internal structures base on "privateKey" and "hashAlgorithm" + switch(privateKey.algorithm.name.toUpperCase()) { - case "1.2.840.113549.1.1.5": - sha_algorithm = "sha-1"; + case "RSASSA-PKCS1-V1_5": + case "ECDSA": + _this.optionalSignature.signatureAlgorithm.algorithm_id = in_window.org.pkijs.getSignatureAlgorithm(defParams.algorithm); break; - case "1.2.840.113549.1.1.11": - sha_algorithm = "sha-256"; - break; - case "1.2.840.113549.1.1.12": - sha_algorithm = "sha-384"; - break; - case "1.2.840.113549.1.1.13": - sha_algorithm = "sha-512"; + case "RSA-PSS": + { + // #region Set "saltLength" as a length (in octets) of hash function result + switch(hashAlgorithm.toUpperCase()) + { + case "SHA-256": + defParams.algorithm.saltLength = 32; + break; + case "SHA-384": + defParams.algorithm.saltLength = 48; + break; + case "SHA-512": + defParams.algorithm.saltLength = 64; + break; + default:; + } + // #endregion + + // #region Fill "RSASSA_PSS_params" object + var paramsObject = {}; + + if(hashAlgorithm.toUpperCase() !== "SHA-1") + { + var hashAlgorithmOID = in_window.org.pkijs.getHashAlgorithmOID(hashAlgorithm); + if(hashAlgorithmOID === "") + return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); }); + + paramsObject.hashAlgorithm = new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: hashAlgorithmOID, + algorithm_params: new org.pkijs.asn1.NULL() + }); + + paramsObject.maskGenAlgorithm = new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: "1.2.840.113549.1.1.8", // MGF1 + algorithm_params: paramsObject.hashAlgorithm.toSchema() + }) + } + + if(defParams.algorithm.saltLength !== 20) + paramsObject.saltLength = defParams.algorithm.saltLength; + + var pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params(paramsObject); + // #endregion + + // #region Automatically set signature algorithm + _this.optionalSignature.signatureAlgorithm = new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: "1.2.840.113549.1.1.10", + algorithm_params: pssParameters.toSchema() + }); + // #endregion + } break; default: - return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + this.signature.signatureAlgorithm.algorithm_id); }); + return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + privateKey.algorithm.name); }); } // #endregion @@ -809,11 +870,16 @@ function(in_window) // #endregion // #region Signing TBS data on provided private key - return crypto.sign({ name: privateKey.algorithm.name, hash: { name: sha_algorithm } }, + return crypto.sign(defParams.algorithm, privateKey, new Uint8Array(tbs)).then( function(result) { + // #region Special case for ECDSA algorithm + if(defParams.algorithm.name === "ECDSA") + result = in_window.org.pkijs.createCMSECDSASignature(result); + // #endregion + _this.optionalSignature.signature = new in_window.org.pkijs.asn1.BITSTRING({ value_hex: result }); }, function(error) @@ -1028,9 +1094,10 @@ function(in_window) } //************************************************************************************** in_window.org.pkijs.simpl.OCSP_RESPONSE.prototype.sign = - function(privateKey) + function(privateKey, hashAlgorithm) { /// Private key for "subjectPublicKeyInfo" structure + /// Hashing algorithm. Default SHA-1 var _this = this; @@ -1040,7 +1107,7 @@ function(in_window) var asn1 = in_window.org.pkijs.fromBER(this.responseBytes.response.value_block.value_hex); var basic_resp_simpl = new in_window.org.pkijs.simpl.OCSP_BASIC_RESPONSE({ schema: asn1.result }); - return basic_resp_simpl.sign(privateKey); + return basic_resp_simpl.sign(privateKey, hashAlgorithm); } else return new Promise(function(resolve, reject) { reject("Unknown ResponseBytes type: " + _this.responseBytes.responseType); }); @@ -1558,23 +1625,9 @@ function(in_window) // #endregion // #region Find a correct hashing algorithm - switch(this.signatureAlgorithm.algorithm_id) - { - case "1.2.840.113549.1.1.5": - sha_algorithm = "sha-1"; - break; - case "1.2.840.113549.1.1.11": - sha_algorithm = "sha-256"; - break; - case "1.2.840.113549.1.1.12": - sha_algorithm = "sha-384"; - break; - case "1.2.840.113549.1.1.13": - sha_algorithm = "sha-512"; - break; - default: - return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + _this.signature.signatureAlgorithm.algorithm_id); }); - } + sha_algorithm = in_window.org.pkijs.getHashAlgorithm(this.signatureAlgorithm); + if(sha_algorithm === "") + return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + _this.signatureAlgorithm.algorithm_id); }); // #endregion // #region Find correct value for "responderID" @@ -1732,11 +1785,21 @@ function(in_window) sequence = sequence.then( function() { + // #region Get information about public key algorithm and default parameters for import + var algorithm_name = in_window.org.pkijs.getAlgorithmNameBySignature(certs[cert_index].signatureAlgorithm.algorithm_id); + if(algorithm_name === "") + return new Promise(function(resolve, reject) { reject("Unsupported public key algorithm: " + certs[cert_index].signatureAlgorithm.algorithm_id); }); + + var algorithm = in_window.org.pkijs.getAlgorithmParameters(algorithm_name, "importkey"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = sha_algorithm; + // #endregion + var publicKeyInfo_schema = certs[cert_index].subjectPublicKeyInfo.toSchema(); var publicKeyInfo_buffer = publicKeyInfo_schema.toBER(false); var publicKeyInfo_view = new Uint8Array(publicKeyInfo_buffer); - return crypto.importKey("spki", publicKeyInfo_view, { name: "RSASSA-PKCS1-v1_5", hash: { name: sha_algorithm } }, true, ["verify"]); + return crypto.importKey("spki", publicKeyInfo_view, algorithm.algorithm, true, algorithm.usages); } ); // #endregion @@ -1745,9 +1808,57 @@ function(in_window) sequence = sequence.then( function(publicKey) { - return crypto.verify({ name: "RSASSA-PKCS1-v1_5", hash: { name: sha_algorithm } }, + // #region Get default algorithm parameters for verification + var algorithm = in_window.org.pkijs.getAlgorithmParameters(publicKey.algorithm.name, "verify"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = sha_algorithm; + // #endregion + + // #region Special case for ECDSA signatures + var signature_value = _this.signature.value_block.value_hex; + + if(publicKey.algorithm.name === "ECDSA") + { + var asn1 = in_window.org.pkijs.fromBER(signature_value); + signature_value = in_window.org.pkijs.createECDSASignatureFromCMS(asn1.result); + } + // #endregion + + // #region Special case for RSA-PSS + if(publicKey.algorithm.name === "RSA-PSS") + { + var pssParameters; + + try + { + pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params({ schema: _this.signatureAlgorithm.algorithm_params }); + } + catch(ex) + { + return new Promise(function(resolve, reject) { reject(ex); }); + } + + if("saltLength" in pssParameters) + algorithm.algorithm.saltLength = pssParameters.saltLength; + else + algorithm.algorithm.saltLength = 20; + + var hash_algo = "SHA-1"; + + if("hashAlgorithm" in pssParameters) + { + hash_algo = in_window.org.pkijs.getHashAlgorithmByOID(pssParameters.hashAlgorithm.algorithm_id); + if(hash_algo === "") + return new Promise(function(resolve, reject) { reject("Unrecognized hash algorithm: " + pssParameters.hashAlgorithm.algorithm_id); }); + } + + algorithm.algorithm.hash.name = hash_algo; + } + // #endregion + + return crypto.verify(algorithm.algorithm, publicKey, - signature_view, + new Uint8Array(signature_value), tbs_view); } ); @@ -1757,8 +1868,11 @@ function(in_window) } //************************************************************************************** in_window.org.pkijs.simpl.OCSP_BASIC_RESPONSE.prototype.sign = - function(privateKey) + function(privateKey, hashAlgorithm) { + /// Private key for "subjectPublicKeyInfo" structure + /// Hashing algorithm. Default SHA-1 + // #region Initial variables var _this = this; // #endregion @@ -1768,25 +1882,85 @@ function(in_window) return new Promise(function(resolve, reject) { reject("Need to provide a private key for signing"); }); // #endregion - // #region Find a correct hashing algorithm - var sha_algorithm = ""; + // #region Get hashing algorithm + if(typeof hashAlgorithm === "undefined") + hashAlgorithm = "SHA-1"; + else + { + // #region Simple check for supported algorithm + var oid = in_window.org.pkijs.getHashAlgorithmOID(hashAlgorithm); + if(oid === "") + return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); }); + // #endregion + } + // #endregion - switch(this.signatureAlgorithm.algorithm_id) + // #region Get a "default parameters" for current algorithm + var defParams = in_window.org.pkijs.getAlgorithmParameters(privateKey.algorithm.name, "sign"); + defParams.algorithm.hash.name = hashAlgorithm; + // #endregion + + // #region Fill internal structures base on "privateKey" and "hashAlgorithm" + switch(privateKey.algorithm.name.toUpperCase()) { - case "1.2.840.113549.1.1.5": - sha_algorithm = "sha-1"; - break; - case "1.2.840.113549.1.1.11": - sha_algorithm = "sha-256"; - break; - case "1.2.840.113549.1.1.12": - sha_algorithm = "sha-384"; + case "RSASSA-PKCS1-V1_5": + case "ECDSA": + _this.signatureAlgorithm.algorithm_id = in_window.org.pkijs.getSignatureAlgorithm(defParams.algorithm); break; - case "1.2.840.113549.1.1.13": - sha_algorithm = "sha-512"; + case "RSA-PSS": + { + // #region Set "saltLength" as a length (in octets) of hash function result + switch(hashAlgorithm.toUpperCase()) + { + case "SHA-256": + defParams.algorithm.saltLength = 32; + break; + case "SHA-384": + defParams.algorithm.saltLength = 48; + break; + case "SHA-512": + defParams.algorithm.saltLength = 64; + break; + default:; + } + // #endregion + + // #region Fill "RSASSA_PSS_params" object + var paramsObject = {}; + + if(hashAlgorithm.toUpperCase() !== "SHA-1") + { + var hashAlgorithmOID = in_window.org.pkijs.getHashAlgorithmOID(hashAlgorithm); + if(hashAlgorithmOID === "") + return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); }); + + paramsObject.hashAlgorithm = new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: hashAlgorithmOID, + algorithm_params: new org.pkijs.asn1.NULL() + }); + + paramsObject.maskGenAlgorithm = new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: "1.2.840.113549.1.1.8", // MGF1 + algorithm_params: paramsObject.hashAlgorithm.toSchema() + }) + } + + if(defParams.algorithm.saltLength !== 20) + paramsObject.saltLength = defParams.algorithm.saltLength; + + var pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params(paramsObject); + // #endregion + + // #region Automatically set signature algorithm + _this.signatureAlgorithm = new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: "1.2.840.113549.1.1.10", + algorithm_params: pssParameters.toSchema() + }); + // #endregion + } break; default: - return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + _this.signature.signatureAlgorithm.algorithm_id); }); + return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + privateKey.algorithm.name); }); } // #endregion @@ -1801,11 +1975,16 @@ function(in_window) // #endregion // #region Signing TBS data on provided private key - return crypto.sign({ name: privateKey.algorithm.name, hash: { name: sha_algorithm } }, + return crypto.sign(defParams.algorithm, privateKey, new Uint8Array(_this.tbsResponseData.tbs)).then( function(result) { + // #region Special case for ECDSA algorithm + if(defParams.algorithm.name === "ECDSA") + result = in_window.org.pkijs.createCMSECDSASignature(result); + // #endregion + _this.signature = new in_window.org.pkijs.asn1.BITSTRING({ value_hex: result }); }, function(error) @@ -2575,9 +2754,10 @@ function(in_window) } //************************************************************************************** in_window.org.pkijs.simpl.TSP_RESPONSE.prototype.sign = - function(privateKey) + function(privateKey, hashAlgorithm) { /// Private key for "subjectPublicKeyInfo" structure + /// Hashing algorithm. Default SHA-1 var _this = this; @@ -2594,7 +2774,7 @@ function(in_window) // #region Sign internal signed data value var signed_simp = new in_window.org.pkijs.simpl.CMS_SIGNED_DATA({ schema: this.timeStampToken.content }); - return signed_simp.sign(privateKey, 0); + return signed_simp.sign(privateKey, 0, hashAlgorithm); // #endregion } //************************************************************************************** diff --git a/org/pkijs/x509_schema.js b/org/pkijs/x509_schema.js index 1ff96a7a8..f2d4bf4af 100644 --- a/org/pkijs/x509_schema.js +++ b/org/pkijs/x509_schema.js @@ -826,6 +826,7 @@ function(in_window) return (new in_window.org.pkijs.asn1.SEQUENCE({ name: (names.block_name || ""), + optional: (names.optional || false), value: [ new in_window.org.pkijs.asn1.OID({ name: (names.algorithmIdentifier || "") }), new in_window.org.pkijs.asn1.ANY({ name: (names.algorithmParams || ""), optional: true }) @@ -932,6 +933,60 @@ function(in_window) //************************************************************************************** // #endregion //************************************************************************************** + // #region ASN.1 schema definition for "RSASSA-PSS-params" type (RFC3447) + //************************************************************************************** + in_window.org.pkijs.schema.x509.RSASSA_PSS_params = + function() + { + //RSASSA-PSS-params ::= SEQUENCE { + // hashAlgorithm [0] HashAlgorithm DEFAULT sha1Identifier, + // maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1Identifier, + // saltLength [2] INTEGER DEFAULT 20, + // trailerField [3] INTEGER DEFAULT 1 } + + var names = in_window.org.pkijs.getNames(arguments[0]); + + return (new in_window.org.pkijs.asn1.SEQUENCE({ + name: (names.block_name || ""), + value: [ + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + optional: true, + value: [in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.hashAlgorithm || {})] + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + optional: true, + value: [in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.maskGenAlgorithm || {})] + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 2 // [2] + }, + optional: true, + value: [new in_window.org.pkijs.asn1.INTEGER({ name: (names.saltLength || "") })] + }), + new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 3 // [3] + }, + optional: true, + value: [new in_window.org.pkijs.asn1.INTEGER({ name: (names.trailerField || "") })] + }), + ] + })); + } + //************************************************************************************** + // #endregion + //************************************************************************************** // #region ASN.1 schema definition for "SubjectPublicKeyInfo" type //************************************************************************************** in_window.org.pkijs.schema.PUBLIC_KEY_INFO = @@ -1060,7 +1115,10 @@ function(in_window) name: (names.block_name || ""), value: [ new in_window.org.pkijs.asn1.OID({ name: (names.extnID || "") }), - new in_window.org.pkijs.asn1.BOOLEAN({ name: (names.critical || ""), optional: true }), + new in_window.org.pkijs.asn1.BOOLEAN({ + name: (names.critical || ""), + optional: true + }), new in_window.org.pkijs.asn1.OCTETSTRING({ name: (names.extnValue || "") }) ] })); diff --git a/org/pkijs/x509_simpl.js b/org/pkijs/x509_simpl.js index 94b0a313e..710b6bf39 100644 --- a/org/pkijs/x509_simpl.js +++ b/org/pkijs/x509_simpl.js @@ -835,6 +835,158 @@ function(in_window) //************************************************************************************** // #endregion //************************************************************************************** + // #region Simplified structure for "RSASSA_PSS_params" type (RFC3447) + //************************************************************************************** + in_window.org.pkijs.simpl.x509.RSASSA_PSS_params = + function() + { + // #region Internal properties of the object + // OPTIONAL this.hashAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); + // OPTIONAL this.maskGenAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); + // OPTIONAL this.saltLength = 20; // new in_window.org.pkijs.asn1.INTEGER(); + // OPTIONAL this.trailerField = 1; // new in_window.org.pkijs.asn1.INTEGER(); + // #endregion + + // #region If input argument array contains "schema" for this object + if((arguments[0] instanceof Object) && ("schema" in arguments[0])) + in_window.org.pkijs.simpl.x509.RSASSA_PSS_params.prototype.fromSchema.call(this, arguments[0].schema); + // #endregion + // #region If input argument array contains "native" values for internal properties + else + { + if(arguments[0] instanceof Object) + { + if("hashAlgorithm" in arguments[0]) + this.hashAlgorithm = arguments[0].hashAlgorithm; + + if("maskGenAlgorithm" in arguments[0]) + this.maskGenAlgorithm = arguments[0].maskGenAlgorithm; + + if("saltLength" in arguments[0]) + this.saltLength = arguments[0].saltLength; + + if("trailerField" in arguments[0]) + this.trailerField = arguments[0].trailerField; + } + } + // #endregion + } + //************************************************************************************** + in_window.org.pkijs.simpl.x509.RSASSA_PSS_params.prototype.fromSchema = + function(schema) + { + // #region Check the schema is valid + var asn1 = in_window.org.pkijs.compareSchema(schema, + schema, + in_window.org.pkijs.schema.x509.RSASSA_PSS_params({ + names: { + hashAlgorithm: { + names: { + block_name: "hashAlgorithm" + } + }, + maskGenAlgorithm: { + names: { + block_name: "maskGenAlgorithm" + } + }, + saltLength: "saltLength", + trailerField: "trailerField" + } + }) + ); + + if(asn1.verified === false) + throw new Error("Object's schema was not verified against input data for RSASSA_PSS_params"); + // #endregion + + // #region Get internal properties from parsed schema + if("hashAlgorithm" in asn1.result) + this.hashAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["hashAlgorithm"] }); + + if("maskGenAlgorithm" in asn1.result) + this.maskGenAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["maskGenAlgorithm"] }); + + if("saltLength" in asn1.result) + this.saltLength = asn1.result["saltLength"].value_block.value_dec; + + if("trailerField" in asn1.result) + this.trailerField = asn1.result["trailerField"].value_block.value_dec; + // #endregion + } + //************************************************************************************** + in_window.org.pkijs.simpl.x509.RSASSA_PSS_params.prototype.toSchema = + function() + { + // #region Create array for output sequence + var output_array = new Array(); + + if("hashAlgorithm" in this) + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 0 // [0] + }, + value: [this.hashAlgorithm.toSchema()] + })); + + if("maskGenAlgorithm" in this) + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 1 // [1] + }, + value: [this.maskGenAlgorithm.toSchema()] + })); + + if("saltLength" in this) + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 2 // [2] + }, + value: [new in_window.org.pkijs.asn1.INTEGER({ value: this.saltLength })] + })); + + if("trailerField" in this) + output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({ + id_block: { + tag_class: 3, // CONTEXT-SPECIFIC + tag_number: 3 // [3] + }, + value: [new in_window.org.pkijs.asn1.INTEGER({ value: this.trailerField })] + })); + // #endregion + + // #region Construct and return new ASN.1 schema for this object + return (new in_window.org.pkijs.asn1.SEQUENCE({ + value: output_array + })); + // #endregion + } + //************************************************************************************** + in_window.org.pkijs.simpl.x509.RSASSA_PSS_params.prototype.toJSON = + function() + { + var _object = {}; + + if("hashAlgorithm" in this) + _object.hashAlgorithm = this.hashAlgorithm.toJSON(); + + if("maskGenAlgorithm" in this) + _object.maskGenAlgorithm = this.maskGenAlgorithm.toJSON(); + + if("saltLength" in this) + _object.saltLength = this.saltLength.toJSON(); + + if("trailerField" in this) + _object.trailerField = this.trailerField.toJSON(); + + return _object; + } + //************************************************************************************** + // #endregion + //************************************************************************************** // #region Simplified structure for "SubjectPublicKeyInfo" type //************************************************************************************** in_window.org.pkijs.simpl.PUBLIC_KEY_INFO = @@ -3395,7 +3547,7 @@ function(in_window) this.critical = false; this.extnValue = new in_window.org.pkijs.asn1.OCTETSTRING(); - // this.parsedValue - Parsed "extnValue" in case of well-known "extnID" + // OPTIONAL this.parsedValue - Parsed "extnValue" in case of well-known "extnID" // #endregion // #region If input argument array contains "schema" for this object @@ -3526,14 +3678,28 @@ function(in_window) in_window.org.pkijs.simpl.EXTENSION.prototype.toSchema = function() { + // #region Create array for output sequence + var output_array = new Array(); + + output_array.push(new in_window.org.pkijs.asn1.OID({ value: this.extnID })); + + if(this.critical) + output_array.push(new in_window.org.pkijs.asn1.BOOLEAN({ value: this.critical })); + + output_array.push(this.extnValue); + // #endregion + // #region Construct and return new ASN.1 schema for this object return (new in_window.org.pkijs.asn1.SEQUENCE({ - value: [ - new in_window.org.pkijs.asn1.OID({ value: this.extnID }), - new in_window.org.pkijs.asn1.BOOLEAN({ value: this.critical }), - this.extnValue - ] + value: output_array })); + //return (new in_window.org.pkijs.asn1.SEQUENCE({ + // value: [ + // new in_window.org.pkijs.asn1.OID({ value: this.extnID }), + // new in_window.org.pkijs.asn1.BOOLEAN({ value: this.critical }), + // this.extnValue + // ] + //})); // #endregion } //************************************************************************************** @@ -3918,36 +4084,30 @@ function(in_window) // #endregion // #region Find signer's hashing algorithm - var sha_algorithm = ""; - - switch(this.signatureAlgorithm.algorithm_id) - { - case "1.2.840.113549.1.1.5": - sha_algorithm = "sha-1"; - break; - case "1.2.840.113549.1.1.11": - sha_algorithm = "sha-256"; - break; - case "1.2.840.113549.1.1.12": - sha_algorithm = "sha-384"; - break; - case "1.2.840.113549.1.1.13": - sha_algorithm = "sha-512"; - break; - default: - return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + _this.signatureAlgorithm.algorithm_id); }); - } + var sha_algorithm = in_window.org.pkijs.getHashAlgorithm(this.signatureAlgorithm); + if(sha_algorithm === "") + return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + _this.signatureAlgorithm.algorithm_id); }); // #endregion // #region Importing public key sequence = sequence.then( function() { + // #region Get information about public key algorithm and default parameters for import + var algorithm_name = in_window.org.pkijs.getAlgorithmNameBySignature(_this.signatureAlgorithm.algorithm_id); + if(algorithm_name === "") + return new Promise(function(resolve, reject) { reject("Unsupported public key algorithm: " + _this.signatureAlgorithm.algorithm_id); }); + + var algorithm = in_window.org.pkijs.getAlgorithmParameters(algorithm_name, "importkey"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = sha_algorithm; + // #endregion + var publicKeyInfo_schema = subjectPublicKeyInfo.toSchema(); var publicKeyInfo_buffer = publicKeyInfo_schema.toBER(false); var publicKeyInfo_view = new Uint8Array(publicKeyInfo_buffer); - return crypto.importKey("spki", publicKeyInfo_view, { name: "RSASSA-PKCS1-v1_5", hash: { name: sha_algorithm } }, true, ["verify"]); + return crypto.importKey("spki", publicKeyInfo_view, algorithm.algorithm, true, algorithm.usages); } ); // #endregion @@ -3956,9 +4116,57 @@ function(in_window) sequence = sequence.then( function(publicKey) { - return crypto.verify({ name: publicKey.algorithm.name, hash: { name: sha_algorithm } }, + // #region Get default algorithm parameters for verification + var algorithm = in_window.org.pkijs.getAlgorithmParameters(publicKey.algorithm.name, "verify"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = sha_algorithm; + // #endregion + + // #region Special case for ECDSA signatures + var signature_value = signature.value_block.value_hex; + + if(publicKey.algorithm.name === "ECDSA") + { + var asn1 = in_window.org.pkijs.fromBER(signature_value); + signature_value = in_window.org.pkijs.createECDSASignatureFromCMS(asn1.result); + } + // #endregion + + // #region Special case for RSA-PSS + if(publicKey.algorithm.name === "RSA-PSS") + { + var pssParameters; + + try + { + pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params({ schema: _this.signatureAlgorithm.algorithm_params }); + } + catch(ex) + { + return new Promise(function(resolve, reject) { reject(ex); }); + } + + if("saltLength" in pssParameters) + algorithm.algorithm.saltLength = pssParameters.saltLength; + else + algorithm.algorithm.saltLength = 20; + + var hash_algo = "SHA-1"; + + if("hashAlgorithm" in pssParameters) + { + hash_algo = in_window.org.pkijs.getHashAlgorithmByOID(pssParameters.hashAlgorithm.algorithm_id); + if(hash_algo === "") + return new Promise(function(resolve, reject) { reject("Unrecognized hash algorithm: " + pssParameters.hashAlgorithm.algorithm_id); }); + } + + algorithm.algorithm.hash.name = hash_algo; + } + // #endregion + + return crypto.verify(algorithm.algorithm, publicKey, - new Uint8Array(signature.value_block.value_hex), + new Uint8Array(signature_value), new Uint8Array(tbs)); } ); @@ -3968,9 +4176,10 @@ function(in_window) } //************************************************************************************** in_window.org.pkijs.simpl.CERT.prototype.sign = - function(privateKey) + function(privateKey, hashAlgorithm) { - /// Private key for "subjectPublicKeyInfo" structure + /// Private key for "subjectPublicKeyInfo" structure + /// Hashing algorithm. Default SHA-1 // #region Initial variables var _this = this; @@ -3981,25 +4190,87 @@ function(in_window) return new Promise(function(resolve, reject) { reject("Need to provide a private key for signing"); }); // #endregion - // #region Find a correct hashing algorithm - var sha_algorithm = ""; + // #region Get hashing algorithm + if(typeof hashAlgorithm === "undefined") + hashAlgorithm = "SHA-1"; + else + { + // #region Simple check for supported algorithm + var oid = in_window.org.pkijs.getHashAlgorithmOID(hashAlgorithm); + if(oid === "") + return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); }); + // #endregion + } + // #endregion + + // #region Get a "default parameters" for current algorithm + var defParams = in_window.org.pkijs.getAlgorithmParameters(privateKey.algorithm.name, "sign"); + defParams.algorithm.hash.name = hashAlgorithm; + // #endregion - switch(this.signatureAlgorithm.algorithm_id) + // #region Fill internal structures base on "privateKey" and "hashAlgorithm" + switch(privateKey.algorithm.name.toUpperCase()) { - case "1.2.840.113549.1.1.5": - sha_algorithm = "sha-1"; + case "RSASSA-PKCS1-V1_5": + case "ECDSA": + _this.signature.algorithm_id = in_window.org.pkijs.getSignatureAlgorithm(defParams.algorithm); + _this.signatureAlgorithm.algorithm_id = _this.signature.algorithm_id; break; - case "1.2.840.113549.1.1.11": - sha_algorithm = "sha-256"; - break; - case "1.2.840.113549.1.1.12": - sha_algorithm = "sha-384"; - break; - case "1.2.840.113549.1.1.13": - sha_algorithm = "sha-512"; + case "RSA-PSS": + { + // #region Set "saltLength" as a length (in octets) of hash function result + switch(hashAlgorithm.toUpperCase()) + { + case "SHA-256": + defParams.algorithm.saltLength = 32; + break; + case "SHA-384": + defParams.algorithm.saltLength = 48; + break; + case "SHA-512": + defParams.algorithm.saltLength = 64; + break; + default:; + } + // #endregion + + // #region Fill "RSASSA_PSS_params" object + var paramsObject = {}; + + if(hashAlgorithm.toUpperCase() !== "SHA-1") + { + var hashAlgorithmOID = in_window.org.pkijs.getHashAlgorithmOID(hashAlgorithm); + if(hashAlgorithmOID === "") + return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); }); + + paramsObject.hashAlgorithm = new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: hashAlgorithmOID, + algorithm_params: new org.pkijs.asn1.NULL() + }); + + paramsObject.maskGenAlgorithm = new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: "1.2.840.113549.1.1.8", // MGF1 + algorithm_params: paramsObject.hashAlgorithm.toSchema() + }) + } + + if(defParams.algorithm.saltLength !== 20) + paramsObject.saltLength = defParams.algorithm.saltLength; + + var pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params(paramsObject); + // #endregion + + // #region Automatically set signature algorithm + _this.signature = new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: "1.2.840.113549.1.1.10", + algorithm_params: pssParameters.toSchema() + }); + _this.signatureAlgorithm = _this.signature; // Must be the same + // #endregion + } break; default: - return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + _this.signature.signatureAlgorithm.algorithm_id); }); + return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + privateKey.algorithm.name); }); } // #endregion @@ -4014,11 +4285,16 @@ function(in_window) // #endregion // #region Signing TBS data on provided private key - return crypto.sign({ name: privateKey.algorithm.name, hash: { name: sha_algorithm } }, + return crypto.sign(defParams.algorithm, privateKey, new Uint8Array(_this.tbs)).then( function(result) { + // #region Special case for ECDSA algorithm + if(defParams.algorithm.name === "ECDSA") + result = in_window.org.pkijs.createCMSECDSASignature(result); + // #endregion + _this.signatureValue = new in_window.org.pkijs.asn1.BITSTRING({ value_hex: result }); }, function(error) @@ -4034,17 +4310,49 @@ function(in_window) { /// Importing public key for current certificate + // #region Initial variables + var algorithm; + // #endregion + // #region Get a "crypto" extension var crypto = in_window.org.pkijs.getCrypto(); if(typeof crypto == "undefined") return new Promise(function(resolve, reject) { reject("Unable to create WebCrypto object"); }); // #endregion + if(arguments[0] instanceof Object) + { + if("algorithm" in arguments[0]) + algorithm = arguments[0].algorithm; + else + return new Promise(function(resolve, reject) { reject("Absent mandatory parameter \"algorithm\""); }); + } + else + { + // #region Find signer's hashing algorithm + var sha_algorithm = in_window.org.pkijs.getHashAlgorithm(this.signatureAlgorithm); + if(sha_algorithm === "") + return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + this.signatureAlgorithm.algorithm_id); }); + // #endregion + + // #region Get information about public key algorithm and default parameters for import + var algorithm_name = in_window.org.pkijs.getAlgorithmNameBySignature(this.signatureAlgorithm.algorithm_id); + if(algorithm_name === "") + return new Promise(function(resolve, reject) { reject("Unsupported public key algorithm: " + this.signatureAlgorithm.algorithm_id); }); + + algorithm = in_window.org.pkijs.getAlgorithmParameters(algorithm_name, "importkey"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = sha_algorithm; + // #endregion + } + + // #region Get neccessary values from internal fields for current certificate var publicKeyInfo_schema = this.subjectPublicKeyInfo.toSchema(); var publicKeyInfo_buffer = publicKeyInfo_schema.toBER(false); var publicKeyInfo_view = new Uint8Array(publicKeyInfo_buffer); + // #endregion - return crypto.importKey("spki", publicKeyInfo_view, { name: "RSASSA-PKCS1-v1_5", hash: { name: sha_algorithm } }, true, ["verify"]); + return crypto.importKey("spki", publicKeyInfo_view, algorithm.algorithm, true, algorithm.usages); } //************************************************************************************** in_window.org.pkijs.simpl.CERT.prototype.getKeyHash = @@ -4453,40 +4761,34 @@ function(in_window) // #endregion // #region Find signer's hashing algorithm - var sha_algorithm = ""; - - switch(this.signatureAlgorithm.algorithm_id) - { - case "1.2.840.113549.1.1.5": - sha_algorithm = "sha-1"; - break; - case "1.2.840.113549.1.1.11": - sha_algorithm = "sha-256"; - break; - case "1.2.840.113549.1.1.12": - sha_algorithm = "sha-384"; - break; - case "1.2.840.113549.1.1.13": - sha_algorithm = "sha-512"; - break; - default: - return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + _this.signatureAlgorithm.algorithm_id); }); - } + var sha_algorithm = in_window.org.pkijs.getHashAlgorithm(this.signatureAlgorithm); + if(sha_algorithm === "") + return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + _this.signatureAlgorithm.algorithm_id); }); // #endregion // #region Import public key sequence = sequence.then( function() { + // #region Get information about public key algorithm and default parameters for import + var algorithm_name = in_window.org.pkijs.getAlgorithmNameBySignature(this.signature.algorithm_id); + if(algorithm_name === "") + return new Promise(function(resolve, reject) { reject("Unsupported public key algorithm: " + _this.signature.algorithm_id); }); + + var algorithm = in_window.org.pkijs.getAlgorithmParameters(algorithm_name, "importkey"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = sha_algorithm; + // #endregion + var publicKeyInfo_schema = subjectPublicKeyInfo.toSchema(); var publicKeyInfo_buffer = publicKeyInfo_schema.toBER(false); var publicKeyInfo_view = new Uint8Array(publicKeyInfo_buffer); return crypto.importKey("spki", publicKeyInfo_view, - { name: "RSASSA-PKCS1-v1_5", hash: { name: sha_algorithm } }, + algorithm.algorithm, true, - ["verify"]); + algorithm.usages); } ); // #endregion @@ -4495,9 +4797,57 @@ function(in_window) sequence = sequence.then( function(publicKey) { - return crypto.verify({ name: publicKey.algorithm.name, hash: { name: sha_algorithm } }, + // #region Get default algorithm parameters for verification + var algorithm = in_window.org.pkijs.getAlgorithmParameters(publicKey.algorithm.name, "verify"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = sha_algorithm; + // #endregion + + // #region Special case for ECDSA signatures + var signature_value = signature.value_block.value_hex; + + if(publicKey.algorithm.name === "ECDSA") + { + var asn1 = in_window.org.pkijs.fromBER(signature_value); + signature_value = in_window.org.pkijs.createECDSASignatureFromCMS(asn1.result); + } + // #endregion + + // #region Special case for RSA-PSS + if(publicKey.algorithm.name === "RSA-PSS") + { + var pssParameters; + + try + { + pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params({ schema: _this.signatureAlgorithm.algorithm_params }); + } + catch(ex) + { + return new Promise(function(resolve, reject) { reject(ex); }); + } + + if("saltLength" in pssParameters) + algorithm.algorithm.saltLength = pssParameters.saltLength; + else + algorithm.algorithm.saltLength = 20; + + var hash_algo = "SHA-1"; + + if("hashAlgorithm" in pssParameters) + { + hash_algo = in_window.org.pkijs.getHashAlgorithmByOID(pssParameters.hashAlgorithm.algorithm_id); + if(hash_algo === "") + return new Promise(function(resolve, reject) { reject("Unrecognized hash algorithm: " + pssParameters.hashAlgorithm.algorithm_id); }); + } + + algorithm.algorithm.hash.name = hash_algo; + } + // #endregion + + return crypto.verify(algorithm.algorithm, publicKey, - new Uint8Array(signature.value_block.value_hex), + new Uint8Array(signature_value), new Uint8Array(tbs)); } ); @@ -4507,9 +4857,10 @@ function(in_window) } //************************************************************************************** in_window.org.pkijs.simpl.CRL.prototype.sign = - function(privateKey) + function(privateKey, hashAlgorithm) { /// Private key for "subjectPublicKeyInfo" structure + /// Hashing algorithm. Default SHA-1 // #region Initial variables var _this = this; @@ -4520,25 +4871,87 @@ function(in_window) return new Promise(function(resolve, reject) { reject("Need to provide a private key for signing"); }); // #endregion - // #region Find a correct hashing algorithm - var sha_algorithm = ""; + // #region Get hashing algorithm + if(typeof hashAlgorithm === "undefined") + hashAlgorithm = "SHA-1"; + else + { + // #region Simple check for supported algorithm + var oid = in_window.org.pkijs.getHashAlgorithmOID(hashAlgorithm); + if(oid === "") + return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); }); + // #endregion + } + // #endregion + + // #region Get a "default parameters" for current algorithm + var defParams = in_window.org.pkijs.getAlgorithmParameters(privateKey.algorithm.name, "sign"); + defParams.algorithm.hash.name = hashAlgorithm; + // #endregion - switch(this.signatureAlgorithm.algorithm_id) + // #region Fill internal structures base on "privateKey" and "hashAlgorithm" + switch(privateKey.algorithm.name.toUpperCase()) { - case "1.2.840.113549.1.1.5": - sha_algorithm = "sha-1"; + case "RSASSA-PKCS1-V1_5": + case "ECDSA": + _this.signature.algorithm_id = in_window.org.pkijs.getSignatureAlgorithm(defParams.algorithm); + _this.signatureAlgorithm.algorithm_id = _this.signature.algorithm_id; break; - case "1.2.840.113549.1.1.11": - sha_algorithm = "sha-256"; - break; - case "1.2.840.113549.1.1.12": - sha_algorithm = "sha-384"; - break; - case "1.2.840.113549.1.1.13": - sha_algorithm = "sha-512"; + case "RSA-PSS": + { + // #region Set "saltLength" as a length (in octets) of hash function result + switch(hashAlgorithm.toUpperCase()) + { + case "SHA-256": + defParams.algorithm.saltLength = 32; + break; + case "SHA-384": + defParams.algorithm.saltLength = 48; + break; + case "SHA-512": + defParams.algorithm.saltLength = 64; + break; + default:; + } + // #endregion + + // #region Fill "RSASSA_PSS_params" object + var paramsObject = {}; + + if(hashAlgorithm.toUpperCase() !== "SHA-1") + { + var hashAlgorithmOID = in_window.org.pkijs.getHashAlgorithmOID(hashAlgorithm); + if(hashAlgorithmOID === "") + return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); }); + + paramsObject.hashAlgorithm = new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: hashAlgorithmOID, + algorithm_params: new org.pkijs.asn1.NULL() + }); + + paramsObject.maskGenAlgorithm = new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: "1.2.840.113549.1.1.8", // MGF1 + algorithm_params: paramsObject.hashAlgorithm.toSchema() + }) + } + + if(defParams.algorithm.saltLength !== 20) + paramsObject.saltLength = defParams.algorithm.saltLength; + + var pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params(paramsObject); + // #endregion + + // #region Automatically set signature algorithm + _this.signature = new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: "1.2.840.113549.1.1.10", + algorithm_params: pssParameters.toSchema() + }); + _this.signatureAlgorithm = _this.signature; // Must be the same + // #endregion + } break; default: - return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + _this.signature.signatureAlgorithm.algorithm_id); }); + return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + privateKey.algorithm.name); }); } // #endregion @@ -4554,12 +4967,17 @@ function(in_window) // #region Signing TBS data on provided private key return crypto.sign( - { name: privateKey.algorithm.name, hash: { name: sha_algorithm } }, + defParams.algorithm, privateKey, new Uint8Array(_this.tbs)). then( function(result) { + // #region Special case for ECDSA algorithm + if(defParams.algorithm.name === "ECDSA") + result = in_window.org.pkijs.createCMSECDSASignature(result); + // #endregion + _this.signatureValue = new in_window.org.pkijs.asn1.BITSTRING({ value_hex: result }); }, function(error) @@ -4876,6 +5294,7 @@ function(in_window) /// Returns a new Promise object (in case of error), or a result of "crypto.subtle.veryfy" function // #region Global variables + var _this = this; var sha_algorithm = ""; var sequence = Promise.resolve(); @@ -4892,34 +5311,30 @@ function(in_window) // #endregion // #region Find a correct hashing algorithm - switch(this.signatureAlgorithm.algorithm_id) - { - case "1.2.840.113549.1.1.5": - sha_algorithm = "sha-1"; - break; - case "1.2.840.113549.1.1.11": - sha_algorithm = "sha-256"; - break; - case "1.2.840.113549.1.1.12": - sha_algorithm = "sha-384"; - break; - case "1.2.840.113549.1.1.13": - sha_algorithm = "sha-512"; - break; - default: - return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + this.signature.signatureAlgorithm.algorithm_id); }); - } + sha_algorithm = in_window.org.pkijs.getHashAlgorithm(this.signatureAlgorithm); + if(sha_algorithm === "") + return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + _this.signatureAlgorithm.algorithm_id); }); // #endregion // #region Importing public key sequence = sequence.then( function() { + // #region Get information about public key algorithm and default parameters for import + var algorithm_name = in_window.org.pkijs.getAlgorithmNameBySignature(_this.signatureAlgorithm.algorithm_id); + if(algorithm_name === "") + return new Promise(function(resolve, reject) { reject("Unsupported public key algorithm: " + _this.signatureAlgorithm.algorithm_id); }); + + var algorithm = in_window.org.pkijs.getAlgorithmParameters(algorithm_name, "importkey"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = sha_algorithm; + // #endregion + var publicKeyInfo_schema = subjectPublicKeyInfo.toSchema(); var publicKeyInfo_buffer = publicKeyInfo_schema.toBER(false); var publicKeyInfo_view = new Uint8Array(publicKeyInfo_buffer); - return crypto.importKey("spki", publicKeyInfo_view, { name: "RSASSA-PKCS1-v1_5", hash: { name: sha_algorithm } }, true, ["verify"]); + return crypto.importKey("spki", publicKeyInfo_view, algorithm.algorithm, true, algorithm.usages); } ); // #endregion @@ -4928,9 +5343,57 @@ function(in_window) sequence = sequence.then( function(publicKey) { - return crypto.verify({ name: publicKey.algorithm.name, hash: { name: sha_algorithm } }, + // #region Get default algorithm parameters for verification + var algorithm = in_window.org.pkijs.getAlgorithmParameters(publicKey.algorithm.name, "verify"); + if("hash" in algorithm.algorithm) + algorithm.algorithm.hash.name = sha_algorithm; + // #endregion + + // #region Special case for ECDSA signatures + var signature_value = signature.value_block.value_hex; + + if(publicKey.algorithm.name === "ECDSA") + { + var asn1 = in_window.org.pkijs.fromBER(signature_value); + signature_value = in_window.org.pkijs.createECDSASignatureFromCMS(asn1.result); + } + // #endregion + + // #region Special case for RSA-PSS + if(publicKey.algorithm.name === "RSA-PSS") + { + var pssParameters; + + try + { + pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params({ schema: _this.signatureAlgorithm.algorithm_params }); + } + catch(ex) + { + return new Promise(function(resolve, reject) { reject(ex); }); + } + + if("saltLength" in pssParameters) + algorithm.algorithm.saltLength = pssParameters.saltLength; + else + algorithm.algorithm.saltLength = 20; + + var hash_algo = "SHA-1"; + + if("hashAlgorithm" in pssParameters) + { + hash_algo = in_window.org.pkijs.getHashAlgorithmByOID(pssParameters.hashAlgorithm.algorithm_id); + if(hash_algo === "") + return new Promise(function(resolve, reject) { reject("Unrecognized hash algorithm: " + pssParameters.hashAlgorithm.algorithm_id); }); + } + + algorithm.algorithm.hash.name = hash_algo; + } + // #endregion + + return crypto.verify(algorithm.algorithm, publicKey, - new Uint8Array(signature.value_block.value_hex), + new Uint8Array(signature_value), new Uint8Array(tbs)); } ); @@ -4940,9 +5403,10 @@ function(in_window) } //************************************************************************************** in_window.org.pkijs.simpl.PKCS10.prototype.sign = - function(privateKey) + function(privateKey, hashAlgorithm) { /// Private key for "subjectPublicKeyInfo" structure + /// Hashing algorithm. Default SHA-1 // #region Initial variables var _this = this; @@ -4953,25 +5417,85 @@ function(in_window) return new Promise(function(resolve, reject) { reject("Need to provide a private key for signing"); }); // #endregion - // #region Find a correct hashing algorithm - var sha_algorithm = ""; + // #region Get hashing algorithm + if(typeof hashAlgorithm === "undefined") + hashAlgorithm = "SHA-1"; + else + { + // #region Simple check for supported algorithm + var oid = in_window.org.pkijs.getHashAlgorithmOID(hashAlgorithm); + if(oid === "") + return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); }); + // #endregion + } + // #endregion + + // #region Get a "default parameters" for current algorithm + var defParams = in_window.org.pkijs.getAlgorithmParameters(privateKey.algorithm.name, "sign"); + defParams.algorithm.hash.name = hashAlgorithm; + // #endregion - switch(this.signatureAlgorithm.algorithm_id) + // #region Fill internal structures base on "privateKey" and "hashAlgorithm" + switch(privateKey.algorithm.name.toUpperCase()) { - case "1.2.840.113549.1.1.5": - sha_algorithm = "sha-1"; + case "RSASSA-PKCS1-V1_5": + case "ECDSA": + _this.signatureAlgorithm.algorithm_id = in_window.org.pkijs.getSignatureAlgorithm(defParams.algorithm); break; - case "1.2.840.113549.1.1.11": - sha_algorithm = "sha-256"; - break; - case "1.2.840.113549.1.1.12": - sha_algorithm = "sha-384"; - break; - case "1.2.840.113549.1.1.13": - sha_algorithm = "sha-512"; + case "RSA-PSS": + { + // #region Set "saltLength" as a length (in octets) of hash function result + switch(hashAlgorithm.toUpperCase()) + { + case "SHA-256": + defParams.algorithm.saltLength = 32; + break; + case "SHA-384": + defParams.algorithm.saltLength = 48; + break; + case "SHA-512": + defParams.algorithm.saltLength = 64; + break; + default:; + } + // #endregion + + // #region Fill "RSASSA_PSS_params" object + var paramsObject = {}; + + if(hashAlgorithm.toUpperCase() !== "SHA-1") + { + var hashAlgorithmOID = in_window.org.pkijs.getHashAlgorithmOID(hashAlgorithm); + if(hashAlgorithmOID === "") + return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); }); + + paramsObject.hashAlgorithm = new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: hashAlgorithmOID, + algorithm_params: new org.pkijs.asn1.NULL() + }); + + paramsObject.maskGenAlgorithm = new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: "1.2.840.113549.1.1.8", // MGF1 + algorithm_params: paramsObject.hashAlgorithm.toSchema() + }) + } + + if(defParams.algorithm.saltLength !== 20) + paramsObject.saltLength = defParams.algorithm.saltLength; + + var pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params(paramsObject); + // #endregion + + // #region Automatically set signature algorithm + _this.signatureAlgorithm = new org.pkijs.simpl.ALGORITHM_IDENTIFIER({ + algorithm_id: "1.2.840.113549.1.1.10", + algorithm_params: pssParameters.toSchema() + }); + // #endregion + } break; default: - return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + this.signature.signatureAlgorithm.algorithm_id); }); + return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + privateKey.algorithm.name); }); } // #endregion @@ -4986,11 +5510,16 @@ function(in_window) // #endregion // #region Signing TBS data on provided private key - return crypto.sign({ name: privateKey.algorithm.name, hash: { name: sha_algorithm } }, + return crypto.sign(defParams.algorithm, privateKey, new Uint8Array(_this.tbs)).then( function(result) { + // #region Special case for ECDSA algorithm + if(defParams.algorithm.name === "ECDSA") + result = in_window.org.pkijs.createCMSECDSASignature(result); + // #endregion + _this.signatureValue = new in_window.org.pkijs.asn1.BITSTRING({ value_hex: result }); }, function(error) diff --git a/package.json b/package.json index e6ac8e990..027e440d8 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ ], "name": "pkijs", "description": "Public Key Infrastructure (PKI) is the basis of how identity and key management is performed on the web today. PKIjs is a pure JavaScript library implementing the formats that are used in PKI applications. It is built on WebCrypto and aspires to make it possible to build native web applications that utilize X.509 and the related formats on the web without plug-ins", - "version": "1.0.3", + "version": "1.1.0", "repository": { "type": "git", "url": "git://github.com/GlobalSign/PKI.js.git"