module Gem::Security

def self.alt_name_or_x509_entry(certificate, x509_entry)

def self.alt_name_or_x509_entry(certificate, x509_entry)
  alt_name = certificate.extensions.find do |extension|
    extension.oid == "#{x509_entry}AltName"
  end
  return alt_name.value if alt_name
  certificate.send x509_entry
end

def self.create_cert(subject, key, age = ONE_YEAR, extensions = EXTENSIONS, serial = 1)

def self.create_cert(subject, key, age = ONE_YEAR, extensions = EXTENSIONS, serial = 1)
  cert = OpenSSL::X509::Certificate.new
  cert.public_key = get_public_key(key)
  cert.version    = 2
  cert.serial     = serial
  cert.not_before = Time.now
  cert.not_after  = Time.now + age
  cert.subject    = subject
  ef = OpenSSL::X509::ExtensionFactory.new nil, cert
  cert.extensions = extensions.map do |ext_name, value|
    ef.create_extension ext_name, value
  end
  cert
end

def self.create_cert_email(email, key, age = ONE_YEAR, extensions = EXTENSIONS)

def self.create_cert_email(email, key, age = ONE_YEAR, extensions = EXTENSIONS)
  subject = email_to_name email
  extensions = extensions.merge "subjectAltName" => "email:#{email}"
  create_cert_self_signed subject, key, age, extensions
end

def self.create_cert_self_signed(subject, key, age = ONE_YEAR, extensions = EXTENSIONS, serial = 1)

def self.create_cert_self_signed(subject, key, age = ONE_YEAR, extensions = EXTENSIONS, serial = 1)
  certificate = create_cert subject, key, age, extensions
  sign certificate, key, certificate, age, extensions, serial
end

def self.create_digest(algorithm = DIGEST_NAME)

def self.create_digest(algorithm = DIGEST_NAME)
  OpenSSL::Digest.new(algorithm)
end

def self.create_key(algorithm)

def self.create_key(algorithm)
  if defined?(OpenSSL::PKey)
    case algorithm.downcase
    when "dsa"
      OpenSSL::PKey::DSA.new(RSA_DSA_KEY_LENGTH)
    when "rsa"
      OpenSSL::PKey::RSA.new(RSA_DSA_KEY_LENGTH)
    when "ec"
      OpenSSL::PKey::EC.generate(EC_NAME)
    else
      raise Gem::Security::Exception,
      "#{algorithm} algorithm not found. RSA, DSA, and EC algorithms are supported."
    end
  end
end

def self.email_to_name(email_address)

def self.email_to_name(email_address)
  email_address = email_address.gsub(/[^\w@.-]+/i, "_")
  cn, dcs = email_address.split "@"
  dcs = dcs.split "."
  OpenSSL::X509::Name.new([
    ["CN", cn],
    *dcs.map {|dc| ["DC", dc] },
  ])
end

def self.get_public_key(key)

def self.get_public_key(key)
  # Ruby 3.0 (Ruby/OpenSSL 2.2) or later
  return OpenSSL::PKey.read(key.public_to_der) if key.respond_to?(:public_to_der)
  return key.public_key unless key.is_a?(OpenSSL::PKey::EC)
  ec_key = OpenSSL::PKey::EC.new(key.group.curve_name)
  ec_key.public_key = key.public_key
  ec_key
end

def self.re_sign(expired_certificate, private_key, age = ONE_YEAR, extensions = EXTENSIONS)

def self.re_sign(expired_certificate, private_key, age = ONE_YEAR, extensions = EXTENSIONS)
  raise Gem::Security::Exception,
        "incorrect signing key for re-signing " +
        expired_certificate.subject.to_s unless
    expired_certificate.check_private_key(private_key)
  unless expired_certificate.subject.to_s ==
         expired_certificate.issuer.to_s
    subject = alt_name_or_x509_entry expired_certificate, :subject
    issuer  = alt_name_or_x509_entry expired_certificate, :issuer
    raise Gem::Security::Exception,
          "#{subject} is not self-signed, contact #{issuer} " \
          "to obtain a valid certificate"
  end
  serial = expired_certificate.serial + 1
  create_cert_self_signed(expired_certificate.subject, private_key, age,
                          extensions, serial)
end

def self.reset

def self.reset
  @trust_dir = nil
end

def self.sign(certificate, signing_key, signing_cert, age = ONE_YEAR, extensions = EXTENSIONS, serial = 1)

def self.sign(certificate, signing_key, signing_cert, age = ONE_YEAR, extensions = EXTENSIONS, serial = 1)
  signee_subject = certificate.subject
  signee_key     = certificate.public_key
  alt_name = certificate.extensions.find do |extension|
    extension.oid == "subjectAltName"
  end
  extensions = extensions.merge "subjectAltName" => alt_name.value if
    alt_name
  issuer_alt_name = signing_cert.extensions.find do |extension|
    extension.oid == "subjectAltName"
  end
  extensions = extensions.merge "issuerAltName" => issuer_alt_name.value if
    issuer_alt_name
  signed = create_cert signee_subject, signee_key, age, extensions, serial
  signed.issuer = signing_cert.subject
  signed.sign signing_key, Gem::Security::DIGEST_NAME
end

def self.trust_dir

def self.trust_dir
  return @trust_dir if @trust_dir
  dir = File.join Gem.user_home, ".gem", "trust"
  @trust_dir ||= Gem::Security::TrustDir.new dir
end

def self.trusted_certificates(&block)

def self.trusted_certificates(&block)
  trust_dir.each_certificate(&block)
end

def self.write(pemmable, path, permissions = 0o600, passphrase = nil, cipher = KEY_CIPHER)

def self.write(pemmable, path, permissions = 0o600, passphrase = nil, cipher = KEY_CIPHER)
  path = File.expand_path path
  File.open path, "wb", permissions do |io|
    if passphrase && cipher
      io.write pemmable.to_pem cipher, passphrase
    else
      io.write pemmable.to_pem
    end
  end
  path
end