RS256加密JWT生成、验证

最近项目上由于集成需求,需要实现单点登录,经过考虑后选择了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,RS256(采用SHA-256 的 RSA 签名)加密
     * @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>

 

 

 

 
  • 9
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
实现JWTRS256加密算法需要使用PHP的openssl扩展,以下是一个简单的示例代码: ```php <?php // 生成JWT Token function generateToken($payload, $privateKey) { // JWT 头部 $header = array( "alg" => "RS256", "typ" => "JWT" ); // JWT 载荷 $payload = json_encode($payload); // JWT 签名 $headerEncoded = base64UrlEncode(json_encode($header)); $payloadEncoded = base64UrlEncode($payload); $data = $headerEncoded . '.' . $payloadEncoded; $signature = ''; openssl_sign($data, $signature, $privateKey, OPENSSL_ALGO_SHA256); $signatureEncoded = base64UrlEncode($signature); // JWT Token $jwtToken = $headerEncoded . '.' . $payloadEncoded . '.' . $signatureEncoded; return $jwtToken; } // 验证JWT Token function verifyToken($jwtToken, $publicKey) { $jwtParts = explode('.', $jwtToken); if (count($jwtParts) != 3) { return false; } $headerEncoded = $jwtParts[0]; $payloadEncoded = $jwtParts[1]; $signatureEncoded = $jwtParts[2]; $header = json_decode(base64UrlDecode($headerEncoded), true); if (!isset($header['alg']) || $header['alg'] !== 'RS256') { return false; } $payload = json_decode(base64UrlDecode($payloadEncoded), true); if (!$payload) { return false; } $data = $headerEncoded . '.' . $payloadEncoded; $signature = base64UrlDecode($signatureEncoded); // 验证签名 $verifyResult = openssl_verify($data, $signature, $publicKey, OPENSSL_ALGO_SHA256); if ($verifyResult === 1) { return true; } else { return false; } } // Base64 URL 编码 function base64UrlEncode($data) { $urlsafe = strtr(base64_encode($data), '+/', '-_'); return rtrim($urlsafe, '='); } // Base64 URL 解码 function base64UrlDecode($data) { $data = strtr($data, '-_', '+/'); $dataLen = strlen($data) % 4; if ($dataLen) { $data .= str_repeat('=', 4 - $dataLen); } return base64_decode($data); } // 读取私钥 $privateKey = openssl_pkey_get_private("file://path/to/private_key.pem"); // 读取公钥 $publicKey = openssl_pkey_get_public("file://path/to/public_key.pem"); // 生成Token $payload = array( "sub" => "1234567890", "name" => "John Doe", "iat" => 1516239022 ); $jwtToken = generateToken($payload, $privateKey); echo "JWT Token: " . $jwtToken . "\n"; // 验证Token $verifyResult = verifyToken($jwtToken, $publicKey); if ($verifyResult) { echo "Token 验证成功\n"; } else { echo "Token 验证失败\n"; } ``` 在以上示例代码中,`generateToken` 函数用于生成JWT Token,`verifyToken` 函数用于验证JWT Token。`base64UrlEncode` 和 `base64UrlDecode` 分别用于进行Base64 URL编码和解码。请注意,需要将私钥和公钥保存在文件中,并且需要指定正确的文件路径。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值