diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index a2a792d4..f0624fc7 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -182,7 +182,9 @@ "RSA Encrypt", "RSA Decrypt", "Parse SSH Host Key", - "Parse CSR" + "Parse CSR", + "Public Key from Certificate", + "Public Key from Private Key" ] }, { diff --git a/src/core/operations/PubKeyFromCert.mjs b/src/core/operations/PubKeyFromCert.mjs new file mode 100644 index 00000000..0233b04a --- /dev/null +++ b/src/core/operations/PubKeyFromCert.mjs @@ -0,0 +1,68 @@ +/** + * @author cplussharp + * @copyright Crown Copyright 2023 + * @license Apache-2.0 + */ + +import r from "jsrsasign"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; + +/** + * Public Key from Certificate operation + */ +class PubKeyFromCert extends Operation { + + /** + * PubKeyFromCert constructor + */ + constructor() { + super(); + + this.name = "Public Key from Certificate"; + this.module = "PublicKey"; + this.description = "Extracts the Public Key from a Certificate."; + this.infoURL = "https://en.wikipedia.org/wiki/X.509"; + this.inputType = "string"; + this.outputType = "string"; + this.args = []; + this.checks = []; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + let output = ""; + let match; + const regex = /-----BEGIN CERTIFICATE-----/g; + while ((match = regex.exec(input)) !== null) { + // find corresponding end tag + const indexBase64 = match.index + match[0].length; + const footer = "-----END CERTIFICATE-----"; + const indexFooter = input.indexOf(footer, indexBase64); + if (indexFooter === -1) { + throw new OperationError(`PEM footer '${footer}' not found`); + } + + const certPem = input.substring(match.index, indexFooter + footer.length); + const cert = new r.X509(); + cert.readCertPEM(certPem); + let pubKey; + try { + pubKey = cert.getPublicKey(); + } catch { + throw new OperationError("Unsupported public key type"); + } + const pubKeyPem = r.KEYUTIL.getPEM(pubKey); + + // PEM ends with '\n', so a new key always starts on a new line + output += pubKeyPem; + } + return output; + } +} + +export default PubKeyFromCert; diff --git a/src/core/operations/PubKeyFromPrivKey.mjs b/src/core/operations/PubKeyFromPrivKey.mjs new file mode 100644 index 00000000..5a08882b --- /dev/null +++ b/src/core/operations/PubKeyFromPrivKey.mjs @@ -0,0 +1,82 @@ +/** + * @author cplussharp + * @copyright Crown Copyright 2023 + * @license Apache-2.0 + */ + +import r from "jsrsasign"; +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; + +/** + * Public Key from Private Key operation + */ +class PubKeyFromPrivKey extends Operation { + + /** + * PubKeyFromPrivKey constructor + */ + constructor() { + super(); + + this.name = "Public Key from Private Key"; + this.module = "PublicKey"; + this.description = "Extracts the Public Key from a Private Key."; + this.infoURL = "https://en.wikipedia.org/wiki/PKCS_8"; + this.inputType = "string"; + this.outputType = "string"; + this.args = []; + this.checks = []; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + let output = ""; + let match; + const regex = /-----BEGIN ((RSA |EC |DSA )?PRIVATE KEY)-----/g; + while ((match = regex.exec(input)) !== null) { + // find corresponding end tag + const indexBase64 = match.index + match[0].length; + const footer = `-----END ${match[1]}-----`; + const indexFooter = input.indexOf(footer, indexBase64); + if (indexFooter === -1) { + throw new OperationError(`PEM footer '${footer}' not found`); + } + + const privKeyPem = input.substring(match.index, indexFooter + footer.length); + let privKey; + try { + privKey = r.KEYUTIL.getKey(privKeyPem); + } catch (err) { + throw new OperationError(`Unsupported key type: ${err}`); + } + let pubKey; + if (privKey.type && privKey.type === "EC") { + pubKey = new r.KJUR.crypto.ECDSA({ curve: privKey.curve }); + pubKey.setPublicKeyHex(privKey.generatePublicKeyHex()); + } else if (privKey.type && privKey.type === "DSA") { + if (!privKey.y) { + throw new OperationError(`DSA Private Key in PKCS#8 is not supported`); + } + pubKey = new r.KJUR.crypto.DSA(); + pubKey.setPublic(privKey.p, privKey.q, privKey.g, privKey.y); + } else if (privKey.n && privKey.e) { + pubKey = new r.RSAKey(); + pubKey.setPublic(privKey.n, privKey.e); + } else { + throw new OperationError(`Unsupported key type`); + } + const pubKeyPem = r.KEYUTIL.getPEM(pubKey); + + // PEM ends with '\n', so a new key always starts on a new line + output += pubKeyPem; + } + return output; + } +} + +export default PubKeyFromPrivKey; diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index 685dfa0c..606675b7 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -121,6 +121,8 @@ import "./tests/PGP.mjs"; import "./tests/PHP.mjs"; import "./tests/PowerSet.mjs"; import "./tests/Protobuf.mjs"; +import "./tests/PubKeyFromCert.mjs"; +import "./tests/PubKeyFromPrivKey.mjs"; import "./tests/Rabbit.mjs"; import "./tests/RAKE.mjs"; import "./tests/Regex.mjs"; diff --git a/tests/operations/tests/PubKeyFromCert.mjs b/tests/operations/tests/PubKeyFromCert.mjs new file mode 100644 index 00000000..ae5609aa --- /dev/null +++ b/tests/operations/tests/PubKeyFromCert.mjs @@ -0,0 +1,215 @@ +/** + * Public Key from Certificate + * + * @author cplussharp + * @copyright Crown Copyright 2023 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +const RSA_CERT = `-----BEGIN CERTIFICATE----- +MIIBfTCCASegAwIBAgIUeisK5Nwss2DGg5PCs4uSxxXyyNkwDQYJKoZIhvcNAQEL +BQAwEzERMA8GA1UEAwwIUlNBIHRlc3QwHhcNMjExMTE5MTcyMDI2WhcNMzExMTE3 +MTcyMDI2WjATMREwDwYDVQQDDAhSU0EgdGVzdDBcMA0GCSqGSIb3DQEBAQUAA0sA +MEgCQQDyq9A6emHSLczn5Omu5muy+AReC53pTGCrW6Bi65OoobahT2RUSzXCYuvB +757fLLTKz+dLeo6sFkNhIzHZI+n7AgMBAAGjUzBRMB0GA1UdDgQWBBRO+jvkqq5p +pnQgwMMnRoun6e7eiTAfBgNVHSMEGDAWgBRO+jvkqq5ppnQgwMMnRoun6e7eiTAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA0EAR/5HAZM5qBhU/ezDUIFx +gmUGoFbIb5kJD41YCnaSdrgWglh4He4melSs42G/oxBBjuCJ0bUpqWnLl+lJkv1z +IA== +-----END CERTIFICATE-----`; + +const RSA_PUBKEY = `-----BEGIN PUBLIC KEY----- +MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPKr0Dp6YdItzOfk6a7ma7L4BF4LnelM +YKtboGLrk6ihtqFPZFRLNcJi68Hvnt8stMrP50t6jqwWQ2EjMdkj6fsCAwEAAQ== +-----END PUBLIC KEY-----`; + +const EC_P256_CERT = `-----BEGIN CERTIFICATE----- +MIIBfzCCASWgAwIBAgIUK4H8J3Hr7NpRLPrACj8Pje4JJJ0wCgYIKoZIzj0EAwIw +FTETMBEGA1UEAwwKUC0yNTYgdGVzdDAeFw0yMTExMTkxNzE5NDVaFw0zMTExMTcx +NzE5NDVaMBUxEzARBgNVBAMMClAtMjU2IHRlc3QwWTATBgcqhkjOPQIBBggqhkjO +PQMBBwNCAAQNRzwDQQM0qgJgg9YwfPXJTOoTmYmC6yBwATwfrzXR+QnxmZM2IIJr +qwuBHa8PVU2HZ2KKtaAo8fg9Uwpq/l7po1MwUTAdBgNVHQ4EFgQU/SxodXrpkybM +gcIgkxnRKd7HMzowHwYDVR0jBBgwFoAU/SxodXrpkybMgcIgkxnRKd7HMzowDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNIADBFAiBU9PrOa/kXCpTTBInRf/sN +ac2iDHmbdpWzcXI+xLKNYAIhAIRR1LRSHVwOTLQ/iBXd+8LCkm5aTB27RW46LN80 +ylxt +-----END CERTIFICATE-----`; + +const EC_P256_PUBKEY = `-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDUc8A0EDNKoCYIPWMHz1yUzqE5mJ +gusgcAE8H6810fkJ8ZmTNiCCa6sLgR2vD1VNh2diirWgKPH4PVMKav5e6Q== +-----END PUBLIC KEY-----`; + +const DSA_CERT = `-----BEGIN CERTIFICATE----- +MIIEXzCCBA2gAwIBAgIUYYcPJB8UQLzUnqkGJvs3J4RI0OgwCwYJYIZIAWUDBAMC +MBMxETAPBgNVBAMMCERTQSBUZXN0MB4XDTIzMTAxNTAwMjEzNVoXDTMzMTAxMjAw +MjEzNVowEzERMA8GA1UEAwwIRFNBIFRlc3QwggNCMIICNQYHKoZIzjgEATCCAigC +ggEBALoLV+uz7vMYZCIuwXNkgZawvDgZAG1T7IiG030WgqesRNncuoUQOmAJCiuN +zkjVNSY08rabex/RIkWILvxP91SlzhA9t9+dp87p238ecxGa1sD2re+y35RP7IxN +T33633NtwGItZ3BqqAhoMmuwwwxau0E8zwYodTTlwTRp4QVPpMH1eJCUBeEzcWP5 +ZZ1lRNhR5M2TqzSU3ya5/4c3a9rI86h9VIVgw8yVvw3y6yclzjALm2ntD5riskdM +Z6mMkfYQwEbIGRTELX6A7LZ0lX1CislenF9ASb2E4g2nGcMQ0uSGzA4W9mf6wwmP +S6iwX5+Qu/i6jCm5i37fQ1H5HHUCHQDA+UnPHM6PZEgfFen8djZpl/cl05MpWk+d +nikFAoIBADXOTpBw0WA+UihxDG+6qqM05kxVMYmz6IRZ/06ffZSGVFN6Bx1i0s3v +kzM5V8GsKpkKkSk7V8fTQnAIIlMmt1Y7ff+ng7+TfYotMrvvEYlolYK06J2WWoUA +8iKp8+n58vdoky+xZmuGmcvCAojVDbEeU2wEqYE1PzrHCSOoOiKB2P4fOhyuF+qx +E8nkzURIg2RmSSkqWOkXiWyKyfpUaB+4cEisp4ThENEPmdntE1vLh2r7EOIxpE5D +0NAy2wFKqe3ljfgE6XsPZKgVAguRDVpzdmL6WDY7DM/BcS726vx+kX55QDkszvec +raNirnir2QrB/a0JQjF6Y62yGmG7GF8DggEFAAKCAQBpN+w0N0b5IIAspXnlJ9yu +B6ORk3j/5rZ+DUtTzW1YAJI6xjTcFQvN7FpVLkmLtXKUXF04R+sdGJ7VFwOb0rba +L5vQzrqNkBrbgSzuzeloiG+7OLA6VeQtNbQh6OurrZFi9gY+qA5ciT9kQXyrHudV +Xu956NDrooRxmv6JIVFvToaNiwe2vcgdkALw8HUbLFYof4SAE9jgU8EpxTp02e8H +zvVSVa6yj1nnGhpzLPlEqF8TZvs9pTg2kIk3/zvWojMJoPyTALfbTjbAeiFMMeKN +K/CKOOJj23AVAZxpMSR6cUbrIcRdKDnhCTVkkxXUecAIUs6Mk10kSfkuiGl9LjKj +o1MwUTAdBgNVHQ4EFgQUE+xZdvgiDIFWKQskMYnNaZ3iPHAwHwYDVR0jBBgwFoAU +E+xZdvgiDIFWKQskMYnNaZ3iPHAwDwYDVR0TAQH/BAUwAwEB/zALBglghkgBZQME +AwIDPwAwPAIcZbtf4+bjXEGQqNs6IglLrOgIjYF46q7qCNfXmQIcMKUtH3S6sDJE +3ds9eL+oC+HPFlfUNfUiU30aDA== +-----END CERTIFICATE-----`; + +const DSA_PUBKEY = `-----BEGIN PUBLIC KEY----- +MIIDQjCCAjUGByqGSM44BAEwggIoAoIBAQC6C1frs+7zGGQiLsFzZIGWsLw4GQBt +U+yIhtN9FoKnrETZ3LqFEDpgCQorjc5I1TUmNPK2m3sf0SJFiC78T/dUpc4QPbff +nafO6dt/HnMRmtbA9q3vst+UT+yMTU99+t9zbcBiLWdwaqgIaDJrsMMMWrtBPM8G +KHU05cE0aeEFT6TB9XiQlAXhM3Fj+WWdZUTYUeTNk6s0lN8muf+HN2vayPOofVSF +YMPMlb8N8usnJc4wC5tp7Q+a4rJHTGepjJH2EMBGyBkUxC1+gOy2dJV9QorJXpxf +QEm9hOINpxnDENLkhswOFvZn+sMJj0uosF+fkLv4uowpuYt+30NR+Rx1Ah0AwPlJ +zxzOj2RIHxXp/HY2aZf3JdOTKVpPnZ4pBQKCAQA1zk6QcNFgPlIocQxvuqqjNOZM +VTGJs+iEWf9On32UhlRTegcdYtLN75MzOVfBrCqZCpEpO1fH00JwCCJTJrdWO33/ +p4O/k32KLTK77xGJaJWCtOidllqFAPIiqfPp+fL3aJMvsWZrhpnLwgKI1Q2xHlNs +BKmBNT86xwkjqDoigdj+HzocrhfqsRPJ5M1ESINkZkkpKljpF4lsisn6VGgfuHBI +rKeE4RDRD5nZ7RNby4dq+xDiMaROQ9DQMtsBSqnt5Y34BOl7D2SoFQILkQ1ac3Zi ++lg2OwzPwXEu9ur8fpF+eUA5LM73nK2jYq54q9kKwf2tCUIxemOtshphuxhfA4IB +BQACggEAaTfsNDdG+SCALKV55SfcrgejkZN4/+a2fg1LU81tWACSOsY03BULzexa +VS5Ji7VylFxdOEfrHRie1RcDm9K22i+b0M66jZAa24Es7s3paIhvuziwOlXkLTW0 +Iejrq62RYvYGPqgOXIk/ZEF8qx7nVV7veejQ66KEcZr+iSFRb06GjYsHtr3IHZAC +8PB1GyxWKH+EgBPY4FPBKcU6dNnvB871UlWuso9Z5xoacyz5RKhfE2b7PaU4NpCJ +N/871qIzCaD8kwC32042wHohTDHijSvwijjiY9twFQGcaTEkenFG6yHEXSg54Qk1 +ZJMV1HnACFLOjJNdJEn5LohpfS4yow== +-----END PUBLIC KEY-----`; + +const ED25519_CERT = `-----BEGIN CERTIFICATE----- +MIIBQjCB9aADAgECAhRjPJhrdNco5LzpsIs0vSLLaZaZ0DAFBgMrZXAwFzEVMBMG +A1UEAwwMRWQyNTUxOSBUZXN0MB4XDTIzMTAxNTAwMjMwOFoXDTMzMTAxMjAwMjMw +OFowFzEVMBMGA1UEAwwMRWQyNTUxOSBUZXN0MCowBQYDK2VwAyEAELP6AflXwsuZ +5q4NDIO0LP2iCdKRvds4nwsUmRhOw3ijUzBRMB0GA1UdDgQWBBRfxS9q0IemWxkH +4mwAwzr9dQx2xzAfBgNVHSMEGDAWgBRfxS9q0IemWxkH4mwAwzr9dQx2xzAPBgNV +HRMBAf8EBTADAQH/MAUGAytlcANBAI/+03iVq4yJ+DaLVs61w41cVX2UxKvquSzv +lllkpkclM9LH5dLrw4ArdTjS9zAjzY/02WkphHhICHXt3KqZTwI= +-----END CERTIFICATE-----`; + +/* +const ED25519_PUBKEY = `-----BEGIN PUBLIC KEY----- +MCowBQYDK2VwAyEAELP6AflXwsuZ5q4NDIO0LP2iCdKRvds4nwsUmRhOw3g= +-----END PUBLIC KEY-----`; +*/ + +const ED448_CERT = `-----BEGIN CERTIFICATE----- +MIIBijCCAQqgAwIBAgIUZaCS7zEjOnQ7O4KUFym6fJF5vl8wBQYDK2VxMBUxEzAR +BgNVBAMMCkVkNDQ4IFRlc3QwHhcNMjMxMDE1MDAyMzI1WhcNMzMxMDEyMDAyMzI1 +WjAVMRMwEQYDVQQDDApFZDQ0OCBUZXN0MEMwBQYDK2VxAzoAVN8kG0TMVyGOu/Ov +BTe8H0Wi4HJrQAlSv4XLwJbkuoi4EeRlEHQwXsNYLZTtY2Jra6AWhbVYYaEAo1Mw +UTAdBgNVHQ4EFgQUJFrepAf9YXrmDMSAzrMeYQmosd0wHwYDVR0jBBgwFoAUJFre +pAf9YXrmDMSAzrMeYQmosd0wDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXEDcwA+YiZj +puFr2aogfV1qg/ixk7qLi25BbKVNR6+7PEUjo7+4yBn9qnLbAHUGnHn7E96pSey9 +VkLqpoDNMRcM3Eb6h3AJpQM0oxGj8q9arjDXqJkXgaO2e0tVn8KKVfy7S8qO72Kd +rWzZowcOjnWKhXm7JgA= +-----END CERTIFICATE-----`; + +/* +const ED448_PUBKEY = `-----BEGIN PUBLIC KEY----- +MEMwBQYDK2VxAzoAVN8kG0TMVyGOu/OvBTe8H0Wi4HJrQAlSv4XLwJbkuoi4EeRl +EHQwXsNYLZTtY2Jra6AWhbVYYaEA +-----END PUBLIC KEY-----` +*/ + +TestRegister.addTests([ + { + name: "Public Key from Certificate: Missing footer", + input: RSA_CERT.substring(0, RSA_CERT.length / 2), + expectedOutput: "PEM footer '-----END CERTIFICATE-----' not found", + recipeConfig: [ + { + op: "Public Key from Certificate", + args: [], + } + ], + }, + + // test RSA certificate + { + name: "Public Key from Certificate: RSA", + input: RSA_CERT, + expectedOutput: (RSA_PUBKEY + "\n").replace(/\r/g, "").replace(/\n/g, "\r\n"), + recipeConfig: [ + { + op: "Public Key from Certificate", + args: [], + } + ], + }, + + // test EC certificate + { + name: "Public Key from Certificate: EC", + input: EC_P256_CERT, + expectedOutput: (EC_P256_PUBKEY + "\n").replace(/\r/g, "").replace(/\n/g, "\r\n"), + recipeConfig: [ + { + op: "Public Key from Certificate", + args: [], + } + ], + }, + + // test DSA certificate + { + name: "Public Key from Certificate: DSA", + input: DSA_CERT, + expectedOutput: (DSA_PUBKEY + "\n").replace(/\r/g, "").replace(/\n/g, "\r\n"), + recipeConfig: [ + { + op: "Public Key from Certificate", + args: [], + } + ], + }, + + // test EdDSA certificates + { + name: "Public Key from Certificate: Ed25519", + input: ED25519_CERT, + expectedOutput: "Unsupported public key type", + recipeConfig: [ + { + op: "Public Key from Certificate", + args: [], + } + ], + }, + { + name: "Public Key from Certificate: Ed448", + input: ED448_CERT, + expectedOutput: "Unsupported public key type", + recipeConfig: [ + { + op: "Public Key from Certificate", + args: [], + } + ], + }, + + // test multi-input + { + name: "Public Key from Certificate: Multiple certificates", + input: RSA_CERT + "\n" + EC_P256_CERT, + expectedOutput: (RSA_PUBKEY + "\n" + EC_P256_PUBKEY + "\n").replace(/\r/g, "").replace(/\n/g, "\r\n"), + recipeConfig: [ + { + op: "Public Key from Certificate", + args: [], + } + ], + } +]); diff --git a/tests/operations/tests/PubKeyFromPrivKey.mjs b/tests/operations/tests/PubKeyFromPrivKey.mjs new file mode 100644 index 00000000..25f14a69 --- /dev/null +++ b/tests/operations/tests/PubKeyFromPrivKey.mjs @@ -0,0 +1,254 @@ +/** + * Public Key from Private Key + * + * @author cplussharp + * @copyright Crown Copyright 2023 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +const RSA_PRIVKEY_PKCS1 = `-----BEGIN RSA PRIVATE KEY----- +MIIBOQIBAAJBAPKr0Dp6YdItzOfk6a7ma7L4BF4LnelMYKtboGLrk6ihtqFPZFRL +NcJi68Hvnt8stMrP50t6jqwWQ2EjMdkj6fsCAwEAAQJAOJUpM0lv36MAQR3WAwsF +F7DOy+LnigteCvaNWiNVxZ6jByB5Qb7sall/Qlu9sFI0ZwrlVcKS0kldee7JTYlL +WQIhAP3UKEfOtpTgT1tYmdhaqjxqMfxBom0Ri+rt9ajlzs6vAiEA9L85B8/Gnb7p +6Af7/wpmafL277OV4X4xBfzMR+TUzHUCIBq+VLQkInaTH6lXL3ZtLwyIf9W9MJjf +RWeuRLjT5bM/AiBF7Kw6kx5Hy1fAtydEApCoDIaIjWJw/kC7WTJ0B+jUUQIgV6dw +NSyj0feakeD890gmId+lvl/w/3oUXiczqvl/N9o= +-----END RSA PRIVATE KEY-----`; + +const RSA_PRIVKEY_PKCS8 = `-----BEGIN PRIVATE KEY----- +MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA8qvQOnph0i3M5+Tp +ruZrsvgEXgud6Uxgq1ugYuuTqKG2oU9kVEs1wmLrwe+e3yy0ys/nS3qOrBZDYSMx +2SPp+wIDAQABAkA4lSkzSW/fowBBHdYDCwUXsM7L4ueKC14K9o1aI1XFnqMHIHlB +vuxqWX9CW72wUjRnCuVVwpLSSV157slNiUtZAiEA/dQoR862lOBPW1iZ2FqqPGox +/EGibRGL6u31qOXOzq8CIQD0vzkHz8advunoB/v/CmZp8vbvs5XhfjEF/MxH5NTM +dQIgGr5UtCQidpMfqVcvdm0vDIh/1b0wmN9FZ65EuNPlsz8CIEXsrDqTHkfLV8C3 +J0QCkKgMhoiNYnD+QLtZMnQH6NRRAiBXp3A1LKPR95qR4Pz3SCYh36W+X/D/ehRe +JzOq+X832g== +-----END PRIVATE KEY-----`; + +const RSA_PUBKEY = `-----BEGIN PUBLIC KEY----- +MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPKr0Dp6YdItzOfk6a7ma7L4BF4LnelM +YKtboGLrk6ihtqFPZFRLNcJi68Hvnt8stMrP50t6jqwWQ2EjMdkj6fsCAwEAAQ== +-----END PUBLIC KEY-----`; + +const EC_P256_PRIVKEY_SEC1 = `-----BEGIN EC PRIVATE KEY----- +MHcCAQEEINtTjwUkgfAiSwqgcGAXWyE0ueIW6n2k395dmQZ3vGr4oAoGCCqGSM49 +AwEHoUQDQgAEDUc8A0EDNKoCYIPWMHz1yUzqE5mJgusgcAE8H6810fkJ8ZmTNiCC +a6sLgR2vD1VNh2diirWgKPH4PVMKav5e6Q== +-----END EC PRIVATE KEY-----`; + +const EC_P256_PRIVKEY_PKCS8 = `-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg21OPBSSB8CJLCqBw +YBdbITS54hbqfaTf3l2ZBne8avihRANCAAQNRzwDQQM0qgJgg9YwfPXJTOoTmYmC +6yBwATwfrzXR+QnxmZM2IIJrqwuBHa8PVU2HZ2KKtaAo8fg9Uwpq/l7p +-----END PRIVATE KEY-----`; + +const EC_P256_PUBKEY = `-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDUc8A0EDNKoCYIPWMHz1yUzqE5mJ +gusgcAE8H6810fkJ8ZmTNiCCa6sLgR2vD1VNh2diirWgKPH4PVMKav5e6Q== +-----END PUBLIC KEY-----`; + +const DSA_PRIVKEY_TRAD = `-----BEGIN DSA PRIVATE KEY----- +MIIDTQIBAAKCAQEAugtX67Pu8xhkIi7Bc2SBlrC8OBkAbVPsiIbTfRaCp6xE2dy6 +hRA6YAkKK43OSNU1JjTytpt7H9EiRYgu/E/3VKXOED23352nzunbfx5zEZrWwPat +77LflE/sjE1Pffrfc23AYi1ncGqoCGgya7DDDFq7QTzPBih1NOXBNGnhBU+kwfV4 +kJQF4TNxY/llnWVE2FHkzZOrNJTfJrn/hzdr2sjzqH1UhWDDzJW/DfLrJyXOMAub +ae0PmuKyR0xnqYyR9hDARsgZFMQtfoDstnSVfUKKyV6cX0BJvYTiDacZwxDS5IbM +Dhb2Z/rDCY9LqLBfn5C7+LqMKbmLft9DUfkcdQIdAMD5Sc8czo9kSB8V6fx2NmmX +9yXTkylaT52eKQUCggEANc5OkHDRYD5SKHEMb7qqozTmTFUxibPohFn/Tp99lIZU +U3oHHWLSze+TMzlXwawqmQqRKTtXx9NCcAgiUya3Vjt9/6eDv5N9ii0yu+8RiWiV +grTonZZahQDyIqnz6fny92iTL7Fma4aZy8ICiNUNsR5TbASpgTU/OscJI6g6IoHY +/h86HK4X6rETyeTNREiDZGZJKSpY6ReJbIrJ+lRoH7hwSKynhOEQ0Q+Z2e0TW8uH +avsQ4jGkTkPQ0DLbAUqp7eWN+ATpew9kqBUCC5ENWnN2YvpYNjsMz8FxLvbq/H6R +fnlAOSzO95yto2KueKvZCsH9rQlCMXpjrbIaYbsYXwKCAQBpN+w0N0b5IIAspXnl +J9yuB6ORk3j/5rZ+DUtTzW1YAJI6xjTcFQvN7FpVLkmLtXKUXF04R+sdGJ7VFwOb +0rbaL5vQzrqNkBrbgSzuzeloiG+7OLA6VeQtNbQh6OurrZFi9gY+qA5ciT9kQXyr +HudVXu956NDrooRxmv6JIVFvToaNiwe2vcgdkALw8HUbLFYof4SAE9jgU8EpxTp0 +2e8HzvVSVa6yj1nnGhpzLPlEqF8TZvs9pTg2kIk3/zvWojMJoPyTALfbTjbAeiFM +MeKNK/CKOOJj23AVAZxpMSR6cUbrIcRdKDnhCTVkkxXUecAIUs6Mk10kSfkuiGl9 +LjKjAhwpK4MOpkKEu+y308fZ+yZXypZW2m9Y/wOT0L4g +-----END DSA PRIVATE KEY-----`; + +const DSA_PRIVKEY_PKCS8 = `-----BEGIN PRIVATE KEY----- +MIICXAIBADCCAjUGByqGSM44BAEwggIoAoIBAQC6C1frs+7zGGQiLsFzZIGWsLw4 +GQBtU+yIhtN9FoKnrETZ3LqFEDpgCQorjc5I1TUmNPK2m3sf0SJFiC78T/dUpc4Q +PbffnafO6dt/HnMRmtbA9q3vst+UT+yMTU99+t9zbcBiLWdwaqgIaDJrsMMMWrtB +PM8GKHU05cE0aeEFT6TB9XiQlAXhM3Fj+WWdZUTYUeTNk6s0lN8muf+HN2vayPOo +fVSFYMPMlb8N8usnJc4wC5tp7Q+a4rJHTGepjJH2EMBGyBkUxC1+gOy2dJV9QorJ +XpxfQEm9hOINpxnDENLkhswOFvZn+sMJj0uosF+fkLv4uowpuYt+30NR+Rx1Ah0A +wPlJzxzOj2RIHxXp/HY2aZf3JdOTKVpPnZ4pBQKCAQA1zk6QcNFgPlIocQxvuqqj +NOZMVTGJs+iEWf9On32UhlRTegcdYtLN75MzOVfBrCqZCpEpO1fH00JwCCJTJrdW +O33/p4O/k32KLTK77xGJaJWCtOidllqFAPIiqfPp+fL3aJMvsWZrhpnLwgKI1Q2x +HlNsBKmBNT86xwkjqDoigdj+HzocrhfqsRPJ5M1ESINkZkkpKljpF4lsisn6VGgf +uHBIrKeE4RDRD5nZ7RNby4dq+xDiMaROQ9DQMtsBSqnt5Y34BOl7D2SoFQILkQ1a +c3Zi+lg2OwzPwXEu9ur8fpF+eUA5LM73nK2jYq54q9kKwf2tCUIxemOtshphuxhf +BB4CHCkrgw6mQoS77LfTx9n7JlfKllbab1j/A5PQviA= +-----END PRIVATE KEY-----`; + +const DSA_PUBKEY = `-----BEGIN PUBLIC KEY----- +MIIDQjCCAjUGByqGSM44BAEwggIoAoIBAQC6C1frs+7zGGQiLsFzZIGWsLw4GQBt +U+yIhtN9FoKnrETZ3LqFEDpgCQorjc5I1TUmNPK2m3sf0SJFiC78T/dUpc4QPbff +nafO6dt/HnMRmtbA9q3vst+UT+yMTU99+t9zbcBiLWdwaqgIaDJrsMMMWrtBPM8G +KHU05cE0aeEFT6TB9XiQlAXhM3Fj+WWdZUTYUeTNk6s0lN8muf+HN2vayPOofVSF +YMPMlb8N8usnJc4wC5tp7Q+a4rJHTGepjJH2EMBGyBkUxC1+gOy2dJV9QorJXpxf +QEm9hOINpxnDENLkhswOFvZn+sMJj0uosF+fkLv4uowpuYt+30NR+Rx1Ah0AwPlJ +zxzOj2RIHxXp/HY2aZf3JdOTKVpPnZ4pBQKCAQA1zk6QcNFgPlIocQxvuqqjNOZM +VTGJs+iEWf9On32UhlRTegcdYtLN75MzOVfBrCqZCpEpO1fH00JwCCJTJrdWO33/ +p4O/k32KLTK77xGJaJWCtOidllqFAPIiqfPp+fL3aJMvsWZrhpnLwgKI1Q2xHlNs +BKmBNT86xwkjqDoigdj+HzocrhfqsRPJ5M1ESINkZkkpKljpF4lsisn6VGgfuHBI +rKeE4RDRD5nZ7RNby4dq+xDiMaROQ9DQMtsBSqnt5Y34BOl7D2SoFQILkQ1ac3Zi ++lg2OwzPwXEu9ur8fpF+eUA5LM73nK2jYq54q9kKwf2tCUIxemOtshphuxhfA4IB +BQACggEAaTfsNDdG+SCALKV55SfcrgejkZN4/+a2fg1LU81tWACSOsY03BULzexa +VS5Ji7VylFxdOEfrHRie1RcDm9K22i+b0M66jZAa24Es7s3paIhvuziwOlXkLTW0 +Iejrq62RYvYGPqgOXIk/ZEF8qx7nVV7veejQ66KEcZr+iSFRb06GjYsHtr3IHZAC +8PB1GyxWKH+EgBPY4FPBKcU6dNnvB871UlWuso9Z5xoacyz5RKhfE2b7PaU4NpCJ +N/871qIzCaD8kwC32042wHohTDHijSvwijjiY9twFQGcaTEkenFG6yHEXSg54Qk1 +ZJMV1HnACFLOjJNdJEn5LohpfS4yow== +-----END PUBLIC KEY-----`; + +const ED25519_PRIVKEY = `-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIC18vtoHINC8Mo9dTIqOrBs3J28ZvHrwzRq57g2kpV98 +-----END PRIVATE KEY-----`; + +/* +const ED25519_PUBKEY = `-----BEGIN PUBLIC KEY----- +MCowBQYDK2VwAyEAELP6AflXwsuZ5q4NDIO0LP2iCdKRvds4nwsUmRhOw3g= +-----END PUBLIC KEY-----`; +*/ + +const ED448_PRIVKEY = `-----BEGIN PRIVATE KEY----- +MEcCAQAwBQYDK2VxBDsEOWdGJ06bDcWznJhBoQqPeTfsCe+AvBv1n7KfIGYzR4tv +1kcwHnbxlemnCMgqvbrRXaLuFUBysUZThA== +-----END PRIVATE KEY-----`; + +/* +const ED448_PUBKEY = `-----BEGIN PUBLIC KEY----- +MEMwBQYDK2VxAzoAVN8kG0TMVyGOu/OvBTe8H0Wi4HJrQAlSv4XLwJbkuoi4EeRl +EHQwXsNYLZTtY2Jra6AWhbVYYaEA +-----END PUBLIC KEY-----`; +*/ + +TestRegister.addTests([ + { + name: "Public Key from Private Key: Missing footer", + input: RSA_PRIVKEY_PKCS1.substring(0, RSA_PRIVKEY_PKCS1.length / 2), + expectedOutput: "PEM footer '-----END RSA PRIVATE KEY-----' not found", + recipeConfig: [ + { + op: "Public Key from Private Key", + args: [], + } + ], + }, + + // test RSA + { + name: "Public Key from Private Key: RSA PKCS#1", + input: RSA_PRIVKEY_PKCS1, + expectedOutput: (RSA_PUBKEY + "\n").replace(/\r/g, "").replace(/\n/g, "\r\n"), + recipeConfig: [ + { + op: "Public Key from Private Key", + args: [], + } + ], + }, + { + name: "Public Key from Private Key: RSA PKCS#8", + input: RSA_PRIVKEY_PKCS8, + expectedOutput: (RSA_PUBKEY + "\n").replace(/\r/g, "").replace(/\n/g, "\r\n"), + recipeConfig: [ + { + op: "Public Key from Private Key", + args: [], + } + ], + }, + + // test EC certificate + { + name: "Public Key from Private Key: EC SEC1", + input: EC_P256_PRIVKEY_SEC1, + expectedOutput: (EC_P256_PUBKEY + "\n").replace(/\r/g, "").replace(/\n/g, "\r\n"), + recipeConfig: [ + { + op: "Public Key from Private Key", + args: [], + } + ], + }, + { + name: "Public Key from Private Key: EC PKCS#8", + input: EC_P256_PRIVKEY_PKCS8, + expectedOutput: (EC_P256_PUBKEY + "\n").replace(/\r/g, "").replace(/\n/g, "\r\n"), + recipeConfig: [ + { + op: "Public Key from Private Key", + args: [], + } + ], + }, + + // test DSA + { + name: "Public Key from Private Key: DSA Traditional", + input: DSA_PRIVKEY_TRAD, + expectedOutput: (DSA_PUBKEY + "\n").replace(/\r/g, "").replace(/\n/g, "\r\n"), + recipeConfig: [ + { + op: "Public Key from Private Key", + args: [], + } + ], + }, + { + name: "Public Key from Private Key: DSA PKCS#8", + input: DSA_PRIVKEY_PKCS8, + expectedOutput: "DSA Private Key in PKCS#8 is not supported", + recipeConfig: [ + { + op: "Public Key from Private Key", + args: [], + } + ], + }, + + // test EdDSA + { + name: "Public Key from Private Key: Ed25519", + input: ED25519_PRIVKEY, + expectedOutput: "Unsupported key type: Error: malformed PKCS8 private key(code:004)", + recipeConfig: [ + { + op: "Public Key from Private Key", + args: [], + } + ], + }, + { + name: "Public Key from Private Key: Ed448", + input: ED448_PRIVKEY, + expectedOutput: "Unsupported key type: Error: malformed PKCS8 private key(code:004)", + recipeConfig: [ + { + op: "Public Key from Private Key", + args: [], + } + ], + }, + + // test multi-input + { + name: "Public Key from Private Key: Multiple keys", + input: RSA_PRIVKEY_PKCS8 + "\n" + EC_P256_PRIVKEY_PKCS8, + expectedOutput: (RSA_PUBKEY + "\n" + EC_P256_PUBKEY + "\n").replace(/\r/g, "").replace(/\n/g, "\r\n"), + recipeConfig: [ + { + op: "Public Key from Private Key", + args: [], + } + ], + } +]);