class SSHData::Certificate
Constants
- ALGOS
- ALGO_DSA
- ALGO_ECDSA256
- ALGO_ECDSA384
- ALGO_ECDSA521
- ALGO_ED25519
- ALGO_RSA
Certificate
algorithm identifiers- ALGO_SKECDSA256
- ALGO_SKED25519
- BEGINNING_OF_TIME
Special values for
valid_before
and valid_after.- CRITICAL_OPTION_FORCE_COMMAND
- CRITICAL_OPTION_SOURCE_ADDRESS
- END_OF_TIME
- TYPE_HOST
- TYPE_USER
Integer certificate types
Attributes
Public Class Methods
Intialize a new Certificate
instance.
algo: - The certificate’s String algorithm id (one of ALGO_RSA
,
ALGO_DSA, ALGO_ECDSA256, ALGO_ECDSA384, ALGO_ECDSA521, or ALGO_ED25519)
nonce: - The certificate’s String nonce field. public_key
: - The certificate’s public key as an PublicKey::Base
subclass instance.
serial: - The certificate’s Integer serial field. type: - The certificate’s Integer type field (one of TYPE_USER
or TYPE_HOST).
key_id
: - The certificate’s String key_id
field. valid_principals
: - The Array of Strings valid_principles field from the
certificate.
valid_after
: - The certificate’s Time valid_after
field. valid_before
: - The certificate’s Time valid_before
field. critical_options
: - The Hash critical_options
field from the certificate. extensions: - The Hash extensions field from the certificate. reserved: - The certificate’s String reserved field. ca_key
: - The issuing CA’s public key as a PublicKey::Base
subclass instance.
signature: - The certificate’s String signature field.
Returns nothing.
# File lib/ssh_data/certificate.rb, line 106 def initialize(public_key:, key_id:, algo: nil, nonce: nil, serial: 0, type: TYPE_USER, valid_principals: [], valid_after: BEGINNING_OF_TIME, valid_before: END_OF_TIME, critical_options: {}, extensions: {}, reserved: "", ca_key: nil, signature: "") @algo = algo || Encoding::CERT_ALGO_BY_PUBLIC_KEY_ALGO[public_key.algo] @nonce = nonce || SecureRandom.random_bytes(32) @public_key = public_key @serial = serial @type = type @key_id = key_id @valid_principals = valid_principals @valid_after = valid_after @valid_before = valid_before @critical_options = critical_options @extensions = extensions @reserved = reserved @ca_key = ca_key @signature = signature end
Parse an OpenSSH certificate in authorized_keys format (see sshd(8) manual page).
cert - An OpenSSH formatted certificate, including key algo,
base64 encoded key and optional comment.
unsafe_no_verify: - Bool of whether to skip verifying certificate signature
(Default false)
Returns a Certificate
instance.
# File lib/ssh_data/certificate.rb, line 45 def self.parse_openssh(cert, unsafe_no_verify: false) algo, raw, _ = SSHData.key_parts(cert) parsed = parse_rfc4253(raw, unsafe_no_verify: unsafe_no_verify) if parsed.algo != algo raise DecodeError, "algo mismatch: #{parsed.algo.inspect}!=#{algo.inspect}" end parsed end
Parse an RFC 4253 binary SSH certificate.
cert - A RFC 4253 binary certificate String. unsafe_no_verify: - Bool of whether to skip verifying certificate
signature (Default false)
Returns a Certificate
instance.
# File lib/ssh_data/certificate.rb, line 66 def self.parse_rfc4253(raw, unsafe_no_verify: false) data, read = Encoding.decode_certificate(raw) if read != raw.bytesize raise DecodeError, "unexpected trailing data" end # Parse data into better types, where possible. public_key = PublicKey.from_data(data.delete(:public_key)) ca_key = PublicKey.from_data(data.delete(:signature_key)) new(**data.merge(public_key: public_key, ca_key: ca_key)).tap do |cert| raise VerifyError unless unsafe_no_verify || cert.verify end end
Public Instance Methods
Check if the given IP address is allowed for use with this certificate.
address - A String IP address.
Returns boolean.
# File lib/ssh_data/certificate.rb, line 213 def allowed_source_address?(address) return true if source_address.nil? parsed_addr = IPAddr.new(address) source_address.any? { |a| a.include?(parsed_addr) } rescue IPAddr::InvalidAddressError return false end
The force-command critical option, if present.
Returns a String or nil.
# File lib/ssh_data/certificate.rb, line 175 def force_command case value = critical_options[CRITICAL_OPTION_FORCE_COMMAND] when String, NilClass value else raise DecodeError, "bad force-request" end end
OpenSSH certificate in authorized_keys format (see sshd(8) manual page).
comment - Optional String comment to append.
Returns a String key.
# File lib/ssh_data/certificate.rb, line 128 def openssh(comment: nil) [algo, Base64.strict_encode64(rfc4253), comment].compact.join(" ") end
RFC4253 binary encoding of the certificate.
Returns a binary String.
# File lib/ssh_data/certificate.rb, line 135 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
Sign this certificate with a private key.
private_key - An SSHData::PrivateKey::Base
subclass instance. algo: - Optionally specify the signature algorithm to use.
Returns nothing.
# File lib/ssh_data/certificate.rb, line 160 def sign(private_key, algo: nil) @ca_key = private_key.public_key @signature = private_key.sign(signed_data, algo: algo) end
The source-address critical option, if present.
Returns an Array of IPAddr instances or nil.
# File lib/ssh_data/certificate.rb, line 187 def source_address return @source_address if defined?(@source_address) value = critical_options[CRITICAL_OPTION_SOURCE_ADDRESS] @source_address = case value when String value.split(",").map do |str_addr| begin IPAddr.new(str_addr.strip) rescue IPAddr::InvalidAddressError => e raise DecodeError, "bad source-address: #{e.message}" end end when NilClass nil else raise DecodeError, "bad source-address" end end
Verify the certificate’s signature.
Returns boolean.
# File lib/ssh_data/certificate.rb, line 168 def verify ca_key.verify(signed_data, signature) end
Private Instance Methods
Helper for getting the RFC4253 encoded public key with the first field (the algorithm) stripped off.
Returns a String.
# File lib/ssh_data/certificate.rb, line 235 def public_key_without_algo key = public_key.rfc4253 _, algo_len = Encoding.decode_string(key) key.byteslice(algo_len..-1) end
The portion of the certificate over which the signature is calculated.
Returns a binary String.
# File lib/ssh_data/certificate.rb, line 226 def signed_data siglen = self.signature.bytesize + 4 rfc4253.byteslice(0...-siglen) end