"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const core_1 = require("@oclif/core");
const x509 = tslib_1.__importStar(require("@peculiar/x509"));
const webcrypto_1 = require("@peculiar/webcrypto");
const fs = tslib_1.__importStar(require("fs-extra"));
const keyImporter_1 = require("../sign/keyImporter");
const inquirer_1 = tslib_1.__importDefault(require("inquirer"));
const x509_1 = require("@peculiar/x509");
const crypto = new webcrypto_1.Crypto();
x509.cryptoProvider.set(crypto);
class Certificate extends core_1.Command {
    async run() {
        const { args, flags } = await this.parse(Certificate);
        if (args['command'] === "request") {
            await this.createCertificateRequest(flags);
        }
        else if (args['command'] === "sign") {
            await this.createCertificateFromRequest(flags);
        }
    }
    async createCertificateRequest(flags) {
        const privateKeyLocation = flags.privateKeyFile.toString();
        if (!fs.existsSync(privateKeyLocation)) {
            this.error("Private key cannot be read at " + privateKeyLocation);
            return;
        }
        const privKeyStr = fs.readFileSync(privateKeyLocation, 'utf-8').trim();
        const keyImporter = keyImporter_1.KeyImporter.fromPrivate(privKeyStr);
        const cn = await this.getCn(flags.cn.toString());
        this.log("Creating certificate signing request...");
        const csr = await x509.Pkcs10CertificateRequestGenerator.create({
            name: "CN=" + cn,
            keys: {
                privateKey: await keyImporter.getPrivateKey(),
                publicKey: await keyImporter.getPublicKey()
            },
            signingAlgorithm: { name: "ECDSA", hash: "SHA-256" },
            extensions: [
                new x509.KeyUsagesExtension(x509.KeyUsageFlags.digitalSignature | x509.KeyUsageFlags.keyEncipherment),
            ],
            attributes: [
                new x509.ChallengePasswordAttribute("password"),
            ]
        });
        await csr.verify();
        //console.log(csr.toTextObject());
        const pem = x509_1.PemConverter.encode(csr.rawData, "NEW CERTIFICATE REQUEST");
        //console.log(pem);
        const reqLocation = flags.certRequestFile.toString();
        this.checkAndWriteFile(reqLocation, pem);
        this.log("Successfully created certificate request at: " + reqLocation);
    }
    async createCertificateFromRequest(flags) {
        const certRequestLocation = flags.certRequestFile.toString();
        if (!fs.existsSync(certRequestLocation)) {
            this.error("Certificate request cannot be read at " + certRequestLocation);
            return;
        }
        const masterPrivKeyFile = flags.masterPrivateKeyFile.toString();
        if (!fs.existsSync(masterPrivKeyFile)) {
            this.error("Master private key cannot be read at " + masterPrivKeyFile);
            return;
        }
        const pem = fs.readFileSync(certRequestLocation, 'utf-8').trim();
        const csr = new x509.Pkcs10CertificateRequest(pem);
        await csr.verify();
        let signerPrivKeyStr = fs.readFileSync(masterPrivKeyFile, 'utf-8').trim();
        let signerKeyImporter = keyImporter_1.KeyImporter.fromPrivate(signerPrivKeyStr);
        const issuerCn = await this.getCn(flags.cn.toString());
        this.log("Creating certificate from signing request...");
        const masterKeyExt = new x509.Extension("2.5.29.18", true, await signerKeyImporter.getRawPublicKey());
        // TODO: use parameter
        const expiry = new Date();
        expiry.setFullYear(expiry.getFullYear() + 2); // default two year validity
        const cert = await x509.X509CertificateGenerator.create({
            serialNumber: "01",
            subject: csr.subjectName,
            issuer: "CN=" + issuerCn,
            notBefore: new Date(),
            notAfter: expiry,
            signingAlgorithm: { name: "ECDSA", hash: "SHA-256" },
            publicKey: csr.publicKey,
            signingKey: await signerKeyImporter.getPrivateKey(),
            extensions: [
                new x509.BasicConstraintsExtension(false, 0, true),
                // new x509.ExtendedKeyUsageExtension(["1.2.3.4.5.6.7", "2.3.4.5.6.7.8"], true),
                new x509.KeyUsagesExtension(x509.KeyUsageFlags.digitalSignature, true),
                await x509.SubjectKeyIdentifierExtension.create(csr.publicKey),
                await x509.AuthorityKeyIdentifierExtension.create(await signerKeyImporter.getPublicKey()),
                masterKeyExt
            ]
        });
        //console.log(cert.toTextObject());
        const certPem = x509_1.PemConverter.encode(cert.rawData, "CERTIFICATE");
        //console.log(certPem);
        const certLocation = flags.certFile.toString();
        this.checkAndWriteFile(certLocation, certPem);
        this.log("Successfully created certificate at: " + certLocation);
    }
    async getCn(cn) {
        if (!cn) {
            let responses = await inquirer_1.default.prompt([{
                    name: "CN",
                    message: "Please enter the CN for the certificate request (This should be a human readable domain or other unique identifier): ",
                    type: "input",
                    validate: (val) => {
                        return val != "";
                    }
                }]);
            cn = responses["CN"];
        }
        return cn;
    }
    checkAndWriteFile(fileLocation, data) {
        // TODO: Prompt if overwriting existing file
        if (fs.existsSync(fileLocation)) {
            this.error("File already exists at: " + fileLocation);
            return;
        }
        fs.writeFileSync(fileLocation, data, 'utf-8');
    }
}
exports.default = Certificate;
Certificate.CERT_REQUEST_FILENAME = "ts-certificate-request.pem";
Certificate.CERT_FILENAME = "ts-certificate.pem";
Certificate.description = 'Create a certificate request or sign an existing request.';
Certificate.flags = {
    privateKeyFile: core_1.Flags.string({ char: 'k', description: 'Hex encoded private key filename (for creating CSR)', default: "ts-signing.key" }),
    masterPrivateKeyFile: core_1.Flags.string({ char: 'm', description: 'Hex encoded master private key filename (for signing CSR)', default: "ts-master.key" }),
    certRequestFile: core_1.Flags.string({ char: 'r', description: 'Certificate signing request PEM input or output filename', default: Certificate.CERT_REQUEST_FILENAME }),
    certFile: core_1.Flags.string({ char: 'r', description: 'Certificate PEM input or output filename', default: Certificate.CERT_FILENAME }),
    cn: core_1.Flags.string({ char: 'c', description: 'The CN for the certificate, or issuer CN if signing', default: "" }),
};
Certificate.args = [
    {
        name: 'command',
        description: "Whether to create a signing 'request' or 'sign' an existing request",
        required: true,
        options: ["request", "sign"]
    }
];
