You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

jwtsigningkey.go 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package oauth2
  5. import (
  6. "crypto/ecdsa"
  7. "crypto/ed25519"
  8. "crypto/elliptic"
  9. "crypto/rand"
  10. "crypto/rsa"
  11. "crypto/sha256"
  12. "crypto/x509"
  13. "encoding/base64"
  14. "encoding/pem"
  15. "fmt"
  16. "math/big"
  17. "os"
  18. "path/filepath"
  19. "strings"
  20. "code.gitea.io/gitea/modules/generate"
  21. "code.gitea.io/gitea/modules/log"
  22. "code.gitea.io/gitea/modules/setting"
  23. "code.gitea.io/gitea/modules/util"
  24. "github.com/golang-jwt/jwt/v4"
  25. ini "gopkg.in/ini.v1"
  26. )
  27. // ErrInvalidAlgorithmType represents an invalid algorithm error.
  28. type ErrInvalidAlgorithmType struct {
  29. Algorightm string
  30. }
  31. func (err ErrInvalidAlgorithmType) Error() string {
  32. return fmt.Sprintf("JWT signing algorithm is not supported: %s", err.Algorightm)
  33. }
  34. // JWTSigningKey represents a algorithm/key pair to sign JWTs
  35. type JWTSigningKey interface {
  36. IsSymmetric() bool
  37. SigningMethod() jwt.SigningMethod
  38. SignKey() interface{}
  39. VerifyKey() interface{}
  40. ToJWK() (map[string]string, error)
  41. PreProcessToken(*jwt.Token)
  42. }
  43. type hmacSigningKey struct {
  44. signingMethod jwt.SigningMethod
  45. secret []byte
  46. }
  47. func (key hmacSigningKey) IsSymmetric() bool {
  48. return true
  49. }
  50. func (key hmacSigningKey) SigningMethod() jwt.SigningMethod {
  51. return key.signingMethod
  52. }
  53. func (key hmacSigningKey) SignKey() interface{} {
  54. return key.secret
  55. }
  56. func (key hmacSigningKey) VerifyKey() interface{} {
  57. return key.secret
  58. }
  59. func (key hmacSigningKey) ToJWK() (map[string]string, error) {
  60. return map[string]string{
  61. "kty": "oct",
  62. "alg": key.SigningMethod().Alg(),
  63. }, nil
  64. }
  65. func (key hmacSigningKey) PreProcessToken(*jwt.Token) {}
  66. type rsaSingingKey struct {
  67. signingMethod jwt.SigningMethod
  68. key *rsa.PrivateKey
  69. id string
  70. }
  71. func newRSASingingKey(signingMethod jwt.SigningMethod, key *rsa.PrivateKey) (rsaSingingKey, error) {
  72. kid, err := createPublicKeyFingerprint(key.Public().(*rsa.PublicKey))
  73. if err != nil {
  74. return rsaSingingKey{}, err
  75. }
  76. return rsaSingingKey{
  77. signingMethod,
  78. key,
  79. base64.RawURLEncoding.EncodeToString(kid),
  80. }, nil
  81. }
  82. func (key rsaSingingKey) IsSymmetric() bool {
  83. return false
  84. }
  85. func (key rsaSingingKey) SigningMethod() jwt.SigningMethod {
  86. return key.signingMethod
  87. }
  88. func (key rsaSingingKey) SignKey() interface{} {
  89. return key.key
  90. }
  91. func (key rsaSingingKey) VerifyKey() interface{} {
  92. return key.key.Public()
  93. }
  94. func (key rsaSingingKey) ToJWK() (map[string]string, error) {
  95. pubKey := key.key.Public().(*rsa.PublicKey)
  96. return map[string]string{
  97. "kty": "RSA",
  98. "alg": key.SigningMethod().Alg(),
  99. "kid": key.id,
  100. "e": base64.RawURLEncoding.EncodeToString(big.NewInt(int64(pubKey.E)).Bytes()),
  101. "n": base64.RawURLEncoding.EncodeToString(pubKey.N.Bytes()),
  102. }, nil
  103. }
  104. func (key rsaSingingKey) PreProcessToken(token *jwt.Token) {
  105. token.Header["kid"] = key.id
  106. }
  107. type eddsaSigningKey struct {
  108. signingMethod jwt.SigningMethod
  109. key ed25519.PrivateKey
  110. id string
  111. }
  112. func newEdDSASingingKey(signingMethod jwt.SigningMethod, key ed25519.PrivateKey) (eddsaSigningKey, error) {
  113. kid, err := createPublicKeyFingerprint(key.Public().(ed25519.PublicKey))
  114. if err != nil {
  115. return eddsaSigningKey{}, err
  116. }
  117. return eddsaSigningKey{
  118. signingMethod,
  119. key,
  120. base64.RawURLEncoding.EncodeToString(kid),
  121. }, nil
  122. }
  123. func (key eddsaSigningKey) IsSymmetric() bool {
  124. return false
  125. }
  126. func (key eddsaSigningKey) SigningMethod() jwt.SigningMethod {
  127. return key.signingMethod
  128. }
  129. func (key eddsaSigningKey) SignKey() interface{} {
  130. return key.key
  131. }
  132. func (key eddsaSigningKey) VerifyKey() interface{} {
  133. return key.key.Public()
  134. }
  135. func (key eddsaSigningKey) ToJWK() (map[string]string, error) {
  136. pubKey := key.key.Public().(ed25519.PublicKey)
  137. return map[string]string{
  138. "alg": key.SigningMethod().Alg(),
  139. "kid": key.id,
  140. "kty": "OKP",
  141. "crv": "Ed25519",
  142. "x": base64.RawURLEncoding.EncodeToString(pubKey),
  143. }, nil
  144. }
  145. func (key eddsaSigningKey) PreProcessToken(token *jwt.Token) {
  146. token.Header["kid"] = key.id
  147. }
  148. type ecdsaSingingKey struct {
  149. signingMethod jwt.SigningMethod
  150. key *ecdsa.PrivateKey
  151. id string
  152. }
  153. func newECDSASingingKey(signingMethod jwt.SigningMethod, key *ecdsa.PrivateKey) (ecdsaSingingKey, error) {
  154. kid, err := createPublicKeyFingerprint(key.Public().(*ecdsa.PublicKey))
  155. if err != nil {
  156. return ecdsaSingingKey{}, err
  157. }
  158. return ecdsaSingingKey{
  159. signingMethod,
  160. key,
  161. base64.RawURLEncoding.EncodeToString(kid),
  162. }, nil
  163. }
  164. func (key ecdsaSingingKey) IsSymmetric() bool {
  165. return false
  166. }
  167. func (key ecdsaSingingKey) SigningMethod() jwt.SigningMethod {
  168. return key.signingMethod
  169. }
  170. func (key ecdsaSingingKey) SignKey() interface{} {
  171. return key.key
  172. }
  173. func (key ecdsaSingingKey) VerifyKey() interface{} {
  174. return key.key.Public()
  175. }
  176. func (key ecdsaSingingKey) ToJWK() (map[string]string, error) {
  177. pubKey := key.key.Public().(*ecdsa.PublicKey)
  178. return map[string]string{
  179. "kty": "EC",
  180. "alg": key.SigningMethod().Alg(),
  181. "kid": key.id,
  182. "crv": pubKey.Params().Name,
  183. "x": base64.RawURLEncoding.EncodeToString(pubKey.X.Bytes()),
  184. "y": base64.RawURLEncoding.EncodeToString(pubKey.Y.Bytes()),
  185. }, nil
  186. }
  187. func (key ecdsaSingingKey) PreProcessToken(token *jwt.Token) {
  188. token.Header["kid"] = key.id
  189. }
  190. // createPublicKeyFingerprint creates a fingerprint of the given key.
  191. // The fingerprint is the sha256 sum of the PKIX structure of the key.
  192. func createPublicKeyFingerprint(key interface{}) ([]byte, error) {
  193. bytes, err := x509.MarshalPKIXPublicKey(key)
  194. if err != nil {
  195. return nil, err
  196. }
  197. checksum := sha256.Sum256(bytes)
  198. return checksum[:], nil
  199. }
  200. // CreateJWTSigningKey creates a signing key from an algorithm / key pair.
  201. func CreateJWTSigningKey(algorithm string, key interface{}) (JWTSigningKey, error) {
  202. var signingMethod jwt.SigningMethod
  203. switch algorithm {
  204. case "HS256":
  205. signingMethod = jwt.SigningMethodHS256
  206. case "HS384":
  207. signingMethod = jwt.SigningMethodHS384
  208. case "HS512":
  209. signingMethod = jwt.SigningMethodHS512
  210. case "RS256":
  211. signingMethod = jwt.SigningMethodRS256
  212. case "RS384":
  213. signingMethod = jwt.SigningMethodRS384
  214. case "RS512":
  215. signingMethod = jwt.SigningMethodRS512
  216. case "ES256":
  217. signingMethod = jwt.SigningMethodES256
  218. case "ES384":
  219. signingMethod = jwt.SigningMethodES384
  220. case "ES512":
  221. signingMethod = jwt.SigningMethodES512
  222. case "EdDSA":
  223. signingMethod = jwt.SigningMethodEdDSA
  224. default:
  225. return nil, ErrInvalidAlgorithmType{algorithm}
  226. }
  227. switch signingMethod.(type) {
  228. case *jwt.SigningMethodEd25519:
  229. privateKey, ok := key.(ed25519.PrivateKey)
  230. if !ok {
  231. return nil, jwt.ErrInvalidKeyType
  232. }
  233. return newEdDSASingingKey(signingMethod, privateKey)
  234. case *jwt.SigningMethodECDSA:
  235. privateKey, ok := key.(*ecdsa.PrivateKey)
  236. if !ok {
  237. return nil, jwt.ErrInvalidKeyType
  238. }
  239. return newECDSASingingKey(signingMethod, privateKey)
  240. case *jwt.SigningMethodRSA:
  241. privateKey, ok := key.(*rsa.PrivateKey)
  242. if !ok {
  243. return nil, jwt.ErrInvalidKeyType
  244. }
  245. return newRSASingingKey(signingMethod, privateKey)
  246. default:
  247. secret, ok := key.([]byte)
  248. if !ok {
  249. return nil, jwt.ErrInvalidKeyType
  250. }
  251. return hmacSigningKey{signingMethod, secret}, nil
  252. }
  253. }
  254. // DefaultSigningKey is the default signing key for JWTs.
  255. var DefaultSigningKey JWTSigningKey
  256. // InitSigningKey creates the default signing key from settings or creates a random key.
  257. func InitSigningKey() error {
  258. var err error
  259. var key interface{}
  260. switch setting.OAuth2.JWTSigningAlgorithm {
  261. case "HS256":
  262. fallthrough
  263. case "HS384":
  264. fallthrough
  265. case "HS512":
  266. key, err = loadOrCreateSymmetricKey()
  267. case "RS256":
  268. fallthrough
  269. case "RS384":
  270. fallthrough
  271. case "RS512":
  272. fallthrough
  273. case "ES256":
  274. fallthrough
  275. case "ES384":
  276. fallthrough
  277. case "ES512":
  278. fallthrough
  279. case "EdDSA":
  280. key, err = loadOrCreateAsymmetricKey()
  281. default:
  282. return ErrInvalidAlgorithmType{setting.OAuth2.JWTSigningAlgorithm}
  283. }
  284. if err != nil {
  285. return fmt.Errorf("Error while loading or creating JWT key: %v", err)
  286. }
  287. signingKey, err := CreateJWTSigningKey(setting.OAuth2.JWTSigningAlgorithm, key)
  288. if err != nil {
  289. return err
  290. }
  291. DefaultSigningKey = signingKey
  292. return nil
  293. }
  294. // loadOrCreateSymmetricKey checks if the configured secret is valid.
  295. // If it is not valid a new secret is created and saved in the configuration file.
  296. func loadOrCreateSymmetricKey() (interface{}, error) {
  297. key := make([]byte, 32)
  298. n, err := base64.RawURLEncoding.Decode(key, []byte(setting.OAuth2.JWTSecretBase64))
  299. if err != nil || n != 32 {
  300. key, err = generate.NewJwtSecret()
  301. if err != nil {
  302. log.Fatal("error generating JWT secret: %v", err)
  303. return nil, err
  304. }
  305. setting.CreateOrAppendToCustomConf(func(cfg *ini.File) {
  306. secretBase64 := base64.RawURLEncoding.EncodeToString(key)
  307. cfg.Section("oauth2").Key("JWT_SECRET").SetValue(secretBase64)
  308. })
  309. }
  310. return key, nil
  311. }
  312. // loadOrCreateAsymmetricKey checks if the configured private key exists.
  313. // If it does not exist a new random key gets generated and saved on the configured path.
  314. func loadOrCreateAsymmetricKey() (interface{}, error) {
  315. keyPath := setting.OAuth2.JWTSigningPrivateKeyFile
  316. isExist, err := util.IsExist(keyPath)
  317. if err != nil {
  318. log.Fatal("Unable to check if %s exists. Error: %v", keyPath, err)
  319. }
  320. if !isExist {
  321. err := func() error {
  322. key, err := func() (interface{}, error) {
  323. switch {
  324. case strings.HasPrefix(setting.OAuth2.JWTSigningAlgorithm, "RS"):
  325. return rsa.GenerateKey(rand.Reader, 4096)
  326. case setting.OAuth2.JWTSigningAlgorithm == "EdDSA":
  327. _, pk, err := ed25519.GenerateKey(rand.Reader)
  328. return pk, err
  329. default:
  330. return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  331. }
  332. }()
  333. if err != nil {
  334. return err
  335. }
  336. bytes, err := x509.MarshalPKCS8PrivateKey(key)
  337. if err != nil {
  338. return err
  339. }
  340. privateKeyPEM := &pem.Block{Type: "PRIVATE KEY", Bytes: bytes}
  341. if err := os.MkdirAll(filepath.Dir(keyPath), os.ModePerm); err != nil {
  342. return err
  343. }
  344. f, err := os.OpenFile(keyPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
  345. if err != nil {
  346. return err
  347. }
  348. defer func() {
  349. if err = f.Close(); err != nil {
  350. log.Error("Close: %v", err)
  351. }
  352. }()
  353. return pem.Encode(f, privateKeyPEM)
  354. }()
  355. if err != nil {
  356. log.Fatal("Error generating private key: %v", err)
  357. return nil, err
  358. }
  359. }
  360. bytes, err := os.ReadFile(keyPath)
  361. if err != nil {
  362. return nil, err
  363. }
  364. block, _ := pem.Decode(bytes)
  365. if block == nil {
  366. return nil, fmt.Errorf("no valid PEM data found in %s", keyPath)
  367. } else if block.Type != "PRIVATE KEY" {
  368. return nil, fmt.Errorf("expected PRIVATE KEY, got %s in %s", block.Type, keyPath)
  369. }
  370. return x509.ParsePKCS8PrivateKey(block.Bytes)
  371. }