如何使用 RSA 加密 JWT

  1. 引入 nimbus-jose-jwt

<dependency>
      <groupId>com.nimbusds</groupId>
      <artifactId>nimbus-jose-jwt</artifactId>
      <version>9.23</version>
</dependency>

该包可以使用rsa算法进行jwt加密

2. 使用openssl生成密钥

生成RSA加密私钥

openssl genrsa -aes256 -passout pass:123456 -out rsa_aes_private.key 1024

使用RSA私钥生成公钥

openssl rsa -in rsa_aes_private.key -pubout -out rsa_public.key

因为使用 openssl 生成的密钥是 pkcs1格式的密钥,java默认只能使用 pkcs8 格式的密钥,所以需要进行pkcs1到pkcs8转换的转换

openssl pkcs8 -topk8 -in rsa_aes_private.key -inform pem -out pkcs8_rsa_private.key -outform pem

3. 在application.yml增加配置

certificate:
  useKid: k1
  certificates:
    - kid: k1
      privateKey: |
        -----BEGIN ENCRYPTED PRIVATE KEY-----
        MIIC3TBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQImUIM57O4TH4CAggA
        MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBDJkNNh8w3fTcQjKP3A6oVHBIIC
        gC7Nuk2xzW2+CHycQ5InCB76u/C1L6jTKC8M7XgAhacM7WfQHHfJFjMsN9J94vwd
        8rDlTPE+nNHmLw386fBFtwDTLC8cuALmcvzH+qxYVXD5ygYGRrclUulOiRwiZ5f4
        TjdmHApP15SbglG/B4tV5ERa2nudccXDdg7fAJsqlaZsqLGnPxYBhbUwE428DFjn
        MkyA2N06AQzyU7aFYeuKGSS5D04HRAyZ/SBVUg4lBXI34TAZGG447LhHxXuorBgH
        N/JJpHGgQyURmH43HI4bpiPnXHbHTRNYUehQGUI/oNWAZugFLFrXnYl120+wkca+
        U8zQu/23uhy+4iCuy5SnNxdOKvSNpBTIh2BEbEm8nmHvbcfg5pcgExb/g7rnFWPP
        ryNdR42Vm5Wp4xrzFT71WwWSUVkC1N037QH0K09BTcJi/XV6qxxOtLSfq2uzTJQ2
        vIs9VGgKy9IAlIa6aur1Th/cpbQ+dz9ld1ZYWHgBxw8hFxZkbu+qZUeAo6c1pHZI
        rwPvYj06BK5R2xkrMYcJaEasJz0PrvxMzk9+0qSJNdT+y9nzaxLN+/ypldm3DarH
        ZiyG5QC/TJTWkckM0AIdZujLIs8j3IQc4Sp21zrjFeMBzVd3CJBGgaFAV3o6CaAp
        9OJYytj/cNAy1jEfTl7AbaRAbteBbSFQdAsSGqgC0u+JpyncH1r3YoM720HIB7Xa
        pLyOCA3zWcbKPwHTBlH1x7+ppXy/zvdAwmUlTydD1aaa2i4bv2+ctdjWhMW77Nxp
        TE3y5Kim8CSW885PgIRxKocU0DgeOEtPmuOxxMjbouSF18mSmZP8NmoiVMpf/cS9
        9c4FlRjxWiPoRY+EMWk81cU=
        -----END ENCRYPTED PRIVATE KEY-----  
      publicKey: |
        -----BEGIN PUBLIC KEY-----
        MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1iRRYvvbs7cPGfpJuND0ArkzE
        MTzeDG0OuTxcdl+A6yOB6jRRe/9fqRRZJSZOcrvr8I2YKs9n+DGo2nGUrmcRlav5
        s77g7jGJmOW6ZvNBBU4g+w9gt4tQPCBcyjSym4HUWz04FT03cfa6Yn7f2xHwjw7j
        3DXGZnp+qx3fox5ezwIDAQAB
        -----END PUBLIC KEY-----
    - kid: k2
      privateKey: |
        -----BEGIN ENCRYPTED PRIVATE KEY-----
        MIIC3TBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQI1wWdC35fHiMCAggA
        MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBAd8fAg67XxjRww/6zZOYiUBIIC
        gFELh2xUblnGRTGQQ7VaRayAMHd1hNNc2P6Pe0ujXxzf1qF/iE6Rhpk2Ag9I0ky+
        siZL8lbh3QzOyQUch+BBYBuUeToSN8jQ+5rTR6Vr8eaC6jRKIZCBdxbbN4r3hSkJ
        nG+BZzjArO8U0T+QTQ3cp3rpvOsRj5xR09nku3HnaY9vNqWAWd5mjJ+b8lRnh64X
        qAAmnBUcB+5xuU/BMGpF8k2X/qNVRM/YZzP1/mWO31kw1VMjSP6zdrRVQuoUv2X8
        bUlv6kBBaaTPK4qkR6y3I7QMiGxA1UL8qTPtxpOlaWR+3ofIBPk1N35k1sFLRgc/
        UhK7q0KphZ75BZGu1PpOu8T0p2fx0BnIQUZKg7+g5oqzNKKo9RlVx87wrJO1Urur
        VJS+FzK1HoGqLtsco2lzeLqmXqCbC+MXygenJJxOZOKW9/LWQxMP3e72/N0LzdqW
        0lbu9f4w86OL7Qwk4zVxCFp9bwDAvf5ZIvfnLI1yl8q7cfny6QKGU+nwENOWn3Px
        I7Dv7vNIs9K6f6Is8XPnEnBIRi2eUwVHVqeu46DLIIzS+YIvsDfkHp3h7fBh7hYW
        Iytia/QfKmpyeZp5GCJZM0pLP0qDLspXdm0oBI+WdnbF2i9YUADGdQw1CpgskYnf
        wBNDdGmkUR4aTDdwvdzPacRDF3ZZg/AaiysuWRIjEsEabwmpi4CmmLiwxwnO5uDn
        4iiLC16PUPK+sIBskYd9UgOMyC+qKbzajCZVyRZDpNPZF+jZE+ND3TOtaWHimP9M
        B3dHj6F+/rHHko9kWsc/V/RaXm//14g8SBn3Hc+vR/IAz9SajMJDRVEmQnWhkfz9
        IdLgYdHew8l+HuvjDCXtmBM=
        -----END ENCRYPTED PRIVATE KEY-----
      publicKey: |
        -----BEGIN PUBLIC KEY-----
        MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6pyUTOpppX5JOtMbT6CjS4/U8
        ZV6Tw9kViTwrILr/AwgUFaaxptaCx+waiQgBJfTE2VVXwXipXpxfYjtgKqVqx4yR
        mVAxGNZjKIkSzAxjo7er2vP084WG/Sh958MXW8A/K7pDkSALusP8YTveEgtTKfln
        feBZh04XQmRYhPPCuQIDAQAB
        -----END PUBLIC KEY-----

以上配置的私钥是进行了转换后的密钥

4. 解析配置文件

定义配置对象

package com.olive.jwt;
import java.io.Serializable;


public class CertVO implements Serializable{


  private String kid;
  
  private String privateKey;
  
  private String publicKey;


  //省略getter setter
  
}

对应配置对象

package com.olive.jwt;
import java.util.List;


import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;


@Component
@ConfigurationProperties(prefix="certificate")
public class CertificateConfig {


  private String useKid;
  
  private List<CertVO> certificates;


  //省略getter  setter
}

5. 定义 jwt payload 对象

package com.olive.jwt;


import java.io.Serializable;


public class JwtPayloadVO implements Serializable{


  private String jti; //jwt token id
  
  private String tid; //companyId
  
  private String cid; //appId
  
  private String iss; //token使用方
  
  private String sub; //token主题 格式 tid:cid:uid
  
  private Long exp; //过期时间 毫秒
  
  private Long iat; //创建时间 毫秒
  
  private String uid; //user id


  //省略getter setter
}

6. 进行jwt 生成与验证

package com.olive.jwt;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.text.ParseException;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.nimbusds.jose.*;


@Component
public class CertificateProvider {


  @Autowired
  private CertificateConfig certificateConfig;


  private RSAPrivateKey rsaPrivateKey = null;


  private Map<String, RSASSAVerifier> verifiers = new HashMap<>();


  @PostConstruct
  public void init() {
    rsaPrivateKey = this.getPrivateKey();
  }


  public RSAPrivateKey getPrivateKey() {
    if (rsaPrivateKey != null) {
      return rsaPrivateKey;
    }
    String use = certificateConfig.getUseKid();
    if (StringUtils.isEmpty(use)) {
      System.out.println("certificate kid is required");
      return null;
    }
    List<CertVO> certVOs = certificateConfig.getCertificates();
    if (certVOs==null && certVOs.size()==0) {
      System.out.println("certificate is required");
      return null;
    }
    try {
      for (CertVO certVO : certVOs) {
        if (use.equals(certVO.getKid())) {
          // 加载私钥
          rsaPrivateKey = this.loadRSARSAPrivateKey(certVO.getPrivateKey());
        }
        RSAPublicKey publicKey = loadRSAPublicKey(certVO.getPublicKey());
        verifiers.put(certVO.getKid(), new RSASSAVerifier(publicKey));
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    if (rsaPrivateKey != null) {
      return rsaPrivateKey;
    } else {
      System.out.println("getPrivateKey certificate kid is required,certificate kid is required");
      return null;
    }
  }


  /**
   * 加载公钥
   *
   * @param keyStr 公钥字符串
   * @return 公钥实体
   * @throws NoSuchAlgorithmException  KeyFactory中无该算法实现
   * @throws InvalidKeySpecException 密钥无法识别
   */
  private RSAPublicKey loadRSAPublicKey(String keyStr) throws NoSuchAlgorithmException, InvalidKeySpecException {
    byte[] clear = publicKeyStrToBytes(keyStr);
    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(clear);
    KeyFactory fact = KeyFactory.getInstance("RSA");
    return (RSAPublicKey) fact.generatePublic(keySpec);
  }


  private RSAPrivateKey loadRSARSAPrivateKey(String keyStr) throws Exception {
    String begin = "-----BEGIN PRIVATE KEY-----";
    String end = "-----END PRIVATE KEY-----";
    String key = keyStr.replace(begin, "").replace(end, "").replaceAll("\\s", "");
    PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
    KeyFactory kf = KeyFactory.getInstance("RSA");
    return (RSAPrivateKey) kf.generatePrivate(spec);
  }
  /**
   * 公钥 字符串转换成二进制
   * @param keyStr 密钥字符串
   * @return 密钥/公钥 二进制
   */
  private byte[] publicKeyStrToBytes(String keyStr) {
    String begin = "-----BEGIN PUBLIC KEY-----";
    String end = "-----END PUBLIC KEY-----";
    String key = keyStr.replace(begin, "").replace(end, "").replaceAll("\\s", "");
    return Base64.getDecoder().decode(key);
  }


  public String generateAccessToken(JwtPayloadVO jwtPayloadVO) {
    Map<String, Object> playloadMap = new HashMap<>();
    playloadMap.put("jti", jwtPayloadVO.getJti());
    playloadMap.put("tid", jwtPayloadVO.getTid());
    playloadMap.put("cid", jwtPayloadVO.getCid());
    playloadMap.put("iss", jwtPayloadVO.getIss());
    playloadMap.put("sub", jwtPayloadVO.getSub());
    playloadMap.put("exp", jwtPayloadVO.getExp());
    playloadMap.put("iat", jwtPayloadVO.getIat());
    if (StringUtils.hasLength(jwtPayloadVO.getUid())) {
      playloadMap.put("uid", jwtPayloadVO.getUid());
    }
    try {
      // 创建JWS头,设置签名算法和类型
      JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(certificateConfig.getUseKid())
          .type(JOSEObjectType.JWT).build();
      JWTClaimsSet claimsSet = JWTClaimsSet.parse(playloadMap);
      // 创建RSA签名器
      JWSSigner signer = new RSASSASigner(rsaPrivateKey, true);
      SignedJWT signedJWT = new SignedJWT(header, claimsSet);
      signedJWT.sign(signer);
      return signedJWT.serialize();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }


  public JWTClaimsSet verify(String token) throws ParseException, JOSEException {
    SignedJWT jwt = SignedJWT.parse(token);
    JWSHeader jwtHeader = jwt.getHeader();
    String keyID = jwtHeader.getKeyID();
    RSASSAVerifier verifier = verifiers.get(keyID);
    if (verifier == null) {
      System.out.println("jwt verify error: kid " + keyID + " mismatch RSASSAVerifier");
      return null;
    }
    boolean verify = jwt.verify(verifier);
    if (!verify) {
      System.out.println("jwt verify fail, invalid token: " + token);
      return null;
    }
    return jwt.getJWTClaimsSet();
  }


}

记得点「」和「在看」↓

爱你们

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
flask_jwt_extended是一个用于Flask应用程序的JSON Web Token (JWT)扩展,它可以帮助你轻松地保护你的API。下面是一个使用RSA非对称加密的示例: 1. 安装所需的依赖 ``` pip install flask flask_jwt_extended cryptography ``` 2. 生成RSA密钥对 ``` from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.primitives import serialization private_key = rsa.generate_private_key( public_exponent=65537, key_size=2048, ) public_key = private_key.public_key() # 保存私钥 with open('private_key.pem', 'wb') as f: f.write(private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption() )) # 保存公钥 with open('public_key.pem', 'wb') as f: f.write(public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo )) ``` 3. 使用RSA密钥对进行JWT签名和验证 ``` from flask import Flask, jsonify from flask_jwt_extended import JWTManager, create_access_token, jwt_required from cryptography.hazmat.primitives import serialization from cryptography.hazmat.backends import default_backend app = Flask(__name__) app.config['JWT_SECRET_KEY'] = 'super-secret' # 这个密钥不用于签名,只用于加密 app.config['JWT_ALGORITHM'] = 'RS256' jwt = JWTManager(app) # 加载RSA密钥 with open('private_key.pem', 'rb') as f: private_key = serialization.load_pem_private_key( f.read(), password=None, backend=default_backend() ) with open('public_key.pem', 'rb') as f: public_key = serialization.load_pem_public_key( f.read(), backend=default_backend() ) @app.route('/login') def login(): access_token = create_access_token( identity='user_id', algorithm='RS256', private_key=private_key ) return jsonify(access_token=access_token) @app.route('/protected') @jwt_required(algorithms=['RS256'], public_key=public_key) def protected(): return jsonify({'message': 'protected'}) if __name__ == '__main__': app.run() ``` 在这个示例中,我们使用RSA非对称加密算法来对JWT进行签名和验证。在生成JWT时,我们使用私钥来签名,而在验证JWT时,我们使用公钥来验证签名。使用RSA非对称加密算法可以更安全地保护JWT

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BUG弄潮儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值