Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions lib/jose/jwk/kty.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ module JOSE::JWK::KTY

def self.from_key(object)
object = object.__getobj__ if object.is_a?(JOSE::JWK::PKeyProxy)

case object
when OpenSSL::X509::Certificate
JOSE::JWK::KTY_X509.from_key(object)
when OpenSSL::PKey::EC
return JOSE::JWK::KTY_EC.from_key(object)
JOSE::JWK::KTY_EC.from_key(object)
when OpenSSL::PKey::RSA
return JOSE::JWK::KTY_RSA.from_key(object)
JOSE::JWK::KTY_RSA.from_key(object)
else
raise ArgumentError, "'object' is not a recognized key type: #{object.class.name}"
end
Expand Down Expand Up @@ -38,4 +41,5 @@ def self.key_encryptor(kty, fields, key)
require 'jose/jwk/kty_okp_ed448ph'
require 'jose/jwk/kty_okp_x25519'
require 'jose/jwk/kty_okp_x448'
require 'jose/jwk/kty_x509'
require 'jose/jwk/kty_rsa'
94 changes: 94 additions & 0 deletions lib/jose/jwk/kty_x509.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
class JOSE::JWK::KTY_X509 < Struct.new(:key)

def self.from_key(object)
object = object.__getobj__ if object.is_a?(JOSE::JWK::PKeyProxy)
case object
when OpenSSL::PKey::PKey
JOSE::JWK::KTY_X509.new(JOSE::JWK::PKeyProxy.new(object))
when OpenSSL::X509::Certificate
JOSE::JWK::KTY_X509.new(JOSE::JWK::PKeyProxy.new(object.public_key))
else
raise ArgumentError, "'object' is not a recognized key type: #{object.class.name}"
end
end

def to_key
key.__getobj__
end

def to_map(fields)
{
'kty' => 'RSA',
'n' => JOSE.urlsafe_encode64(key.n.to_s(2)),
'e' => JOSE.urlsafe_encode64(key.e.to_s(2))
}
end

def to_public_map(fields)
to_map(fields)
end

def to_thumbprint_map(fields)
to_map(fields).slice('e', 'kty', 'n')
end

def block_encryptor(fields = nil)
if fields && fields['use'] == 'enc' && !fields['alg'].nil? && !fields['enc'].nil?
JOSE::Map[
'alg' => fields['alg'],
'enc' => fields['enc']
]
else
JOSE::Map[
'alg' => 'RSA-OAEP',
'enc' => 'A128GCM'
]
end
end

def encrypt_public(plain_text, rsa_padding: :rsa_pkcs1_padding, rsa_oaep_md: nil)
case rsa_padding
when :rsa_pkcs1_padding
key.public_encrypt(plain_text, OpenSSL::PKey::RSA::PKCS1_PADDING)
when :rsa_pkcs1_oaep_padding
rsa_oaep_md ||= OpenSSL::Digest::SHA1
key.public_encrypt(plain_text, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
else
raise ArgumentError, "unsupported RSA padding: #{rsa_padding.inspect}"
end
end

def verify(message, digest_type, signature, padding: :rsa_pkcs1_padding)
case padding
when :rsa_pkcs1_padding
key.verify(digest_type.new, signature, message)
when :rsa_pkcs1_pss_padding
if key.respond_to?(:verify_pss)
digest_name = digest_type.new.name
key.verify_pss(digest_name, signature, message, salt_length: :digest, mgf1_hash: digest_name)
else
JOSE::JWA::PKCS1.rsassa_pss_verify(digest_type, message, signature, key)
end
else
raise ArgumentError, "unsupported RSA padding: #{padding.inspect}"
end
rescue OpenSSL::PKey::PKeyError
false
end

def signer(fields = nil)
if fields && fields['use'] == 'sig' && !fields['alg'].nil?
JOSE::Map['alg' => fields['alg']]
else
JOSE::Map['alg' => 'RS256']
end
end

def verifier(fields)
if fields && fields['use'] == 'sig' && !fields['alg'].nil?
[fields['alg']]
else
['PS256', 'PS384', 'PS512', 'RS256', 'RS384', 'RS512']
end
end
end
31 changes: 23 additions & 8 deletions lib/jose/jwk/pem.rb
Original file line number Diff line number Diff line change
@@ -1,19 +1,34 @@
module JOSE::JWK::PEM

extend self

def from_binary(object, password = nil)
pkey = OpenSSL::PKey.read(object, password)
return JOSE::JWK::KTY.from_key(pkey)
begin
pkey = OpenSSL::PKey.read(object, password)
return JOSE::JWK::KTY.from_key(pkey)
rescue OpenSSL::PKey::PKeyError
begin
cert = OpenSSL::X509::Certificate.new(object)
return JOSE::JWK::KTY_X509.new(JOSE::JWK::PKeyProxy.new(cert.public_key))
rescue OpenSSL::X509::CertificateError => e
raise RuntimeError, "Unsupported key type or incorrect password: #{e.message}"
end
end
end

def to_binary(key, password = nil)
if password
cipher = OpenSSL::Cipher.new('DES-EDE3-CBC')
return key.to_pem(cipher, password)
else
if key.is_a?(JOSE::JWK::PKeyProxy)
if password
cipher = OpenSSL::Cipher.new('DES-EDE3-CBC')
return key.to_pem(cipher, password)
else
return key.to_pem
end
elsif key.is_a?(OpenSSL::X509::Certificate)
return key.to_pem
elsif key.is_a?(JOSE::JWK::PKeyProxy)
return key.__getobj__.to_pem
else
raise ArgumentError, "Unsupported key type: #{key.class}"
end
end

end
72 changes: 72 additions & 0 deletions test/jose/jwk/kty_x509_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
require 'test_helper'

class JOSE::JWK::KTY_X509Test < Minitest::Test
def test_from_key_and_to_key
x509_pem_data = <<~PEM
-----BEGIN CERTIFICATE-----
MIIDxzCCAq+gAwIBAgIUXm1i9UarQZwGQ3MaNarRSUZbwVAwDQYJKoZIhvcNAQEL
BQAwczELMAkGA1UEBhMCQlIxCzAJBgNVBAgMAlJTMQwwCgYDVQQHDANQT0ExDTAL
BgNVBAoMBHRlc3QxDTALBgNVBAsMBHRlc3QxDTALBgNVBAMMBHRlc3QxHDAaBgkq
hkiG9w0BCQEWDXRlc3RAdGVzdC5jb20wHhcNMjQwNTMxMjIyNDI3WhcNMjUwNTMx
MjIyNDI3WjBzMQswCQYDVQQGEwJCUjELMAkGA1UECAwCUlMxDDAKBgNVBAcMA1BP
QTENMAsGA1UECgwEdGVzdDENMAsGA1UECwwEdGVzdDENMAsGA1UEAwwEdGVzdDEc
MBoGCSqGSIb3DQEJARYNdGVzdEB0ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBALsMvAFvOJtRO8rwF6QwJ/UfhhJ+JHtLdycfLrR/wCW0acHr
MqMeoTKJcRsyQTTQCjO7r1QScv+xaEQohBE6Vq8O8rD38xZn4lesCq3mSf2mUi3k
etZv1pFKU/lN7SceUG65/vVAMoj9HEFZ43WfpDkYFvFDw2dR+vkcSp5SWQW8JxrA
nuGSP+1E57cAsoPHcWPgYBe5y8ndOQREikpOkKUbCcDN5mrg0Y0kUHboXm18jKeW
dCejOj9z0DS1mFqpE8sG4Khv0aL7kAzwQb8vNVoMog3R+qqgv61e3U6BAKyU3k0w
Xet9tyAgofHscO4QEo6ThELTFPgHnvD9DaclKZ8CAwEAAaNTMFEwHQYDVR0OBBYE
FE8ld9J8T8FPZtudE6emFcwOL1MiMB8GA1UdIwQYMBaAFE8ld9J8T8FPZtudE6em
FcwOL1MiMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAIhtTzS3
yoUnKYhIqUIRs5Dp+iwXjAjx9J5kR1cjfBuNGOKvJIqPTkcL7wu9fEG0eR8wsuWD
kYbwNk07fc03Gx/44COH0jIwn8XsDffj3ITA0gU8p9Ee6I/f2jBUQ2SvyC+lr6lc
YJKY3aNi+osMhXOVOguOl6DsxvvmGCI26BMpZueu1WXBEcMNCH/lgFwzNC6HHVKu
kxvYEOrhcgodA5kiOFltgZdwqb1Q7EBFFn1rKPQFvc2XVlJrubyiOXalcwWJ/REa
o0bTj132BSdfkPF2l1rZBQM2pzPg0U7DiTvEa6yMaj4IN8Gv140ogF0niyDegElt
ls8R0jfIBZj2N0I=
-----END CERTIFICATE-----
PEM

x509_cert = OpenSSL::X509::Certificate.new(x509_pem_data)
x509_key = x509_cert.public_key
x509_jwk = JOSE::JWK::KTY_X509.new(JOSE::JWK::PKeyProxy.new(x509_key))
assert_equal x509_key.to_pem.strip, x509_jwk.key.__getobj__.to_pem.strip
end

def test_from_binary_and_to_binary
x509_pem_data = <<~PEM
-----BEGIN CERTIFICATE-----
MIIDxzCCAq+gAwIBAgIUXm1i9UarQZwGQ3MaNarRSUZbwVAwDQYJKoZIhvcNAQEL
BQAwczELMAkGA1UEBhMCQlIxCzAJBgNVBAgMAlJTMQwwCgYDVQQHDANQT0ExDTAL
BgNVBAoMBHRlc3QxDTALBgNVBAsMBHRlc3QxDTALBgNVBAMMBHRlc3QxHDAaBgkq
hkiG9w0BCQEWDXRlc3RAdGVzdC5jb20wHhcNMjQwNTMxMjIyNDI3WhcNMjUwNTMx
MjIyNDI3WjBzMQswCQYDVQQGEwJCUjELMAkGA1UECAwCUlMxDDAKBgNVBAcMA1BP
QTENMAsGA1UECgwEdGVzdDENMAsGA1UECwwEdGVzdDENMAsGA1UEAwwEdGVzdDEc
MBoGCSqGSIb3DQEJARYNdGVzdEB0ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBALsMvAFvOJtRO8rwF6QwJ/UfhhJ+JHtLdycfLrR/wCW0acHr
MqMeoTKJcRsyQTTQCjO7r1QScv+xaEQohBE6Vq8O8rD38xZn4lesCq3mSf2mUi3k
etZv1pFKU/lN7SceUG65/vVAMoj9HEFZ43WfpDkYFvFDw2dR+vkcSp5SWQW8JxrA
nuGSP+1E57cAsoPHcWPgYBe5y8ndOQREikpOkKUbCcDN5mrg0Y0kUHboXm18jKeW
dCejOj9z0DS1mFqpE8sG4Khv0aL7kAzwQb8vNVoMog3R+qqgv61e3U6BAKyU3k0w
Xet9tyAgofHscO4QEo6ThELTFPgHnvD9DaclKZ8CAwEAAaNTMFEwHQYDVR0OBBYE
FE8ld9J8T8FPZtudE6emFcwOL1MiMB8GA1UdIwQYMBaAFE8ld9J8T8FPZtudE6em
FcwOL1MiMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAIhtTzS3
yoUnKYhIqUIRs5Dp+iwXjAjx9J5kR1cjfBuNGOKvJIqPTkcL7wu9fEG0eR8wsuWD
kYbwNk07fc03Gx/44COH0jIwn8XsDffj3ITA0gU8p9Ee6I/f2jBUQ2SvyC+lr6lc
YJKY3aNi+osMhXOVOguOl6DsxvvmGCI26BMpZueu1WXBEcMNCH/lgFwzNC6HHVKu
kxvYEOrhcgodA5kiOFltgZdwqb1Q7EBFFn1rKPQFvc2XVlJrubyiOXalcwWJ/REa
o0bTj132BSdfkPF2l1rZBQM2pzPg0U7DiTvEa6yMaj4IN8Gv140ogF0niyDegElt
ls8R0jfIBZj2N0I=
-----END CERTIFICATE-----
PEM

x509_cert = OpenSSL::X509::Certificate.new(x509_pem_data)
x509_key = x509_cert.public_key
x509_jwk = JOSE::JWK::KTY_X509.new(JOSE::JWK::PKeyProxy.new(x509_key))

from_binary_jwk = JOSE::JWK::KTY_X509.new(JOSE::JWK::PKeyProxy.new(OpenSSL::X509::Certificate.new(x509_pem_data).public_key))

assert_equal x509_jwk.key.__getobj__.to_pem.strip, from_binary_jwk.key.__getobj__.to_pem.strip
end
end
42 changes: 42 additions & 0 deletions test/jose/jwk/pem_test.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
require 'test_helper'

class JOSE::JWK::PEMTest < Minitest::Test
def normalize_pem(pem)
pem.strip.gsub("\r\n", "\n").gsub("\n", "\n")
end

def test_from_pem_and_to_pem
ec_pem_data = \
Expand Down Expand Up @@ -55,4 +58,43 @@ def test_from_pem_and_to_pem
assert_equal rsa_pem, JOSE::JWK.from_pem(encrypted_rsa_pem_data, rsa_pem_password)
end

def sanitize_pem(pem)
pem.gsub(/\s+/, '').gsub(/\n/, '')
end

def test_from_pem_and_to_pem_x509
x509_pem_data = <<~PEM
-----BEGIN CERTIFICATE-----
MIIDxzCCAq+gAwIBAgIUXm1i9UarQZwGQ3MaNarRSUZbwVAwDQYJKoZIhvcNAQEL
BQAwczELMAkGA1UEBhMCQlIxCzAJBgNVBAgMAlJTMQwwCgYDVQQHDANQT0ExDTAL
BgNVBAoMBHRlc3QxDTALBgNVBAsMBHRlc3QxDTALBgNVBAMMBHRlc3QxHDAaBgkq
hkiG9w0BCQEWDXRlc3RAdGVzdC5jb20wHhcNMjQwNTMxMjIyNDI3WhcNMjUwNTMx
MjIyNDI3WjBzMQswCQYDVQQGEwJCUjELMAkGA1UECAwCUlMxDDAKBgNVBAcMA1BP
QTENMAsGA1UECgwEdGVzdDENMAsGA1UECwwEdGVzdDENMAsGA1UEAwwEdGVzdDEc
MBoGCSqGSIb3DQEJARYNdGVzdEB0ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBALsMvAFvOJtRO8rwF6QwJ/UfhhJ+JHtLdycfLrR/wCW0acHr
MqMeoTKJcRsyQTTQCjO7r1QScv+xaEQohBE6Vq8O8rD38xZn4lesCq3mSf2mUi3k
etZv1pFKU/lN7SceUG65/vVAMoj9HEFZ43WfpDkYFvFDw2dR+vkcSp5SWQW8JxrA
nuGSP+1E57cAsoPHcWPgYBe5y8ndOQREikpOkKUbCcDN5mrg0Y0kUHboXm18jKeW
dCejOj9z0DS1mFqpE8sG4Khv0aL7kAzwQb8vNVoMog3R+qqgv61e3U6BAKyU3k0w
Xet9tyAgofHscO4QEo6ThELTFPgHnvD9DaclKZ8CAwEAAaNTMFEwHQYDVR0OBBYE
FE8ld9J8T8FPZtudE6emFcwOL1MiMB8GA1UdIwQYMBaAFE8ld9J8T8FPZtudE6em
FcwOL1MiMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAIhtTzS3
yoUnKYhIqUIRs5Dp+iwXjAjx9J5kR1cjfBuNGOKvJIqPTkcL7wu9fEG0eR8wsuWD
kYbwNk07fc03Gx/44COH0jIwn8XsDffj3ITA0gU8p9Ee6I/f2jBUQ2SvyC+lr6lc
YJKY3aNi+osMhXOVOguOl6DsxvvmGCI26BMpZueu1WXBEcMNCH/lgFwzNC6HHVKu
kxvYEOrhcgodA5kiOFltgZdwqb1Q7EBFFn1rKPQFvc2XVlJrubyiOXalcwWJ/REa
o0bTj132BSdfkPF2l1rZBQM2pzPg0U7DiTvEa6yMaj4IN8Gv140ogF0niyDegElt
ls8R0jfIBZj2N0I=
-----END CERTIFICATE-----
PEM

x509_cert = OpenSSL::X509::Certificate.new(x509_pem_data)
x509_key = x509_cert.public_key
x509_jwk = JOSE::JWK::KTY_X509.new(JOSE::JWK::PKeyProxy.new(x509_key))

from_binary_jwk = JOSE::JWK::KTY_X509.new(JOSE::JWK::PKeyProxy.new(OpenSSL::X509::Certificate.new(x509_pem_data).public_key))

assert_equal x509_jwk.key.__getobj__.to_pem.strip, from_binary_jwk.key.__getobj__.to_pem.strip
end
end