RS256加密JWT生成、验证

原创 2018年04月16日 21:03:42

最近项目上由于集成需求,需要实现单点登录,经过考虑后选择了JWT,RS256公私玥加密方式实现,搜索后发现基于RS256的实现不太多,大多基于HS256对称加密,加密解密用同一SecretKey,泄漏后安全方面彻底崩坏,有些提到RS256的都是一些支离破碎的代码,没有什么参考价值。经过google,加上自己的整理,关键demo代码整理如下:

import junit.framework.TestCase;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.lang.JoseException;
import sun.security.util.DerInputStream;
import sun.security.util.DerValue;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.text.SimpleDateFormat;
import java.util.UUID;

public class LocalTest extends TestCase{

    public void testCreateToken() throws IOException {
        System.out.println(createToken());
    }

    public void testVerifyToken() throws Exception {
        String token = createToken();
        System.out.println(token);

        JwtClaims jwtClaims = verifyToken(token);
        System.out.println(jwtClaims.getClaimValue("name"));
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(jwtClaims.getIssuedAt().getValueInMillis()));
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(jwtClaims.getExpirationTime().getValueInMillis()));
    }

    /**
     * 生成jwt,SHA256加密
     * @return
     * @throws IOException
     */
    private String createToken() throws IOException {
        PrivateKey privateKey = getPrivateKey(getPrivateKeyString());
        final JwtClaims claims = new JwtClaims();
        claims.setClaim("name", "jack");
        claims.setSubject("a@a.com");
        claims.setAudience("test");//用于验证签名是否合法,验证方必须包含这些内容才验证通过
        claims.setExpirationTimeMinutesInTheFuture(60*24*30);
        claims.setIssuedAtToNow();

        // Generate the payload
        final JsonWebSignature jws = new JsonWebSignature();
        jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
        jws.setPayload(claims.toJson());
        jws.setKeyIdHeaderValue(UUID.randomUUID().toString());

        // Sign using the private key
        jws.setKey(privateKey);
        try {
            return jws.getCompactSerialization();
        } catch (JoseException e) {
            return null;
        }
    }

    /**
     * 验证jwt
     * @param token
     * @return
     * @throws Exception
     */
    private JwtClaims verifyToken(String token) throws Exception {

        try {
            PublicKey publicKey = getPublicKey(getPEMPublicKeyString());

            JwtConsumer jwtConsumer = new JwtConsumerBuilder()
                    .setRequireExpirationTime()
                    .setVerificationKey(publicKey)
                    .setExpectedAudience("test")//用于验证签名是否合法,可以设置多个,且可设置必须存在项,如果jwt中不包含这些内容则不通过
                    .build();

            return jwtConsumer.processToClaims(token);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private String getPrivateKeyString() throws IOException {
        //    生成方法:安装openssl,执行     openssl genrsa -out private.pem 2048
        return IOUtils.toString(new FileInputStream("/home/leen/.ssh/private.pem"));
    }

    private String getPEMPublicKeyString() throws IOException {
        //    导出公钥方法:生成私钥(private.pem)后,执行    openssl rsa -in private.pem -outform PEM -pubout -out public.pem
        return IOUtils.toString(new FileInputStream("/home/leen/.ssh/public.pem"));
    }

    /**
     * 获取PublicKey对象
     * @param publicKeyBase64
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    private PublicKey getPublicKey(String publicKeyBase64) throws NoSuchAlgorithmException, InvalidKeySpecException {
        String pem = publicKeyBase64
                .replaceAll("\\-*BEGIN.*KEY\\-*", "")
                .replaceAll("\\-*END.*KEY\\-*", "");
        java.security.Security.addProvider(
                new org.bouncycastle.jce.provider.BouncyCastleProvider()
        );
        X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(pem));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");

        PublicKey publicKey = keyFactory.generatePublic(pubKeySpec);
        System.out.println(publicKey);
        return publicKey;
    }

    /**
     * 获取PrivateKey对象
     * @param privateKeyBase64
     * @return
     */
    private PrivateKey getPrivateKey(String privateKeyBase64) {
        String privKeyPEM = privateKeyBase64
                .replaceAll("\\-*BEGIN.*KEY\\-*", "")
                .replaceAll("\\-*END.*KEY\\-*", "");

        // Base64 decode the data
        byte[] encoded = Base64.decodeBase64(privKeyPEM);

        try {
            DerInputStream derReader = new DerInputStream(encoded);
            DerValue[] seq = derReader.getSequence(0);

            if (seq.length < 9) {
                throw new GeneralSecurityException("Could not read private key");
            }

            // skip version seq[0];
            BigInteger modulus = seq[1].getBigInteger();
            BigInteger publicExp = seq[2].getBigInteger();
            BigInteger privateExp = seq[3].getBigInteger();
            BigInteger primeP = seq[4].getBigInteger();
            BigInteger primeQ = seq[5].getBigInteger();
            BigInteger expP = seq[6].getBigInteger();
            BigInteger expQ = seq[7].getBigInteger();
            BigInteger crtCoeff = seq[8].getBigInteger();

            RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(modulus, publicExp, privateExp,
                    primeP, primeQ, expP, expQ, crtCoeff);

            KeyFactory factory = KeyFactory.getInstance("RSA");
            return factory.generatePrivate(keySpec);
        } catch (IOException | GeneralSecurityException e) {
            e.printStackTrace();
        }
        return null;
    }
}

pom依赖:

<dependency>
            <groupId>org.bitbucket.b_c</groupId>
            <artifactId>jose4j</artifactId>
            <version>0.6.3</version>
        </dependency>


版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011411069/article/details/79966226

Spring+SpringMVC+Mybatis+shiro权限登录管理

最近公司要做一个新闻发布的系统,需要实现前台展示新闻页面,任何人都可以访问,后台需要权限登录,实现管理功能。 我使用Spring 4.3.6.RELEASE,mybatis 3.4.2版本,shiro...
  • qq_30781315
  • qq_30781315
  • 2017-05-05 11:48:58
  • 5085

MyBatis 基于注解的增删改查示例(Spring IOC注入)

代码: 数据模型层 package com.sunline.entity; import java.io.Serializable; public class User implements Se...
  • linhaiyun_ytdx
  • linhaiyun_ytdx
  • 2017-10-28 10:52:13
  • 120

JWT 进阶 -- JJWT

jwt是什么?JWTs是JSON对象的编码表示。JSON对象由零或多个名称/值对组成,其中名称为字符串,值为任意JSON值。JWT有助于在clear(例如在URL中)发送这样的信息,可以被信任为不可读...
  • change_on
  • change_on
  • 2017-07-28 20:59:23
  • 7594

golang 实现 json web token

json web token 简介 json web token 简称 jwt.他是一种轻量级的规范.这种规范允许客户端和服务端之间传递一些非敏感信息. 常用于用户认证和授权系统. jwt组成部分 ...
  • hzwy23
  • hzwy23
  • 2016-11-18 23:35:49
  • 5364

Codeforces 584 A. Olesya and Rodion 数论+找规律(构造)

#include #include #include #include #include #include using namespace std; int main(){ in...
  • xiaoli_nu
  • xiaoli_nu
  • 2016-08-21 20:28:34
  • 140

Java读取properties文件 【转】

Java读取properties文件 【转】 使用J2SE API读取Properties文件的六种方法 1。使用java.util.Properties类的load(...
  • ljw520204
  • ljw520204
  • 2011-08-30 18:18:04
  • 341

JWT——Token认证的两种实现和安全详解

前言: 最近因为项目中需要解决跨域取值的问题,所有考虑到用Token认证做技术支撑点,自己看了许多与之相关的文章,从中总结出了以下两个要点(签名和token时间)。在说这两个要点之前先大概简单说一下与...
  • buyaoshuohua1
  • buyaoshuohua1
  • 2017-06-26 14:07:11
  • 17179

基于jwt的token验证

JWT简介 JWT(json web token)是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。 JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身...
  • weixin_38568779
  • weixin_38568779
  • 2017-08-07 13:27:59
  • 887

用Apache POI来实现对Excel的读写

转自:http://www.lookhan.com/experience/experience/20110113211355.html   在我们的项目当中经常会遇到把数据导入...
  • headays
  • headays
  • 2012-02-03 15:51:06
  • 715

How to fix the dreaded "java.lang.OutOfMemoryError: PermGen space" exception (classloader leaks)

In the previous blog entry Classloader leaks: the dreaded "java.lang.OutOfMemoryError: PermGen spa...
  • wanglha
  • wanglha
  • 2014-10-17 10:58:42
  • 462
收藏助手
不良信息举报
您举报文章:RS256加密JWT生成、验证
举报原因:
原因补充:

(最多只允许输入30个字)