class JSON::JWS
Constants
- NUM_OF_SEGMENTS
Attributes
signature_base_string[W]
Public Class Methods
decode_compact_serialized(input, public_key_or_secret, algorithms = nil, allow_blank_payload = false)
click to toggle source
# File lib/json/jws.rb, line 177 def decode_compact_serialized(input, public_key_or_secret, algorithms = nil, allow_blank_payload = false) unless input.count('.') + 1 == NUM_OF_SEGMENTS raise InvalidFormat.new("Invalid JWS Format. JWS should include #{NUM_OF_SEGMENTS} segments.") end header, claims, signature = input.split('.', NUM_OF_SEGMENTS).collect do |segment| Base64.urlsafe_decode64 segment.to_s end header = JSON.parse(header).with_indifferent_access if allow_blank_payload && claims == '' claims = nil else claims = JSON.parse(claims).with_indifferent_access end jws = new claims jws.header = header jws.signature = signature jws.signature_base_string = input.split('.')[0, NUM_OF_SEGMENTS - 1].join('.') jws.verify! public_key_or_secret, algorithms unless public_key_or_secret == :skip_verification jws end
decode_json_serialized(input, public_key_or_secret, algorithms = nil, allow_blank_payload = false)
click to toggle source
# File lib/json/jws.rb, line 198 def decode_json_serialized(input, public_key_or_secret, algorithms = nil, allow_blank_payload = false) input = input.with_indifferent_access header, payload, signature = if input[:signatures].present? [ input[:signatures].first[:protected], input[:payload], input[:signatures].first[:signature] ].collect do |segment| segment end else [:protected, :payload, :signature].collect do |key| input[key] end end compact_serialized = [header, payload, signature].join('.') decode_compact_serialized compact_serialized, public_key_or_secret, algorithms, allow_blank_payload end
new(jwt)
click to toggle source
# File lib/json/jws.rb, line 11 def initialize(jwt) update jwt end
Public Instance Methods
sign!(private_key_or_secret)
click to toggle source
# File lib/json/jws.rb, line 15 def sign!(private_key_or_secret) self.alg = autodetected_algorithm_from(private_key_or_secret) if alg == :autodetect self.signature = sign signature_base_string, private_key_or_secret self end
update(hash_or_jwt)
click to toggle source
Calls superclass method
# File lib/json/jws.rb, line 33 def update(hash_or_jwt) super if hash_or_jwt.is_a? JSON::JWT self.header.update hash_or_jwt.header self.signature = hash_or_jwt.signature self.blank_payload = hash_or_jwt.blank_payload end self end
verify!(public_key_or_secret, algorithms = nil)
click to toggle source
# File lib/json/jws.rb, line 21 def verify!(public_key_or_secret, algorithms = nil) if alg&.to_sym == :none raise UnexpectedAlgorithm if public_key_or_secret signature == '' or raise VerificationFailed elsif algorithms.blank? || Array(algorithms).include?(alg&.to_sym) public_key_or_secret && valid?(public_key_or_secret) or raise VerificationFailed else raise UnexpectedAlgorithm.new('Unexpected alg header') end end
Private Instance Methods
asn1_to_raw(signature, private_key)
click to toggle source
# File lib/json/jws.rb, line 171 def asn1_to_raw(signature, private_key) byte_size = (private_key.group.degree + 7) / 8 OpenSSL::ASN1.decode(signature).value.map { |value| value.value.to_s(2).rjust(byte_size, "\x00") }.join end
autodetected_algorithm_from(private_key_or_secret)
click to toggle source
# File lib/json/jws.rb, line 65 def autodetected_algorithm_from(private_key_or_secret) private_key_or_secret = with_jwk_support private_key_or_secret case private_key_or_secret when String :HS256 when OpenSSL::PKey::RSA :RS256 when OpenSSL::PKey::EC case private_key_or_secret.group.curve_name when 'prime256v1' :ES256 when 'secp384r1' :ES384 when 'secp521r1' :ES512 when 'secp256k1' :ES256K else raise UnknownAlgorithm.new('Unknown EC Curve') end else raise UnexpectedAlgorithm.new('Signature algorithm auto-detection failed') end end
digest()
click to toggle source
# File lib/json/jws.rb, line 45 def digest OpenSSL::Digest.new "SHA#{alg.to_s[2, 3]}" end
ecdsa?()
click to toggle source
# File lib/json/jws.rb, line 61 def ecdsa? [:ES256, :ES384, :ES512, :ES256K].include? alg&.to_sym end
hmac?()
click to toggle source
# File lib/json/jws.rb, line 49 def hmac? [:HS256, :HS384, :HS512].include? alg&.to_sym end
raw_to_asn1(signature, public_key)
click to toggle source
# File lib/json/jws.rb, line 164 def raw_to_asn1(signature, public_key) byte_size = (public_key.group.degree + 7) / 8 r = signature[0..(byte_size - 1)] s = signature[byte_size..-1] OpenSSL::ASN1::Sequence.new([r, s].map { |int| OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(int, 2)) }).to_der end
rsa?()
click to toggle source
# File lib/json/jws.rb, line 53 def rsa? [:RS256, :RS384, :RS512].include? alg&.to_sym end
rsa_pss?()
click to toggle source
# File lib/json/jws.rb, line 57 def rsa_pss? [:PS256, :PS384, :PS512].include? alg&.to_sym end
sign(signature_base_string, private_key_or_secret)
click to toggle source
# File lib/json/jws.rb, line 99 def sign(signature_base_string, private_key_or_secret) private_key_or_secret = with_jwk_support private_key_or_secret case when hmac? secret = private_key_or_secret OpenSSL::HMAC.digest digest, secret, signature_base_string when rsa? private_key = private_key_or_secret private_key.sign digest, signature_base_string when rsa_pss? private_key = private_key_or_secret private_key.sign_pss digest, signature_base_string, salt_length: :digest, mgf1_hash: digest when ecdsa? private_key = private_key_or_secret verify_ecdsa_group! private_key asn1_to_raw( private_key.sign(digest, signature_base_string), private_key ) else raise UnexpectedAlgorithm.new('Unknown Signature Algorithm') end end
signature_base_string()
click to toggle source
# File lib/json/jws.rb, line 90 def signature_base_string @signature_base_string ||= [ header.to_json, self.to_json ].collect do |segment| Base64.urlsafe_encode64 segment, padding: false end.join('.') end
valid?(public_key_or_secret)
click to toggle source
# File lib/json/jws.rb, line 123 def valid?(public_key_or_secret) public_key_or_secret = with_jwk_support public_key_or_secret case when hmac? secret = public_key_or_secret secure_compare sign(signature_base_string, secret), signature when rsa? public_key = public_key_or_secret public_key.verify digest, signature, signature_base_string when rsa_pss? public_key = public_key_or_secret public_key.verify_pss digest, signature, signature_base_string, salt_length: :digest, mgf1_hash: digest when ecdsa? public_key = public_key_or_secret verify_ecdsa_group! public_key public_key.verify digest, raw_to_asn1(signature, public_key), signature_base_string else raise UnexpectedAlgorithm.new('Unknown Signature Algorithm') end rescue TypeError => e raise UnexpectedAlgorithm.new(e.message) end
verify_ecdsa_group!(key)
click to toggle source
# File lib/json/jws.rb, line 146 def verify_ecdsa_group!(key) group_name = case digest.digest_length * 8 when 256 case key.group.curve_name when 'secp256k1' :secp256k1 else :prime256v1 end when 384 :secp384r1 when 512 :secp521r1 end newkey = OpenSSL::PKey::EC.generate(group_name.to_s) newkey.check_key end