From f4dec575ed918445a599b3851981b2589b27d634 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 24 Oct 2014 09:33:44 -0700 Subject: [PATCH] core: Vagrant::Util::Keypair for generating keypairs --- lib/vagrant/util/keypair.rb | 46 ++++++++++++++++++++++++++ test/unit/vagrant/util/keypair_test.rb | 34 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 lib/vagrant/util/keypair.rb create mode 100644 test/unit/vagrant/util/keypair_test.rb diff --git a/lib/vagrant/util/keypair.rb b/lib/vagrant/util/keypair.rb new file mode 100644 index 000000000..7fec84b2a --- /dev/null +++ b/lib/vagrant/util/keypair.rb @@ -0,0 +1,46 @@ +require "base64" +require "openssl" + +module Vagrant + module Util + class Keypair + # Creates an SSH keypair and returns it. + # + # @param [String] password Password for the key, or nil for no password. + # @return [Array] PEM-encoded public and private key, + # respectively. The final element is the OpenSSH encoded public + # key. + def self.create(password=nil) + rsa_key = OpenSSL::PKey::RSA.new(2048) + public_key = rsa_key.public_key + private_key = rsa_key.to_pem + + if password + cipher = OpenSSL::Cipher::Cipher.new('des3') + private_key = rsa_key.to_pem(cipher, password) + end + + # Generate the binary necessary for the OpenSSH public key. + binary = [7].pack("N") + binary += "ssh-rsa" + ["e", "n"].each do |m| + val = public_key.send(m) + data = val.to_s(2) + + first_byte = data[0,1].unpack("c").first + if val < 0 + data[0] = [0x80 & first_byte].pack("c") + elsif first_byte < 0 + data = 0.chr + data + end + + binary += [data.length].pack("N") + data + end + + openssh_key = "ssh-rsa #{Base64.encode64(binary).gsub("\n", "")} vagrant" + public_key = public_key.to_pem + return [public_key, private_key, openssh_key] + end + end + end +end diff --git a/test/unit/vagrant/util/keypair_test.rb b/test/unit/vagrant/util/keypair_test.rb new file mode 100644 index 000000000..30ab76eba --- /dev/null +++ b/test/unit/vagrant/util/keypair_test.rb @@ -0,0 +1,34 @@ +require "openssl" + +require File.expand_path("../../../base", __FILE__) + +require "vagrant/util/keypair" + +describe Vagrant::Util::Keypair do + describe ".create" do + it "generates a usable keypair with no password" do + # I don't know how to validate the final return value yet... + pubkey, privkey, _ = described_class.create + + pubkey = OpenSSL::PKey::RSA.new(pubkey) + privkey = OpenSSL::PKey::RSA.new(privkey) + + encrypted = pubkey.public_encrypt("foo") + decrypted = privkey.private_decrypt(encrypted) + + expect(decrypted).to eq("foo") + end + + it "generates a keypair that requires a password" do + pubkey, privkey, _ = described_class.create("password") + + pubkey = OpenSSL::PKey::RSA.new(pubkey) + privkey = OpenSSL::PKey::RSA.new(privkey, "password") + + encrypted = pubkey.public_encrypt("foo") + decrypted = privkey.private_decrypt(encrypted) + + expect(decrypted).to eq("foo") + end + end +end