diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a4ed05c..7ea5b874 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Reject tokens with too many segments in `JWT::EncodedToken` signature verification [#782](https://github.com/jwt/ruby-jwt/pull/782) ([@anakinj](https://github.com/anakinj)) - Raise `JWT::MalformedTokenError` for a token header that is not a JSON object, including a string header in `JWT.decode` without verification [#783](https://github.com/jwt/ruby-jwt/pull/783) ([@anakinj](https://github.com/anakinj)) - Do not call the `key_finder` of `JWT::EncodedToken` when the token's algorithm is not allowed, so a mismatched `alg` header no longer verifies with a key finder [#784](https://github.com/jwt/ruby-jwt/pull/784) ([@anakinj](https://github.com/anakinj)) +- Accept the RFC 8812 curve name `secp256k1` when importing EC JWKs, alongside `P-256K` [#787](https://github.com/jwt/ruby-jwt/pull/787) ([@anakinj](https://github.com/anakinj)) - Your contribution here ## [v3.3.0](https://github.com/jwt/ruby-jwt/tree/v3.3.0) (2026-09-11) diff --git a/lib/jwt/jwk/ec.rb b/lib/jwt/jwk/ec.rb index 7a7761c5..2b60fbb1 100644 --- a/lib/jwt/jwk/ec.rb +++ b/lib/jwt/jwk/ec.rb @@ -230,7 +230,7 @@ def to_openssl_curve(crv) when 'P-256' then 'prime256v1' when 'P-384' then 'secp384r1' when 'P-521' then 'secp521r1' - when 'P-256K' then 'secp256k1' + when 'secp256k1', 'P-256K' then 'secp256k1' else raise JWT::JWKError, 'Invalid curve provided' end end diff --git a/spec/jwt/jwk/ec_spec.rb b/spec/jwt/jwk/ec_spec.rb index 301f3b61..e1c47aee 100644 --- a/spec/jwt/jwk/ec_spec.rb +++ b/spec/jwt/jwk/ec_spec.rb @@ -169,6 +169,7 @@ expect(JWT::JWK::EC.to_openssl_curve('P-384')).to eq('secp384r1') expect(JWT::JWK::EC.to_openssl_curve('P-521')).to eq('secp521r1') expect(JWT::JWK::EC.to_openssl_curve('P-256K')).to eq('secp256k1') + expect(JWT::JWK::EC.to_openssl_curve('secp256k1')).to eq('secp256k1') end end context 'when an invalid curve name is given' do @@ -252,5 +253,26 @@ end end end + + context 'when crv is the RFC 8812 name secp256k1' do + let(:ec_key) { OpenSSL::PKey::EC.generate('secp256k1') } + let(:keypair) { ec_key } + let(:include_private) { true } + let(:params) { exported_key.merge(crv: 'secp256k1') } + + it 'imports the key and keeps the given curve name' do + expect(subject.private?).to eq(true) + expect(subject.verify_key.group.curve_name).to eq('secp256k1') + expect(subject.export(include_private: true)).to eq(params) + end + + it 'verifies an ES256K token' do + token = JWT.encode({ 'sub' => '1' }, ec_key, 'ES256K', kid: subject.kid) + jwks = { keys: [params.reject { |k, _v| k == :d }] } + + payload, = JWT.decode(token, nil, true, algorithms: ['ES256K'], jwks: jwks) + expect(payload).to eq('sub' => '1') + end + end end end