github.com/avenga/couper@v1.12.2/accesscontrol/jwk/jwk.go (about)

     1  package jwk
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"crypto/elliptic"
     6  	"crypto/rsa"
     7  	"crypto/x509"
     8  	"encoding/base64"
     9  	"encoding/json"
    10  	"fmt"
    11  	"math/big"
    12  )
    13  
    14  type JWK struct {
    15  	Key       interface{}
    16  	KeyID     string
    17  	KeyType   string
    18  	Algorithm string
    19  	Use       string
    20  }
    21  
    22  type rawJWK struct {
    23  	Alg string `json:"alg"`
    24  	Kid string `json:"kid"`
    25  	Kty string `json:"kty"`
    26  	Use string `json:"use"`
    27  	// RSA public key
    28  	E   *base64URLEncodedField `json:"e"`
    29  	N   *base64URLEncodedField `json:"n"`
    30  	X5c []*base64EncodedField  `json:"x5c"`
    31  	// ECDSA public key
    32  	Crv string                 `json:"crv"`
    33  	X   *base64URLEncodedField `json:"x"`
    34  	Y   *base64URLEncodedField `json:"y"`
    35  	//X5t *base64URLEncodedField `json:"x5t"`
    36  	//X5tS256" *base64URLEncodedField `json:"x5t#S256"`
    37  }
    38  
    39  func (j *JWK) UnmarshalJSON(data []byte) error {
    40  	raw := &rawJWK{}
    41  	err := json.Unmarshal(data, raw)
    42  	if err != nil {
    43  		// TODO log warning properly
    44  		fmt.Printf("Invalid JWK: %v\n", err)
    45  		return nil
    46  	}
    47  
    48  	var key interface{}
    49  	jwk := &JWK{KeyID: raw.Kid, Algorithm: raw.Alg, KeyType: raw.Kty, Use: raw.Use}
    50  
    51  	switch raw.Kty {
    52  	case "RSA":
    53  		key, err = getPublicKeyFromX5c(raw.X5c)
    54  		if err != nil {
    55  			// TODO log warning properly
    56  			fmt.Printf("Invalid x5c: %v\n", err)
    57  			return nil
    58  		}
    59  
    60  		if key != nil {
    61  			jwk.Key = key
    62  		} else if raw.N != nil && raw.E != nil {
    63  			jwk.Key = &rsa.PublicKey{
    64  				N: raw.N.toBigInt(),
    65  				E: raw.E.toInt(),
    66  			}
    67  		} else {
    68  			// TODO log warning properly
    69  			fmt.Printf("Ignoring invalid %s key: %q\n", raw.Kty, raw.Kid)
    70  			return nil
    71  		}
    72  	case "EC":
    73  		key, err = getPublicKeyFromX5c(raw.X5c)
    74  		if err != nil {
    75  			// TODO log warning properly
    76  			fmt.Printf("Invalid x5c: %v\n", err)
    77  			return nil
    78  		}
    79  
    80  		if key != nil {
    81  			jwk.Key = key
    82  		} else {
    83  			curve, err := getCurve(raw.Crv)
    84  			if err == nil && raw.X != nil && raw.Y != nil {
    85  				jwk.Key = &ecdsa.PublicKey{
    86  					Curve: curve,
    87  					X:     raw.X.toBigInt(),
    88  					Y:     raw.Y.toBigInt(),
    89  				}
    90  			} else {
    91  				fmt.Printf("Ignoring invalid %s key: %q (invalid crv/x/y)\n", raw.Kty, raw.Kid)
    92  				return nil
    93  			}
    94  		}
    95  	default:
    96  		// TODO log warning properly
    97  		fmt.Printf("Found unsupported %s key: %q\n", raw.Kty, raw.Kid)
    98  		return nil
    99  	}
   100  
   101  	*j = *jwk
   102  
   103  	return nil
   104  }
   105  
   106  // Base64URL encoded
   107  
   108  type base64URLEncodedField struct {
   109  	data []byte
   110  }
   111  
   112  func newBase64URLEncodedField(data []byte) *base64URLEncodedField {
   113  	return &base64URLEncodedField{
   114  		data: data,
   115  	}
   116  }
   117  
   118  func (f *base64URLEncodedField) MarshalJSON() ([]byte, error) {
   119  	return json.Marshal(base64.RawURLEncoding.EncodeToString(f.data))
   120  }
   121  
   122  func (f *base64URLEncodedField) UnmarshalJSON(data []byte) error {
   123  	var encoded string
   124  	err := json.Unmarshal(data, &encoded)
   125  	if err != nil {
   126  		return err
   127  	}
   128  
   129  	if encoded == "" {
   130  		return nil
   131  	}
   132  
   133  	decoded, err := base64.RawURLEncoding.DecodeString(encoded)
   134  	if err != nil {
   135  		return err
   136  	}
   137  
   138  	*f = *newBase64URLEncodedField(decoded)
   139  
   140  	return nil
   141  }
   142  
   143  func (f base64URLEncodedField) toBigInt() *big.Int {
   144  	return new(big.Int).SetBytes(f.data)
   145  }
   146  
   147  func (f base64URLEncodedField) toInt() int {
   148  	return int(f.toBigInt().Int64())
   149  }
   150  
   151  // Base64 encoded
   152  
   153  type base64EncodedField struct {
   154  	data []byte
   155  }
   156  
   157  func (f *base64EncodedField) UnmarshalJSON(data []byte) error {
   158  	var encoded string
   159  	err := json.Unmarshal(data, &encoded)
   160  	if err != nil {
   161  		return err
   162  	}
   163  
   164  	if encoded == "" {
   165  		return nil
   166  	}
   167  
   168  	decoded, err := base64.StdEncoding.DecodeString(encoded)
   169  	if err != nil {
   170  		return err
   171  	}
   172  
   173  	*f = base64EncodedField{
   174  		data: decoded,
   175  	}
   176  
   177  	return nil
   178  }
   179  
   180  func getCurve(name string) (elliptic.Curve, error) {
   181  	switch name {
   182  	case "P-256":
   183  		return elliptic.P256(), nil
   184  	case "P-384":
   185  		return elliptic.P384(), nil
   186  	case "P-521":
   187  		return elliptic.P521(), nil
   188  	}
   189  	return nil, fmt.Errorf("invalid crv: %s", name)
   190  }
   191  
   192  func getPublicKeyFromX5c(x5c []*base64EncodedField) (interface{}, error) {
   193  	if len(x5c) == 0 {
   194  		return nil, nil
   195  	}
   196  
   197  	certificate, err := x509.ParseCertificate(x5c[0].data)
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  	return certificate.PublicKey, nil
   202  }