# frozen_string_literal: true require "openssl" module ShopifyCLI module Theme module DevServer class CertificateManager attr_reader :ctx, :domain_name, :certificate, :private_key ISSUER_EXTENSIONS = [ ["subjectKeyIdentifier", "hash", false], ["authorityKeyIdentifier", "keyid:always", false], ] def initialize(ctx, domain_name) @ctx = ctx @domain_name = domain_name end def find_or_create_certificates! @private_key = if (private_key_pem = ShopifyCLI::DB.get(:ssl_private_key)) OpenSSL::PKey::RSA.new(private_key_pem) else OpenSSL::PKey::RSA.new(2048) end @certificate = if (certificate_pem = ShopifyCLI::DB.get(:ssl_certificate)) OpenSSL::X509::Certificate.new(certificate_pem) else x509_certificate = build_x509_certificate sign_certificate!(x509_certificate) x509_certificate end ShopifyCLI::DB.set(ssl_certificate: certificate.to_pem) ShopifyCLI::DB.set(ssl_private_key: private_key.to_pem) end private def build_x509_certificate certificate = OpenSSL::X509::Certificate.new certificate.public_key = private_key.public_key certificate.subject = subject certificate.version = 2 certificate.serial = 0x0 certificate.not_before = Time.now.utc certificate.not_after = Time.now.utc + 365 * 24 * 60 * 60 certificate end def sign_certificate!(certificate) ef = OpenSSL::X509::ExtensionFactory.new ef.subject_certificate = certificate ef.issuer_certificate = certificate ISSUER_EXTENSIONS.each do |args| certificate.add_extension(ef.create_extension(*args)) end certificate.add_extension(ef.create_extension("subjectAltName", "DNS:#{@domain_name}", false)) certificate.sign(private_key, OpenSSL::Digest.new("SHA256")) end def subject OpenSSL::X509::Name.parse("/CN=#{@domain_name}/") end end end end end