# frozen_string_literal: true require 'bolt/secret/base' require 'fileutils' module Bolt class Plugin class Pkcs7 < Bolt::Secret::Base def self.validate_config(config) known_keys = %w[private-key public-key keysize] known_keys.each do |key| unless key.is_a? String raise Bolt::ValidationError, "Invalid config for pkcs7 plugin: '#{key}' is not a String" end end config.keys.each do |key| unless known_keys.include?(key) raise Bolt::ValidationError, "Unpexpected key in pkcs7 plugin config: #{key}" end end end def name 'pkcs7' end def initialize(boltdir, options) self.class.validate_config(options) require 'openssl' @boltdir = boltdir @options = options || {} @logger = Logging.logger[self] end def private_key_path path = @options['private-key'] || 'keys/private_key.pkcs7.pem' path = File.absolute_path(path, @boltdir) @logger.debug("Using private-key: #{path}") path end def private_key @private_key ||= OpenSSL::PKey::RSA.new(File.read(private_key_path)) end def public_key_path path = @options['public-key'] || 'keys/public_key.pkcs7.pem' path = File.absolute_path(path, @boltdir) @logger.debug("Using public-key: #{path}") path end def public_key @public_key ||= OpenSSL::X509::Certificate.new(File.read(public_key_path)) end def keysize @options['keysize'] || 2048 end # The following implementations are intended to be compatible with hiera-eyaml def encrypt_value(plaintext) cipher = OpenSSL::Cipher::AES.new(256, :CBC) OpenSSL::PKCS7.encrypt([public_key], plaintext, cipher, OpenSSL::PKCS7::BINARY).to_der end def decrypt_value(ciphertext) pkcs7 = OpenSSL::PKCS7.new(ciphertext) pkcs7.decrypt(private_key, public_key) end def secret_createkeys key = OpenSSL::PKey::RSA.new(keysize) cert = OpenSSL::X509::Certificate.new cert.subject = OpenSSL::X509::Name.parse('/') cert.serial = 1 cert.version = 2 cert.not_before = Time.now cert.not_after = Time.now + 50 * 365 * 24 * 60 * 60 cert.public_key = key.public_key cert.sign(key, OpenSSL::Digest.new('SHA512')) @logger.warn("Overwriting private-key '#{private_key_path}'") if File.exist?(private_key_path) @logger.warn("Overwriting public-key '#{public_key_path}'") if File.exist?(public_key_path) private_keydir = File.dirname(private_key_path) FileUtils.mkdir_p(private_keydir) unless File.exist?(private_keydir) FileUtils.touch(private_key_path) File.chmod(0o600, private_key_path) File.write(private_key_path, key.to_pem) public_keydir = File.dirname(public_key_path) FileUtils.mkdir_p(public_keydir) unless File.exist?(public_keydir) File.write(public_key_path, cert.to_pem) end end end end