mirror of
https://github.com/distribution/distribution.git
synced 2025-04-27 19:15:28 +00:00
fix: implement JWK thumbprint for Ed25519 public keys (#4626)
This commit is contained in:
commit
e827ce2772
@ -690,7 +690,7 @@ Default `signingalgorithms`:
|
|||||||
Additional notes on `rootcertbundle`:
|
Additional notes on `rootcertbundle`:
|
||||||
|
|
||||||
- The public key of this certificate will be automatically added to the list of known keys.
|
- The public key of this certificate will be automatically added to the list of known keys.
|
||||||
- The public key will be identified by it's [RFC7638 Thumbprint](https://datatracker.ietf.org/doc/html/rfc7638).
|
- The public key will be identified by its JWK Thumbprint. See [RFC 7638](https://datatracker.ietf.org/doc/html/rfc7638) and [RFC 8037](https://datatracker.ietf.org/doc/html/rfc8037) for reference.
|
||||||
|
|
||||||
For more information about Token based authentication configuration, see the
|
For more information about Token based authentication configuration, see the
|
||||||
[specification](../spec/auth/token.md).
|
[specification](../spec/auth/token.md).
|
||||||
|
@ -351,7 +351,7 @@ func newAccessController(options map[string]interface{}) (auth.AccessController,
|
|||||||
rootPool := x509.NewCertPool()
|
rootPool := x509.NewCertPool()
|
||||||
for _, rootCert := range rootCerts {
|
for _, rootCert := range rootCerts {
|
||||||
rootPool.AddCert(rootCert)
|
rootPool.AddCert(rootCert)
|
||||||
if key := GetRFC7638Thumbprint(rootCert.PublicKey); key != "" {
|
if key := GetJWKThumbprint(rootCert.PublicKey); key != "" {
|
||||||
trustedKeys[key] = rootCert.PublicKey
|
trustedKeys[key] = rootCert.PublicKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package token
|
|||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
@ -54,15 +55,26 @@ func hashAndEncode(payload string) string {
|
|||||||
return base64.RawURLEncoding.EncodeToString(shasum[:])
|
return base64.RawURLEncoding.EncodeToString(shasum[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: use GetJWKThumbprint instead.
|
||||||
|
func GetRFC7638Thumbprint(publickey crypto.PublicKey) string {
|
||||||
|
return getJWKThumbprint(publickey, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetJWKThumbprint calculates the JWK thumbprint of a public key.
|
||||||
|
// The current implementation uses SHA256, but this algorithm may change
|
||||||
|
// in the future as cryptographic best practices evolve. It returns an
|
||||||
|
// empty string if the public key type is not supported.
|
||||||
|
func GetJWKThumbprint(publickey crypto.PublicKey) string {
|
||||||
|
return getJWKThumbprint(publickey, false)
|
||||||
|
}
|
||||||
|
|
||||||
// RFC7638 states in section 3 sub 1 that the keys in the JSON object payload
|
// RFC7638 states in section 3 sub 1 that the keys in the JSON object payload
|
||||||
// are required to be ordered lexicographical order. Golang does not guarantee
|
// are required to be ordered lexicographical order. Golang does not guarantee
|
||||||
// order of keys[0]
|
// order of keys[0]
|
||||||
// [0]: https://groups.google.com/g/golang-dev/c/zBQwhm3VfvU
|
// [0]: https://groups.google.com/g/golang-dev/c/zBQwhm3VfvU
|
||||||
//
|
//
|
||||||
// The payloads are small enough to create the JSON strings manually
|
// The payloads are small enough to create the JSON strings manually
|
||||||
func GetRFC7638Thumbprint(publickey crypto.PublicKey) string {
|
func getJWKThumbprint(publickey crypto.PublicKey, skipED25519 bool) string {
|
||||||
var payload string
|
|
||||||
|
|
||||||
switch pubkey := publickey.(type) {
|
switch pubkey := publickey.(type) {
|
||||||
case *rsa.PublicKey:
|
case *rsa.PublicKey:
|
||||||
e_big := big.NewInt(int64(pubkey.E)).Bytes()
|
e_big := big.NewInt(int64(pubkey.E)).Bytes()
|
||||||
@ -70,17 +82,22 @@ func GetRFC7638Thumbprint(publickey crypto.PublicKey) string {
|
|||||||
e := base64.RawURLEncoding.EncodeToString(e_big)
|
e := base64.RawURLEncoding.EncodeToString(e_big)
|
||||||
n := base64.RawURLEncoding.EncodeToString(pubkey.N.Bytes())
|
n := base64.RawURLEncoding.EncodeToString(pubkey.N.Bytes())
|
||||||
|
|
||||||
payload = fmt.Sprintf(`{"e":"%s","kty":"RSA","n":"%s"}`, e, n)
|
return hashAndEncode(fmt.Sprintf(`{"e":"%s","kty":"RSA","n":"%s"}`, e, n))
|
||||||
case *ecdsa.PublicKey:
|
case *ecdsa.PublicKey:
|
||||||
params := pubkey.Params()
|
params := pubkey.Params()
|
||||||
crv := params.Name
|
crv := params.Name
|
||||||
x := base64.RawURLEncoding.EncodeToString(params.Gx.Bytes())
|
x := base64.RawURLEncoding.EncodeToString(params.Gx.Bytes())
|
||||||
y := base64.RawURLEncoding.EncodeToString(params.Gy.Bytes())
|
y := base64.RawURLEncoding.EncodeToString(params.Gy.Bytes())
|
||||||
|
|
||||||
payload = fmt.Sprintf(`{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`, crv, x, y)
|
return hashAndEncode(fmt.Sprintf(`{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`, crv, x, y))
|
||||||
|
case ed25519.PublicKey:
|
||||||
|
if skipED25519 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
x := base64.RawURLEncoding.EncodeToString(pubkey)
|
||||||
|
|
||||||
|
return hashAndEncode(fmt.Sprintf(`{"crv":"Ed25519","kty":"OTP","x":"%s"}`, x))
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
return hashAndEncode(payload)
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user