text: const debug = require('debug')('aio-cli-plugin-certificate:helpers')
  const forge = require('node-forge')
  const pki = forge.pki
  
  /**
    openssl req -x509 -sha256 -nodes -days 365 -subj "/C=US/" -newkey rsa:2048 -keyout private.key -out certificate_pub.crt
    -x509          output a x509 structure instead of a cert. req.
    -nodes         don't encrypt the output key
    -days          number of days a certificate generated by -x509 is valid for.
    -subj arg      set or modify request subject
    -newkey rsa:bits generate a new RSA key of 'bits' in size
    -keyout arg    file to send the key to
    -out arg       output file
  */
  
  /**
   * Generates a certificate and a private key pair
   * Similar to openssl req -x509 -sha256 -nodes -days 365 -subj "/C=US/" -newkey rsa:2048
   *
   * @param {string} commonName
   * @param {number} days (integer)
   * @param {object} [attributes={}] optional certificate attributes
   * @param {string} [attributes.country]
   * @param {string} [attributes.state]
   * @param {string} [attributes.locality]
   * @param {string} [attributes.organization]
   * @param {string} [attributes.unit]
   * @returns {{privateKey: string, cert: string}} key pair
   */
  function generate (commonName, days, /* istanbul ignore next */ attributes = {}) {
    debug('generating a certificate with ', commonName, attributes)
    // generates a keypair
  
    const keys = pki.rsa.generateKeyPair(2048)
    // create a new certificate
    const cert = pki.createCertificate()
  
    // fill the required fields
    cert.publicKey = keys.publicKey
    cert.serialNumber = Date.now().toString()
    cert.validity.notBefore = new Date()
    cert.validity.notAfter = new Date()
    cert.validity.notAfter.setDate(cert.validity.notAfter.getDate() + days)
  
    const attrs = []
  
    attrs.push({ name: 'commonName', value: commonName })
  
    if (attributes.country) {
      attrs.push({ name: 'countryName', value: attributes.country })
    }
  
    if (attributes.state) {
      attrs.push({ shortName: 'ST', value: attributes.state })
    }
  
    if (attributes.locality) {
      attrs.push({ name: 'localityName', value: attributes.locality })
    }
  
    if (attributes.organization) {
      attrs.push({ name: 'organizationName', value: attributes.organization })
    }
  
    if (attributes.unit) {
      attrs.push({ shortName: 'OU', value: attributes.unit })
    }
  
    // set subject and issuer
    cert.setSubject(attrs)
    cert.setIssuer(attrs)
  
    cert.setExtensions([
      {
        name: 'basicConstraints',
        cA: false
      },
      {
        name: 'keyUsage',
        keyCertSign: true,
        digitalSignature: true,
        nonRepudiation: true,
        keyEncipherment: true,
        dataEncipherment: true
      },
      {
        name: 'extKeyUsage',
        serverAuth: true,
        clientAuth: true,
        codeSigning: true,
        emailProtection: true,
        timeStamping: true
      },
      {
        name: 'subjectAltName',
        altNames: [{
          type: 2, // DNS
          value: commonName
        }]
      }
    ])
  
    // sign the cert
    cert.sign(keys.privateKey, forge.md.sha256.create())
    const pk = pki.privateKeyToPem(keys.privateKey)
    debug('generated private key')
    // convert cert to PEM format
    const pemCert = pki.certificateToPem(cert)
    return {
      privateKey: pk,
      cert: pemCert
    }
  }
  
  /**
   * Verifies a valid pem certificate and returns information about its validity
   * Throws if the input is not a valid pem certificate.
   *
   * @param {string|Buffer} pemCert
   * @returns {{verified: boolean, validUntil: Date, validSince: Date}} key pair
   */
  function verify (pemCert) {
    // this will throw if not a valid pem
    const cert = pki.certificateFromPem(pemCert)
  
    cert.verify(cert)
    const caStore = pki.createCaStore([cert])
    const isPkiVerified = pki.verifyCertificateChain(caStore, [cert])
  
    return {
      verified: isPkiVerified,
      validUntil: cert.validity.notAfter,
      validSince: cert.validity.notBefore
    }
  }
  
  module.exports = {
    generate,
    verify
  }
  