Develop short video e-commerce from scratch using nimbus-jose-jwt for symmetric signature and asymmetric signature JWT implementation

The basic introduction of JWT can be found at this address: https://jwt.io/introduction , the following are all excerpts from above.

What is JSON Web Token

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way to securely transmit information between parties , formatted as a JSON object . These messages can be verified and trusted because they are digitally signed .

A JWT can be signed using a symmetric algorithm (using the HMAC algorithm) or an asymmetric algorithm (using a public/private key pair of RSA or ECDSA).

While JWTs can be encrypted for confidentiality (JWK), we use signed tokens (JWS) more.

  • Signed tokens verify the integrity of the claims, while encrypted tokens hide those claims from other parties.
  • When signing using a public/private key pair, the signature also proves that only the party holding the private key is the signer.

When to Use JSON Web Tokens

Here are some common scenarios that apply to JSON Web Tokens:

  • Authorization : This is the most common scenario for using JWT. Once the user is logged in, every subsequent request will contain the JWT, allowing the user to access the routes, services, and resources permitted by that token. Single sign-on is a feature that uses JWT extensively because it has little overhead and can be easily used across different domains.

  • Information exchange : JSON Web Tokens are a good way to securely transfer information between parties. Because JWTs can be signed, for example, using a public/private key pair, you can be confident that the sender is who they say they are. Also, since the signature is calculated using the header and payload, you can also verify that the content has not been tampered with.

What is the structure of JSON Web Token

In its compact form, the JSON Web Token consists of three parts separated by dots (.), which are:

  • Header
  • Payload
  • Signature

Therefore, a JWT is usually:xxxxx.yyyyy.zzzzz

Let's explain the different parts one by one.

Header

The header usually consists of two parts: the token type (JWT) and the signature algorithm used, e.g. HMAC SHA256or RSA.

{
    
    
  "alg": "HS256",
  "typ": "JWT"
}

This JSON is then Base64Url encoded to form the first part of the JWT.

Payload

The second part of the token is the payload, which contains claims. Claims are statements about an entity (usually a user) and attached data. There are three types of declarations: registration declarations, public declarations, and private declarations.

  • Registered Declarations : This is a set of predefined declarations which are not mandatory but recommended to provide a useful set of interoperable declarations. Some of these include: iss (issuer), exp (expiration time), sub (subject), aud (audience), etc.

Note that the claim name is only three characters long, since JWTs are designed to be compact.

  • Public Claims : These claims can be freely defined by the person using the JWT. But to avoid conflicts, they should be defined in the IANA JSON Web Token Registry, or as URIs containing conflicting security namespaces.

  • Private Statements : These are custom statements used to share information between parties who agree to use them, and are neither registration statements nor public statements.

{
    
    
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

Then it is encoded by Base64Url to form the second part of JSON Web Token.

Note that for signed tokens, although protected from tampering, this information can be read by anyone . Do not put confidential information in the payload or header elements of a JWT unless it is encrypted.

Signature

To create the signature part, you need to take the encoded header, the encoded payload, a key, the algorithm specified in the header, and sign it.

For example, if you wanted to use the HMAC SHA256 algorithm, the signature would be created as follows:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

Signatures are used to verify that the message has not been altered in transit and, for tokens signed with a private key, also verify the identity of the sender of the JWT.

stitched together

The output is three Base64-URL strings separated by dots, which can be easily passed in HTML and HTTP environments, and is more compact compared to XML-based standards such as SAML.

Below is an example of a JWT with a previously encoded header and payload, signed with a secret key.

Encoded JWT

How to use JSON Web Token

In authentication, when a user successfully logs in with their credentials, a JSON Web Token is returned. It is important to note that JWTs should not be kept longer than necessary, as they are credentials that need to be protected. Also, due to the lack of security, storing sensitive session data in browser storage should be avoided.

Token transfer: When a user wants to access a protected route or resource, the JWT is usually included in the request, placed in the authorization header, using the Bearer scheme. The content of the header should look like this:

Authorization: Bearer <token>

The server's protected route checks for the presence of a valid JWT in the Authorization header, and if so, allows the user to access the protected resource. If the JWT contains the necessary data, it can reduce the need for database queries for some operations, although this is not always the case.

Note that if the JWT token is sent via HTTP headers, try to keep it from being too large. Some servers do not accept headers larger than 8 KB.

Tool Library

You can find more authoritative and easy-to-use tool libraries at this website .

Most of them use one of the above three, we use nimbus-jose-jwt here.

Documentation: https://connect2id.com/products/nimbus-jose-jwt

rely

<dependency>
  <groupId>com.nimbusds</groupId>
  <artifactId>nimbus-jose-jwt</artifactId>
  <version>9.31</version>
</dependency>
<!-- 如果您正在使用以下情况,请取消注释下面的依赖项:
     - JDK 10或更早版本,并且您想要使用RSASSA-PSS(PS256、PS384、PS512)签名算法。
     - JDK 10或更早版本,并且您想要使用EdECDH(X25519或X448)椭圆曲线迪菲-赫尔曼密钥交换加密。
     - JDK 14或更早版本,并且您想要使用EdDSA(Ed25519或Ed448)椭圆曲线签名算法。
     在JDK 15或更高版本上,这些算法是不必要的。
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.70</version>
    <scope>runtime</scope>
</dependency>
-->

The latest version

process

Symmetric signature

Signature example :

// 生成对称加密密钥
 byte[] sharedKey = "YourSharedKey-122345678sahkjhjkasdfasdf".getBytes();

// 创建一个JWT对象
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().subject("user123")
                // 设置过期时间为当前时间后的一分钟
                .expirationTime(new Date(System.currentTimeMillis() + 60 * 1000)).build();

JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.HS256).build();
SignedJWT signedJWT = new SignedJWT(header, claimsSet);

// 创建HMAC签名器
JWSSigner signer = new MACSigner(sharedKey);

// 对JWT进行签名
signedJWT.sign(signer);

// 将JWT序列化为字符串
String jwtString = signedJWT.serialize();

System.out.println("JWT Token: " + jwtString);

Signature verification example

// 生成对称加密密钥
 byte[] sharedKey = "YourSharedKey-122345678sahkjhjkasdfasdf".getBytes();

// 解析JWT字符串
String jwtString = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2ODg1NDY2MDAsInN1YiI6InVzZXIxMjMifQ.3mGtNjwt6Z50DhEeBv2zo9qi8aHGh9Mu2RWLVeH0FE8";
SignedJWT signedJWT = SignedJWT.parse(jwtString);
// 创建HMAC验证器
JWSVerifier verifier = new MACVerifier(sharedKey);
// 验证JWT签名
boolean isValid = signedJWT.verify(verifier);

if (isValid) {
    
    
      System.out.println("JWT signature is valid.");
      // 获取JWT的声明
      JWTClaimsSet claimsSet = signedJWT.getJWTClaimsSet();
      System.out.println("Subject: " + claimsSet.getSubject());
      System.out.println("Expiration Time: " + claimsSet.getExpirationTime());

      if (!signedJWT.getJWTClaimsSet().getExpirationTime().after(new Date())) {
    
    
                System.out.println("JWT signature is expired.");
        }
} else {
    
    
            System.out.println("JWT signature is not valid.");
}

asymmetric signature

The RSA algorithm and the ECDSA (Elliptic Curve Digital Signature Algorithm) algorithm are commonly used asymmetric encryption algorithms for generating and verifying digital signatures.

The RSA algorithm is a number theory problem based on the factorization of large prime numbers. It uses a pair of public and private keys for encryption and decryption operations, and can also be used to generate and verify digital signatures. The RSA algorithm has good performance in terms of security and wide application, but due to its computational complexity, the encryption and decryption operations for large amounts of data may be time-consuming .

The ECDSA algorithm is based on the elliptic curve discrete logarithm problem. Compared with the RSA algorithm, the ECDSA algorithm uses a shorter key length and provides the same level of security. This makes the ECDSA algorithm more advantageous in resource-constrained environments, such as mobile devices and IoT devices. The ECDSA algorithm also has faster encryption and decryption speeds .

How to generate EC384 public and private keys

method one

// Generate an EC key pair
ECKey ecJWK = new ECKeyGenerator(Curve.P_384)
                .keyID("123")
                .generate();
ECPublicKey ecPublicKey = ecJWK.toECPublicKey();
ECPrivateKey ecPrivateKey = ecJWK.toECPrivateKey();
// 将公钥编码为Base64字符串
String publicKeyBase64 = Base64.getEncoder().encodeToString(publicKey.getEncoded());
// 将私钥编码为Base64字符串
String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKey.getEncoded())    

//  或者
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;

// 生成ECDSA密钥对
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
keyPairGenerator.initialize(384); // 使用EC384曲线
KeyPair keyPair = keyPairGenerator.generateKeyPair();

// 获取私钥和公钥
ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();

// 打印私钥和公钥
System.out.println("Private Key: " + privateKey);
System.out.println("Public Key: " + publicKey);
// 将公钥编码为Base64字符串
String publicKeyBase64 = Base64.getEncoder().encodeToString(publicKey.getEncoded());
// 将私钥编码为Base64字符串
String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKey.getEncoded())    

publicKeyBase64: MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEVT+YNmKBnvXtS11FvcKe7tBHi3aAbvk87+tBGFadfHM/zy1+Q4EjlXjbLhhl1LNPup5BHhQBG+jKRP0/Rvoy0LiNmDdX9MqC0xvTtefFKBL4CsM0vlViObOUNxumzxMH
privateKeyBase64: ME4CAQAwEAYHKoZIzj0CAQYFK4EEACIENzA1AgEBBDDAaCeLDnCRmkmZ8vs7nlnApCxBIL2RyizpY4jh1VE5Svr4d92AwjZyrt5Szl8AvPE=

way two

openssl ecparam -list_curves
# 生成私钥
openssl ecparam -genkey -name secp384r1 -noout -out ec384-private.pem
# 根据私钥生成公钥
openssl ec -in ec384-private.pem -pubout -out ec384-public.pem
# 把私钥转换为PKCS8格式
openssl pkcs8 -topk8 -nocrypt -in ec384-private.pem -out ec384-private.pem_pkcs8.pem
# 注意
publicKeyBase64 = ec384-public.pem中的字符串
privateKeyBase64 = ec384-private.pem_pkcs8.pem中的字符串

Serialize, deserialize and transfer public and private keys

Note that the private key must be kept by the issuer himself. It doesn't matter if the public key is used. The public key is meant to be made public. It can be transmitted by WeChat mail, etc.

ECDSA public keys can be stored and transmitted in a variety of formats. Here is an example encoded using Base64:

// 假设已经有Base64编码的公钥和私钥字符串
String publicKeyBase64 = "YourBase64EncodedPublicKey";
String privateKeyBase64 = "YourBase64EncodedPrivateKey";
// 将Base64字符串解码为字节数组
byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyBase64);
byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyBase64);

// 创建公钥的KeySpec对象
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes);

// 创建私钥的KeySpec对象
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);

// 使用KeyFactory生成公钥和私钥对象
KeyFactory keyFactory = KeyFactory.getInstance("EC");
ECPublicKey publicKey = (ECPublicKey) keyFactory.generatePublic(publicKeySpec);
ECPrivateKey privateKey = (ECPrivateKey) keyFactory.generatePrivate(privateKeySpec);

System.out.println("Public Key: " + publicKey);
System.out.println("Private Key: " + privateKey);

Signature example :

import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;

// 创建一个JWT对象
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
        .subject("user123")
        .expirationTime(new Date(new Date().getTime() + 60 * 1000)) // 设置过期时间为当前时间后的一分钟
        .build();

JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.ES384)
        .build();

SignedJWT signedJWT = new SignedJWT(header, claimsSet);

// 创建ECDSA私钥签名器
JWSSigner signer = new ECDSASigner(privateKey);

// 对JWT进行签名
signedJWT.sign(signer);

// 将JWT序列化为字符串
String jwtString = signedJWT.serialize();

System.out.println("JWT Token: " + jwtString);

Signature verification example

import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;

// 解析JWT字符串
SignedJWT signedJWT = SignedJWT.parse(jwtString);

// 创建ECDSA公钥验证器
JWSVerifier verifier = new ECDSAVerifier(publicKey);

// 验证JWT签名
boolean isValid;
try {
    
    
    isValid = signedJWT.verify(verifier);
} catch (JOSEException e) {
    
    
    isValid = false;
}

if (isValid) {
    
    
    System.out.println("JWT signature is valid.");

    // 获取JWT的声明
    JWTClaimsSet claimsSet = signedJWT.getJWTClaimsSet();
    System.out.println("Subject: " + claimsSet.getSubject());
    System.out.println("Expiration Time: " + claimsSet.getExpirationTime());
} else {
    
    
    System.out.println("JWT signature is not valid.");
}

Summarize

Symmetric signatures are suitable for the following situations:

  1. Fast performance requirements: Symmetric signature algorithms are generally faster than asymmetric signature algorithms because they use the same key for signing and verification.
  2. Internal communication: Symmetric signatures are an easy choice when signing is used for internal communication and keys do not need to be shared between different entities.
  3. Key management: Symmetric signatures only need to manage one key, while asymmetric signatures need to manage public and private key pairs.

Asymmetric signatures are suitable for the following situations:

  1. Security requirements: Asymmetric signatures provide higher security because it uses different keys for signing and verification, the private key is kept private and the public key is publicly shared.
  2. Cross-network communication: Asymmetric signatures are a more secure choice when signatures are used for cross-network communication where public keys need to be shared between different entities.
  3. Digital certificates: Asymmetric signatures are used to generate and verify digital certificates to ensure authentication of communications and integrity of data.

Guess you like

Origin blog.csdn.net/abu935009066/article/details/131564646