Gin - RSA512 signature using JWT's asymmetric encryption algorithm

Keywords: security gin

1.RSA asymmetric encryption

RSA asymmetric encryption encrypts the signature, not the header and load of jwt. The header and load are encrypted with base64 jwt official website You can know
Tokens are put together by spelling strings.
It is concluded that:
1. User defined data can be placed in the load, but sensitive data cannot be placed.



As shown in the figure above, even if you take the token here, you can see the header and payload, but the Invalid Signature displayed in the lower left corner is invalid because there is no public key
Enter the corresponding public key, and you can see that the verification is completed. Thus, you can know the usage mode and scenario of public key and key.

2. Public key and key

Asymmetric encryption requires public key and key, which appear in pairs
The general key is used for signature and the public key is used for verification. This is generally the data stored in two files. The rsa Library of golang can be used to generate the key, and the public key can be obtained directly from the key.

Generating keys using golang's rsa Library

In fact, here, we can choose to regenerate a bunch of public keys every time a new token is generated, and then publish the public keys to the system that needs to update the public keys, or we can always use only one pair of public keys. This is very helpful in distributed systems. The system that generates the key pair can only publish the public key to each system. Because they only need to do the verification function.

func CreateKeys() (*rsa.PrivateKey, rsa.PublicKey) {
	privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
	if err != nil {
		panic(err)
	}
	publicKey := privateKey.PublicKey
	return privateKey, publicKey
}

Here, we save the generated key into a private.key file

Get the key according to the file path

func GetPrivateKey(privateKeyFilePath string) *rsa.PrivateKey {
	file, err := os.Open(filePath)
	if err != nil {
		fmt.Errorf("%v", err)
	}
	pkBytes, err := ioutil.ReadAll(file)
	if err != nil {
		fmt.Errorf("%v", err)
	}
	key, err := jwt.ParseRSAPrivateKeyFromPEM(pkBytes)
	if err != nil {
		fmt.Errorf("cannot parse private key>%v", err)
	}
	return key
}

Get public key

func GetPublicKeyByPrivateKey(privateKeyFilePath string) *rsa.PublicKey {
	privateKey := GetPrivateKey(filePath)
	publicKey := privateKey.PublicKey
	return &publicKey
}

Generate token

func (j *JWT) CreateToken(claims request.CustomClaims) (string, error) {
//Select the signature algorithm and pass in the load
	token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims)
	//Get key
	privateKey := GetPrivateKey("./source/keyFile/private.key")
	//autograph
	tokenStr, err := token.SignedString(privateKey)
	return tokenStr, err
}

Parse token

Here, the public key is obtained from the key because it is parsed in the jwt signature system. It doesn't matter

// Parse token
func (j *JWT) ParseToken(tokenString string) (*request.CustomClaims, error) {
	publicKey := GetPublicKeyByPrivateKey("Key data file path")
	token, err := jwt.ParseWithClaims(tokenString, &request.CustomClaims{}, func(token *jwt.Token) (i interface{}, e error) {
		return publicKey, nil
	})
	if err != nil {
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
				return nil, TokenMalformed
			} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
				// Token is expired
				return nil, TokenExpired
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
				return nil, TokenNotValidYet
			} else {
				return nil, TokenInvalid
			}
		}
	}
	if token != nil {
		if claims, ok := token.Claims.(*request.CustomClaims); ok && token.Valid {
			return claims, nil
		}
		return nil, TokenInvalid
	} else {
		return nil, TokenInvalid
	}
}

Posted by panic! on Mon, 06 Dec 2021 16:03:23 -0800