require 'tempfile' module Rgpg module GpgHelper def self.generate_key_pair(key_base_name, recipient, real_name) public_key_file_name = "#{key_base_name}.pub" private_key_file_name = "#{key_base_name}.sec" script = generate_key_script(public_key_file_name, private_key_file_name, recipient, real_name) script_file = Tempfile.new('gpg-script') begin script_file.write(script) script_file.close result = system("gpg --batch --gen-key #{script_file.path}") raise RuntimeError.new('gpg failed') unless result ensure script_file.close script_file.unlink end end def self.encrypt_file(public_key_file_name, input_file_name, output_file_name) raise ArgumentError.new("Public key file \"#{public_key_file_name}\" does not exist") unless File.exist?(public_key_file_name) raise ArgumentError.new("Input file \"#{input_file_name}\" does not exist") unless File.exist?(input_file_name) recipient = get_recipient(public_key_file_name) with_temporary_encrypt_keyring(public_key_file_name) do |keyring_file_name| run_gpg( '--keyring', keyring_file_name, '--output', output_file_name, '--encrypt', '--recipient', recipient, '--yes', '--trust-model', 'always', input_file_name ) end end def self.decrypt_file(public_key_file_name, private_key_file_name, input_file_name, output_file_name) raise ArgumentError.new("Public key file \"#{public_key_file_name}\" does not exist") unless File.exist?(public_key_file_name) raise ArgumentError.new("Private key file \"#{public_key_file_name}\" does not exist") unless File.exist?(private_key_file_name) raise ArgumentError.new("Input file \"#{input_file_name}\" does not exist") unless File.exist?(input_file_name) recipient = get_recipient(private_key_file_name) with_temporary_decrypt_keyrings(public_key_file_name, private_key_file_name) do |keyring_file_name, secret_keyring_file_name| run_gpg( '--keyring', keyring_file_name, '--secret-keyring', secret_keyring_file_name, '--output', output_file_name, '--decrypt', '--yes', '--trust-model', 'always', input_file_name ) end end private def self.run_gpg(*args) fragments = [ 'gpg', '--no-default-keyring' ] + args command_line = fragments.join(' ') output_file = Tempfile.new('gpg-output') begin output_file.close result = system("#{command_line} > #{output_file.path} 2>&1") ensure output_file.unlink end raise RuntimeError.new('gpg failed') unless result end def self.generate_key_script(public_key_file_name, private_key_file_name, recipient, real_name) <<-EOS %echo Generating a standard key Key-Type: DSA Key-Length: 1024 Subkey-Type: ELG-E Subkey-Length: 1024 Name-Real: #{real_name} Name-Comment: Key automatically generated by rgpg Name-Email: #{recipient} Expire-Date: 0 %pubring #{public_key_file_name} %secring #{private_key_file_name} # Do a commit here, so that we can later print "done" :-) %commit %echo done EOS end def self.get_recipient(key_file_name) result = `gpg #{key_file_name}`.lines.first.chomp raise RuntimeError.new('gpg failed') unless $? result =~ /^(pub|sec)\s+\d+D\/([0-9a-fA-F]{8}).+<(.+)>/ or raise RuntimeError.new('Invalid output') key_id = $2 recipient = $3 recipient end def self.with_temporary_encrypt_keyring(public_key_file_name) with_temporary_keyring_file do |keyring_file_name| run_gpg( '--keyring', keyring_file_name, '--import', public_key_file_name ) yield keyring_file_name end end def self.with_temporary_decrypt_keyrings(public_key_file_name, private_key_file_name) with_temporary_keyring_file do |keyring_file_name| with_temporary_keyring_file do |secret_keyring_file_name| run_gpg( '--keyring', keyring_file_name, '--secret-keyring', secret_keyring_file_name, '--import', private_key_file_name ) yield keyring_file_name, secret_keyring_file_name end end end def self.with_temporary_keyring_file keyring_file = Tempfile.new('gpg-key-ring') begin keyring_file_name = keyring_file.path keyring_file.close keyring_file.unlink yield keyring_file_name ensure File.unlink(keyring_file_name) if File.exist?(keyring_file_name) backup_keyring_file_name = "#{keyring_file_name}~" File.unlink(backup_keyring_file_name) if File.exist?(backup_keyring_file_name) end end end end