re-encode certificates back into openssh format

This commit is contained in:
Ben Toews 2019-02-21 16:27:00 -07:00
Родитель 7fdb72a330
Коммит cfc5029bfb
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E9C423BE17EFEE70
8 изменённых файлов: 143 добавлений и 146 удалений

Просмотреть файл

@ -119,5 +119,49 @@ module SSHData
@ca_key = ca_key
@signature = signature
end
# OpenSSH certificate in authorized_keys format (see sshd(8) manual page).
#
# comment - Optional String comment to append.
#
# Returns a String key.
def openssh(comment: nil)
[algo, Base64.strict_encode64(rfc4253), comment].compact.join(" ")
end
# RFC4253 binary encoding of the certificate.
#
# Returns a binary String.
def rfc4253
Encoding.encode_fields(
[:string, algo],
[:string, nonce],
[:raw, public_key_without_algo],
[:uint64, serial],
[:uint32, type],
[:string, key_id],
[:list, valid_principals],
[:time, valid_after],
[:time, valid_before],
[:options, critical_options],
[:options, extensions],
[:string, reserved],
[:string, ca_key.rfc4253],
[:string, signature],
)
end
private
# Helper for getting the RFC4253 encoded public key with the first field
# (the algorithm) stripped off.
#
# Returns a String.
def public_key_without_algo
key = public_key.rfc4253
_, algo_len = Encoding.decode_string(key)
key.byteslice(algo_len..-1)
end
end
end

Просмотреть файл

@ -377,6 +377,8 @@ module SSHData
def encode_fields(*fields)
fields.map do |type, value|
case type
when :raw
value
when :string
encode_string(value)
when :list
@ -392,7 +394,7 @@ module SSHData
when :options
encode_options(value)
else
raise DecodeError
raise DecodeError, "bad type: #{type}"
end
end.join
end

Просмотреть файл

@ -33,7 +33,7 @@ module SSHData
raise "implement me"
end
# RFC4253 binary encoding of public key.
# RFC4253 binary encoding of the public key.
#
# Returns a binary String.
def rfc4253

Просмотреть файл

@ -80,7 +80,7 @@ module SSHData
openssl.verify(OpenSSL::Digest::SHA1.new, openssl_sig, signed_data)
end
# RFC4253 binary encoding of public key.
# RFC4253 binary encoding of the public key.
#
# Returns a binary String.
def rfc4253

Просмотреть файл

@ -100,7 +100,7 @@ module SSHData
openssl.verify(digest.new, openssl_sig, signed_data)
end
# RFC4253 binary encoding of public key.
# RFC4253 binary encoding of the public key.
#
# Returns a binary String.
def rfc4253

Просмотреть файл

@ -46,7 +46,7 @@ module SSHData
end
end
# RFC4253 binary encoding of public key.
# RFC4253 binary encoding of the public key.
#
# Returns a binary String.
def rfc4253

Просмотреть файл

@ -32,7 +32,7 @@ module SSHData
openssl.verify(OpenSSL::Digest::SHA1.new, raw_sig, signed_data)
end
# RFC4253 binary encoding of public key.
# RFC4253 binary encoding of the public key.
#
# Returns a binary String.
def rfc4253

Просмотреть файл

@ -1,16 +1,6 @@
require_relative "./spec_helper"
describe SSHData::Certificate do
let(:rsa_cert) { described_class.parse_openssh(fixture("rsa_leaf_for_rsa_ca-cert.pub")) }
let(:dsa_cert) { described_class.parse_openssh(fixture("dsa_leaf_for_rsa_ca-cert.pub")) }
let(:ecdsa_cert) { described_class.parse_openssh(fixture("ecdsa_leaf_for_rsa_ca-cert.pub")) }
let(:ed25519_cert) { described_class.parse_openssh(fixture("ed25519_leaf_for_rsa_ca-cert.pub")) }
let(:rsa_ca_cert) { described_class.parse_openssh(fixture("rsa_leaf_for_rsa_ca-cert.pub")) }
let(:dsa_ca_cert) { described_class.parse_openssh(fixture("rsa_leaf_for_dsa_ca-cert.pub")) }
let(:ecdsa_ca_cert) { described_class.parse_openssh(fixture("rsa_leaf_for_ecdsa_ca-cert.pub")) }
let(:ed25519_ca_cert) { described_class.parse_openssh(fixture("rsa_leaf_for_ed25519_ca-cert.pub")) }
let(:min_time) { Time.at(0) }
let(:max_time) { Time.at((2**64)-1) }
@ -42,7 +32,7 @@ describe SSHData::Certificate do
cert = [algo, b64, comment].join(" ")
expect {
described_class.parse_openssh(cert, unsafe_no_verify: true)
described_class.parse_openssh(cert)
}.to raise_error(SSHData::DecodeError)
end
@ -51,7 +41,7 @@ describe SSHData::Certificate do
cert = [SSHData::Certificate::ALGO_ED25519, b64, comment].join(" ")
expect {
described_class.parse_openssh(cert, unsafe_no_verify: true)
described_class.parse_openssh(cert)
}.to raise_error(SSHData::DecodeError)
end
@ -60,143 +50,104 @@ describe SSHData::Certificate do
cert = [type, b64].join(" ")
expect {
described_class.parse_openssh(cert, unsafe_no_verify: true)
described_class.parse_openssh(cert)
}.not_to raise_error
end
it "parses RSA certs" do
expect(rsa_cert.algo).to eq(SSHData::Certificate::ALGO_RSA)
expect(rsa_cert.nonce).to be_a(String)
expect(rsa_cert.public_key).to be_a(SSHData::PublicKey::RSA)
expect(rsa_cert.serial).to eq(123)
expect(rsa_cert.type).to eq(SSHData::Certificate::TYPE_USER)
expect(rsa_cert.key_id).to eq("my-ident")
expect(rsa_cert.valid_principals).to eq(["p1", "p2"])
expect(rsa_cert.valid_after).to eq(min_time)
expect(rsa_cert.valid_before).to eq(max_time)
expect(rsa_cert.critical_options).to eq({"foo" => "bar"})
expect(rsa_cert.extensions).to eq({"permit-X11-forwarding" => true, "baz" => "qwer"})
expect(rsa_cert.reserved).to eq("")
expect(rsa_cert.ca_key).to be_a(SSHData::PublicKey::RSA)
expect(rsa_cert.signature).to be_a(String)
end
test_cases = []
it "parses DSA certs" do
expect(dsa_cert.algo).to eq(SSHData::Certificate::ALGO_DSA)
expect(dsa_cert.nonce).to be_a(String)
expect(dsa_cert.public_key).to be_a(SSHData::PublicKey::DSA)
expect(dsa_cert.serial).to eq(123)
expect(dsa_cert.type).to eq(SSHData::Certificate::TYPE_USER)
expect(dsa_cert.key_id).to eq("my-ident")
expect(dsa_cert.valid_principals).to eq(["p1", "p2"])
expect(dsa_cert.valid_after).to eq(min_time)
expect(dsa_cert.valid_before).to eq(max_time)
expect(dsa_cert.critical_options).to eq({"foo" => "bar"})
expect(dsa_cert.extensions).to eq({"permit-X11-forwarding" => true, "baz" => "qwer"})
expect(dsa_cert.reserved).to eq("")
expect(dsa_cert.ca_key).to be_a(SSHData::PublicKey::RSA)
expect(dsa_cert.signature).to be_a(String)
end
test_cases << [
:rsa_cert, # name
"rsa_leaf_for_rsa_ca-cert.pub", # fixture
SSHData::Certificate::ALGO_RSA, # algo
SSHData::PublicKey::RSA, # public key type
SSHData::PublicKey::RSA # ca key type
]
it "parses ECDSA certs" do
expect(ecdsa_cert.algo).to eq(SSHData::Certificate::ALGO_ECDSA256)
expect(ecdsa_cert.nonce).to be_a(String)
expect(ecdsa_cert.public_key).to be_a(SSHData::PublicKey::ECDSA)
expect(ecdsa_cert.serial).to eq(123)
expect(ecdsa_cert.type).to eq(SSHData::Certificate::TYPE_USER)
expect(ecdsa_cert.key_id).to eq("my-ident")
expect(ecdsa_cert.valid_principals).to eq(["p1", "p2"])
expect(ecdsa_cert.valid_after).to eq(min_time)
expect(ecdsa_cert.valid_before).to eq(max_time)
expect(ecdsa_cert.critical_options).to eq({"foo" => "bar"})
expect(ecdsa_cert.extensions).to eq({"permit-X11-forwarding" => true, "baz" => "qwer"})
expect(ecdsa_cert.reserved).to eq("")
expect(ecdsa_cert.ca_key).to be_a(SSHData::PublicKey::RSA)
expect(ecdsa_cert.signature).to be_a(String)
end
test_cases << [
:dsa_cert, # name
"dsa_leaf_for_rsa_ca-cert.pub", # fixture
SSHData::Certificate::ALGO_DSA, # algo
SSHData::PublicKey::DSA, # public key type
SSHData::PublicKey::RSA # ca key type
]
it "parses ED25519 certs" do
expect(ed25519_cert.algo).to eq(SSHData::Certificate::ALGO_ED25519)
expect(ed25519_cert.nonce).to be_a(String)
expect(ed25519_cert.public_key).to be_a(SSHData::PublicKey::ED25519)
expect(ed25519_cert.serial).to eq(123)
expect(ed25519_cert.type).to eq(SSHData::Certificate::TYPE_USER)
expect(ed25519_cert.key_id).to eq("my-ident")
expect(ed25519_cert.valid_principals).to eq(["p1", "p2"])
expect(ed25519_cert.valid_after).to eq(min_time)
expect(ed25519_cert.valid_before).to eq(max_time)
expect(ed25519_cert.critical_options).to eq({"foo" => "bar"})
expect(ed25519_cert.extensions).to eq({"permit-X11-forwarding" => true, "baz" => "qwer"})
expect(ed25519_cert.reserved).to eq("")
expect(ed25519_cert.ca_key).to be_a(SSHData::PublicKey::RSA)
expect(ed25519_cert.signature).to be_a(String)
end
test_cases << [
:ecdsa_cert, # name
"ecdsa_leaf_for_rsa_ca-cert.pub", # fixture
SSHData::Certificate::ALGO_ECDSA256, # algo
SSHData::PublicKey::ECDSA, # public key type
SSHData::PublicKey::RSA # ca key type
]
it "parses certs issued by RSA CAs" do
expect(rsa_ca_cert.algo).to eq(SSHData::Certificate::ALGO_RSA)
expect(rsa_ca_cert.nonce).to be_a(String)
expect(rsa_ca_cert.public_key).to be_a(SSHData::PublicKey::RSA)
expect(rsa_ca_cert.serial).to eq(123)
expect(rsa_ca_cert.type).to eq(SSHData::Certificate::TYPE_USER)
expect(rsa_ca_cert.key_id).to eq("my-ident")
expect(rsa_ca_cert.valid_principals).to eq(["p1", "p2"])
expect(rsa_ca_cert.valid_after).to eq(min_time)
expect(rsa_ca_cert.valid_before).to eq(max_time)
expect(rsa_ca_cert.critical_options).to eq({"foo" => "bar"})
expect(rsa_ca_cert.extensions).to eq({"permit-X11-forwarding" => true, "baz" => "qwer"})
expect(rsa_ca_cert.reserved).to eq("")
expect(rsa_ca_cert.ca_key).to be_a(SSHData::PublicKey::RSA)
expect(rsa_ca_cert.signature).to be_a(String)
end
test_cases << [
:ed25519_cert, # name
"ed25519_leaf_for_rsa_ca-cert.pub", # fixture
SSHData::Certificate::ALGO_ED25519, # algo
SSHData::PublicKey::ED25519, # public key type
SSHData::PublicKey::RSA # ca key type
]
it "parses certs issued by DSA CAs" do
expect(dsa_ca_cert.algo).to eq(SSHData::Certificate::ALGO_RSA)
expect(dsa_ca_cert.nonce).to be_a(String)
expect(dsa_ca_cert.public_key).to be_a(SSHData::PublicKey::RSA)
expect(dsa_ca_cert.serial).to eq(123)
expect(dsa_ca_cert.type).to eq(SSHData::Certificate::TYPE_USER)
expect(dsa_ca_cert.key_id).to eq("my-ident")
expect(dsa_ca_cert.valid_principals).to eq(["p1", "p2"])
expect(dsa_ca_cert.valid_after).to eq(min_time)
expect(dsa_ca_cert.valid_before).to eq(max_time)
expect(dsa_ca_cert.critical_options).to eq({"foo" => "bar"})
expect(dsa_ca_cert.extensions).to eq({"permit-X11-forwarding" => true, "baz" => "qwer"})
expect(dsa_ca_cert.reserved).to eq("")
expect(dsa_ca_cert.ca_key).to be_a(SSHData::PublicKey::DSA)
expect(dsa_ca_cert.signature).to be_a(String)
end
test_cases << [
:rsa_ca, # name
"rsa_leaf_for_rsa_ca-cert.pub", # fixture
SSHData::Certificate::ALGO_RSA, # algo
SSHData::PublicKey::RSA, # public key type
SSHData::PublicKey::RSA # ca key type
]
it "parses certs issued by ECDSA CAs" do
expect(ecdsa_ca_cert.algo).to eq(SSHData::Certificate::ALGO_RSA)
expect(ecdsa_ca_cert.nonce).to be_a(String)
expect(ecdsa_ca_cert.public_key).to be_a(SSHData::PublicKey::RSA)
expect(ecdsa_ca_cert.serial).to eq(123)
expect(ecdsa_ca_cert.type).to eq(SSHData::Certificate::TYPE_USER)
expect(ecdsa_ca_cert.key_id).to eq("my-ident")
expect(ecdsa_ca_cert.valid_principals).to eq(["p1", "p2"])
expect(ecdsa_ca_cert.valid_after).to eq(min_time)
expect(ecdsa_ca_cert.valid_before).to eq(max_time)
expect(ecdsa_ca_cert.critical_options).to eq({"foo" => "bar"})
expect(ecdsa_ca_cert.extensions).to eq({"permit-X11-forwarding" => true, "baz" => "qwer"})
expect(ecdsa_ca_cert.reserved).to eq("")
expect(ecdsa_ca_cert.ca_key).to be_a(SSHData::PublicKey::ECDSA)
expect(ecdsa_ca_cert.signature).to be_a(String)
end
test_cases << [
:dsa_ca, # name
"rsa_leaf_for_dsa_ca-cert.pub", # fixture
SSHData::Certificate::ALGO_RSA, # algo
SSHData::PublicKey::RSA, # public key type
SSHData::PublicKey::DSA # ca key type
]
it "parses certs issued by ED25519 CAs" do
expect(ed25519_ca_cert.algo).to eq(SSHData::Certificate::ALGO_RSA)
expect(ed25519_ca_cert.nonce).to be_a(String)
expect(ed25519_ca_cert.public_key).to be_a(SSHData::PublicKey::RSA)
expect(ed25519_ca_cert.serial).to eq(123)
expect(ed25519_ca_cert.type).to eq(SSHData::Certificate::TYPE_USER)
expect(ed25519_ca_cert.key_id).to eq("my-ident")
expect(ed25519_ca_cert.valid_principals).to eq(["p1", "p2"])
expect(ed25519_ca_cert.valid_after).to eq(min_time)
expect(ed25519_ca_cert.valid_before).to eq(max_time)
expect(ed25519_ca_cert.critical_options).to eq({"foo" => "bar"})
expect(ed25519_ca_cert.extensions).to eq({"permit-X11-forwarding" => true, "baz" => "qwer"})
expect(ed25519_ca_cert.reserved).to eq("")
expect(ed25519_ca_cert.ca_key).to be_a(SSHData::PublicKey::ED25519)
expect(ed25519_ca_cert.signature).to be_a(String)
test_cases << [
:ecdsa_ca, # name
"rsa_leaf_for_ecdsa_ca-cert.pub", # fixture
SSHData::Certificate::ALGO_RSA, # algo
SSHData::PublicKey::RSA, # public key type
SSHData::PublicKey::ECDSA # ca key type
]
test_cases << [
:ed25519_ca, # name
"rsa_leaf_for_ed25519_ca-cert.pub", # fixture
SSHData::Certificate::ALGO_RSA, # algo
SSHData::PublicKey::RSA, # public key type
SSHData::PublicKey::ED25519 # ca key type
]
test_cases.each do |name, fixture_name, algo, public_key_class, ca_key_class|
describe(name) do
let(:openssh) { fixture(fixture_name).strip }
let(:comment) { SSHData.key_parts(openssh).last }
subject { SSHData::Certificate.parse_openssh(openssh) }
it "parses correctly" do
expect(subject.algo).to eq(algo)
expect(subject.nonce).to be_a(String)
expect(subject.public_key).to be_a(public_key_class)
expect(subject.serial).to eq(123)
expect(subject.type).to eq(SSHData::Certificate::TYPE_USER)
expect(subject.key_id).to eq("my-ident")
expect(subject.valid_principals).to eq(["p1", "p2"])
expect(subject.valid_after).to eq(min_time)
expect(subject.valid_before).to eq(max_time)
expect(subject.critical_options).to eq({"foo" => "bar"})
expect(subject.extensions).to eq({"permit-X11-forwarding" => true, "baz" => "qwer"})
expect(subject.reserved).to eq("")
expect(subject.ca_key).to be_a(ca_key_class)
expect(subject.signature).to be_a(String)
end
it "encodes correctly" do
expect(subject.openssh(comment: comment)).to eq(openssh)
end
end
end
end