SHA256withRSA 完美工具类 RSAUtils,包含:
1、签名
2、验签
3、公钥加密
4、钥加密私
RSAUtils工具类:
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.security.InvalidKeyException; import java.security.Key; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.Signature; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; 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.util.Enumeration; import java.util.HashMap; import java.util.Map; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.ArrayUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class RSAUtils { private static final Logger logger = LoggerFactory.getLogger(RSAUtils.class); // MAX_DECRYPT_BLOCK应等于密钥长度/8(1byte=8bit),所以当密钥位数为2048时,最大解密长度应为256. // 128 对应 1024,256对应2048 private static final int KEYSIZE = 2048; // RSA最大加密明文大小 private static final int MAX_ENCRYPT_BLOCK = 117; // RSA最大解密密文大小 private static final int MAX_DECRYPT_BLOCK = 128; // 不仅可以使用DSA算法,同样也可以使用RSA算法做数字签名 private static final String KEY_ALGORITHM = "RSA"; private static final String SIGNATURE_ALGORITHM = "SHA256withRSA"; public static final String DEFAULT_SEED = "$%^*%^()(ED47d784sde78"; // 默认种子 public static final String PUBLIC_KEY = "PublicKey"; public static final String PRIVATE_KEY = "PrivateKey"; /** * * 生成密钥 * * @param seed 种子 * * @return 密钥对象 * @throws Exception * */ public static Map<String, Key> initKey(String seed) throws Exception { logger.info("生成密钥"); KeyPairGenerator keygen = KeyPairGenerator.getInstance(KEY_ALGORITHM); SecureRandom secureRandom = new SecureRandom(); // 如果指定seed,那么secureRandom结果是一样的,所以生成的公私钥也永远不会变 // secureRandom.setSeed(seed.getBytes()); // Modulus size must range from 512 to 1024 and be a multiple of 64 keygen.initialize(KEYSIZE, secureRandom); KeyPair keys = keygen.genKeyPair(); PrivateKey privateKey = keys.getPrivate(); PublicKey publicKey = keys.getPublic(); Map<String, Key> map = new HashMap<>(2); map.put(PUBLIC_KEY, publicKey); map.put(PRIVATE_KEY, privateKey); return map; } /** * * 生成默认密钥 * * * @return 密钥对象 * @throws Exception * */ public static Map<String, Key> initKey() throws Exception { return initKey(DEFAULT_SEED); } /** * * 取得私钥 * * @param keyMap * * @return * @throws Exception * */ public static String getPrivateKey(Map<String, Key> keyMap) throws Exception { Key key = (Key) keyMap.get(PRIVATE_KEY); return encryptBASE64(key.getEncoded()); // base64加密私钥 } /** * * 取得公钥 * * @param keyMap * * @return * @throws Exception * */ public static String getPublicKey(Map<String, Key> keyMap) throws Exception { Key key = (Key) keyMap.get(PUBLIC_KEY); return encryptBASE64(key.getEncoded()); // base64加密公钥 } /** * * 用私钥对信息进行数字签名 * * @param data 加密数据 * * @param privateKey 私钥-base64加密的 * * @return * * @throws Exception * */ public static String signByPrivateKey(byte[] data, String privateKey) throws Exception { logger.info("用私钥对信息进行数字签名"); byte[] keyBytes = decryptBASE64(privateKey); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM); PrivateKey priKey = factory.generatePrivate(keySpec);// 生成私钥 // 用私钥对信息进行数字签名 Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initSign(priKey); signature.update(data); return encryptBASE64(signature.sign()); } /** * * BASE64Encoder 加密 * * @param data 要加密的数据 * * @return 加密后的字符串 * */ private static String encryptBASE64(byte[] data) { // BASE64Encoder encoder = new BASE64Encoder(); // String encode = encoder.encode(data); // return encode; return new String(Base64.encodeBase64(data)); } private static byte[] decryptBASE64(String data) { // BASE64Decoder 每76个字符换行 // BASE64Decoder decoder = new BASE64Decoder(); // byte[] buffer = decoder.decodeBuffer(data); // return buffer; // codec 的 Base64 不换行 return Base64.decodeBase64(data); } public static boolean verifyByPublicKey(byte[] data, String publicKey, String sign) throws Exception { byte[] keyBytes = decryptBASE64(publicKey); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); PublicKey pubKey = keyFactory.generatePublic(keySpec); Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initVerify(pubKey); signature.update(data); return signature.verify(decryptBASE64(sign)); // 验证签名 } /** * RSA公钥加密 * * @param str 加密字符串 * @param publicKey 公钥 * @return 密文 * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException * @throws NoSuchPaddingException * @throws InvalidKeyException * @throws UnsupportedEncodingException * @throws BadPaddingException * @throws IllegalBlockSizeException * @throws Exception 加密过程中的异常信息 */ public static String encryptByPublicKey(String str, String publicKey) throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException { // base64编码的公钥 byte[] keyBytes = decryptBASE64(publicKey); RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(KEY_ALGORITHM) .generatePublic(new X509EncodedKeySpec(keyBytes)); // RSA加密 Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, pubKey); byte[] data = str.getBytes("UTF-8"); // 加密时超过117字节就报错。为此采用分段加密的办法来加密 byte[] enBytes = null; for (int i = 0; i < data.length; i += MAX_ENCRYPT_BLOCK) { // 注意要使用2的倍数,否则会出现加密后的内容再解密时为乱码 byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_ENCRYPT_BLOCK)); enBytes = ArrayUtils.addAll(enBytes, doFinal); } String outStr = encryptBASE64(enBytes); return outStr; } /** * RSA私钥加密 * * @param str 加密字符串 * @param privateKey 公钥 * @return 密文 * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException * @throws NoSuchPaddingException * @throws InvalidKeyException * @throws UnsupportedEncodingException * @throws BadPaddingException * @throws IllegalBlockSizeException * @throws Exception 加密过程中的异常信息 */ public static String encryptByPrivateKey(String str, String privateKey) throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException { // base64编码的公钥 byte[] keyBytes = decryptBASE64(privateKey); RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(KEY_ALGORITHM) .generatePrivate(new PKCS8EncodedKeySpec(keyBytes)); // RSA加密 Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, priKey); byte[] data = str.getBytes("UTF-8"); // 加密时超过117字节就报错。为此采用分段加密的办法来加密 byte[] enBytes = null; for (int i = 0; i < data.length; i += MAX_ENCRYPT_BLOCK) { // 注意要使用2的倍数,否则会出现加密后的内容再解密时为乱码 byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_ENCRYPT_BLOCK)); enBytes = ArrayUtils.addAll(enBytes, doFinal); } String outStr = encryptBASE64(enBytes); return outStr; } /** * 读取公钥 * * @param publicKeyPath * @return */ public static PublicKey readPublic(String publicKeyPath) { if (publicKeyPath != null) { try (FileInputStream bais = new FileInputStream(publicKeyPath)) { CertificateFactory certificatefactory = CertificateFactory.getInstance("X.509"); X509Certificate cert = (X509Certificate) certificatefactory.generateCertificate(bais); return cert.getPublicKey(); } catch (CertificateException e) { logger.error(e.getMessage(), e); } catch (FileNotFoundException e) { logger.error(e.getMessage(), e); } catch (IOException e) { logger.error(e.getMessage(), e); } } return null; } /** * 读取私钥 * * @param path * @return */ public static PrivateKey readPrivate(String privateKeyPath, String privateKeyPwd) { if (privateKeyPath == null || privateKeyPwd == null) { return null; } try (InputStream stream = new FileInputStream(new File(privateKeyPath));) { // 获取JKS 服务器私有证书的私钥,取得标准的JKS的 KeyStore实例 KeyStore store = KeyStore.getInstance("JKS");// JKS,二进制格式,同时包含证书和私钥,一般有密码保护;PKCS12,二进制格式,同时包含证书和私钥,一般有密码保护。 // jks文件密码,根据实际情况修改 store.load(stream, privateKeyPwd.toCharArray()); // 获取jks证书别名 Enumeration<String> en = store.aliases(); String pName = null; while (en.hasMoreElements()) { String n = (String) en.nextElement(); if (store.isKeyEntry(n)) { pName = n; } } // 获取证书的私钥 PrivateKey key = (PrivateKey) store.getKey(pName, privateKeyPwd.toCharArray()); return key; } catch (Exception e) { logger.error(e.getMessage(), e); } return null; } /** * RSA私钥解密 * * @param encryStr 加密字符串 * @param privateKey 私钥 * @return 铭文 * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException * @throws NoSuchPaddingException * @throws BadPaddingException * @throws IllegalBlockSizeException * @throws InvalidKeyException * @throws Exception 解密过程中的异常信息 */ public static String decryptByPrivateKey(String encryStr, String privateKey) throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException { // base64编码的私钥 byte[] decoded = decryptBASE64(privateKey); RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(KEY_ALGORITHM) .generatePrivate(new PKCS8EncodedKeySpec(decoded)); // RSA解密 Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, priKey); // 64位解码加密后的字符串 byte[] data = decryptBASE64(encryStr); // 解密时超过128字节报错。为此采用分段解密的办法来解密 StringBuilder sb = new StringBuilder(); for (int i = 0; i < data.length; i += MAX_DECRYPT_BLOCK) { byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_DECRYPT_BLOCK)); sb.append(new String(doFinal)); } return sb.toString(); } /** * RSA公钥解密 * * @param encryStr 加密字符串 * @param privateKey 私钥 * @return 铭文 * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException * @throws NoSuchPaddingException * @throws BadPaddingException * @throws IllegalBlockSizeException * @throws InvalidKeyException * @throws Exception 解密过程中的异常信息 */ public static String decryptByPublicKey(String encryStr, String publicKey) throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException { // base64编码的私钥 byte[] decoded = decryptBASE64(publicKey); RSAPublicKey priKey = (RSAPublicKey) KeyFactory.getInstance(KEY_ALGORITHM) .generatePublic(new X509EncodedKeySpec(decoded)); // RSA解密 Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, priKey); // 64位解码加密后的字符串 byte[] data = decryptBASE64(encryStr); // 解密时超过128字节报错。为此采用分段解密的办法来解密 StringBuilder sb = new StringBuilder(); for (int i = 0; i < data.length; i += MAX_DECRYPT_BLOCK) { byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_DECRYPT_BLOCK)); sb.append(new String(doFinal)); } return sb.toString(); } /** * main方法测试 第一种用法:公钥加密,私钥解密。---用于加解密 第二种用法:私钥签名,公钥验签。---用于签名 * * @param args * @throws Exception */ public static void main(String[] args) throws Exception { String ss = "hello"; byte[] data = ss.getBytes(); Map<String, Key> keyMap = initKey();// 构建密钥 PublicKey publicKey = (PublicKey) keyMap.get(PUBLIC_KEY); PrivateKey privateKey = (PrivateKey) keyMap.get(PRIVATE_KEY); logger.info("私钥format:{}", privateKey.getFormat()); logger.info("公钥format:{}", publicKey.getFormat()); logger.info("私钥string:{}", getPrivateKey(keyMap)); logger.info("公钥string:{}", getPublicKey(keyMap)); // 产生签名 String sign = signByPrivateKey(data, getPrivateKey(keyMap)); logger.info("签名sign={}", sign); // 验证签名 boolean verify1 = verifyByPublicKey(ss.getBytes(), getPublicKey(keyMap), sign); logger.info("经验证数据和签名匹配:{} ", verify1); boolean verify = verifyByPublicKey(data, getPublicKey(keyMap), sign); logger.error("经验证数据和签名匹配:{} ", verify); // logger.info("数字签名为"+sign); String s = "测试,e8986ae53e76e7514ebc7e8a42e81e6cea5b6280fb5d3259d5f0a46f9f6e090c"; String encryStr = encryptByPublicKey(s, getPublicKey(keyMap)); logger.info("字符串 {} 的公钥加密结果为:{}", s, encryStr); String decryStr = decryptByPrivateKey(encryStr, getPrivateKey(keyMap)); logger.info("私钥解密结果为:{}", decryStr); logger.info("========================================================================================"); String s2 = "测试222,e8986ae53e76e7514ebc7e8a42e81e6cea5b6280fb5d3259d5f0a46f9f6e090c"; String encryStr2 = encryptByPrivateKey(s, getPrivateKey(keyMap)); logger.info("字符串 {} 的私钥加密结果为:{}", s2, encryStr2); String decryStr2 = decryptByPublicKey(encryStr2, getPublicKey(keyMap)); logger.info("公钥解密结果为:{}", decryStr2); } }
当我在使用springcloud的oauth2时,经常会用到jwt作为用户身份的确认和鉴权。而我们知道jwt是由三部分组成,其中第三部分就是数字签名了,而springcloud的oauth2中的默认jwt签名算法为SHA256withRSA。
下面通过调用OauthServer的{/oauth/token_key}接口来看一下返回情况:
HMAC与SHA256withRSA
hmac做签名时需要指定一个secret和指定hamc算法,常见的hmac算法有hamcsha1、hamcsha256等,通常用hmac生成信息摘要后会再用RSA算法对其进行加密
SHA256withRSA做签名则一步到位,需要先生成RSA密钥对,其中私钥用于签名,公钥用于验签。
为了方便理解:
hmac数字签名 = rsa_encrypt(hmac(信息) + RSA私钥)
SHA256withRSA数字签名 = SHA256withRSA_encrypt(信息 + RSA私钥)
RSA密钥之PKCS#1、PKCS#8
pkcs时密钥的语法标准,细心的同学应该会经常遇到PKCS#1,PKCS#8,PKCS#12等标准,具体所代表的含义这里不做赘述了。通常我们生成密钥对时,会对私钥进行加密,加密后的格式为PKCS#1或PKCS#8格式,而一般不会对公钥进行加密。对于开发者而言,JAVA中通常使用的是PKCS8格式的私钥,php、.net使用的是PKCS1格式的私钥。简单例子: 用openssl生成PKCS#1格式的私钥:openssl genrsa -des3 -out test.key 1024
通过文件首尾部能判断:
JAVA示例
1.签名示例
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openssl.PEMWriter; import org.bouncycastle.util.encoders.Base64; import java.io.IOException; import java.io.StringWriter; import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.Security; import java.security.Signature; import java.security.SignatureException; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.util.HashMap; import java.util.Map; public class SignTest { /** * 生成没有加密的密钥对 * @param algorithm * @param keysize * @return * @throws Exception */ public static Map<String, Object> createKey(String algorithm,int keysize) throws Exception { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(algorithm); keyPairGen.initialize(keysize); KeyPair keyPair = keyPairGen.generateKeyPair(); //通过对象 KeyPair 获取RSA公私钥对象RSAPublicKey RSAPrivateKey RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); //公私钥对象存入map中 Map<String, Object> keyMap = new HashMap<String, Object>(2); keyMap.put("publicKey", publicKey); keyMap.put("privateKey", privateKey); return keyMap; } /** * 生成加过密的密钥对 pkcs#1格式私钥 pkcs#8格式公钥 * * @param algorithm * @param keysize * @param privateKeyPwd * @return * @throws Exception */ public static Map<String, Object> createEncryKeyStr(String algorithm, int keysize, String privateKeyPwd) throws Exception { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(algorithm); keyPairGen.initialize(keysize); KeyPair keyPair = keyPairGen.generateKeyPair(); //通过对象 KeyPair 获取RSA公私钥对象RSAPublicKey RSAPrivateKey RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); String pubKeyStr = new String(Base64.encode(publicKey.getEncoded())); //pkcs8格式 String priKeyStr = new String(Base64.encode(privateKey.getEncoded())); //pkcs8格式 System.out.println(priKeyStr);//从输出结果可以知道是PKCS#8格式的 //私钥加密 String privateKeyStr = privateKeyPwdToPKCS1(privateKey, privateKeyPwd);//使用BC加密私钥格式会被转为PKSC#1格式 System.out.println(privateKeyStr); System.out.println(privateKeyNoPwdToPKCS1(privateKey)); //公私钥对象存入map中 Map<String, Object> keyMap = new HashMap<String, Object>(2); keyMap.put("publicKeyStr", pubKeyStr); keyMap.put("privateKeyStr", privateKeyStr); return keyMap; } /** * 将私钥转为PKCS#1格式私钥(加密) * * @param privateKey * @param filePasswd * @return */ private static String privateKeyPwdToPKCS1(PrivateKey privateKey, String filePasswd) { Security.addProvider(new BouncyCastleProvider()); StringWriter sw = new StringWriter(); PEMWriter writer = new PEMWriter(sw); try { writer.writeObject(privateKey, "DESEDE", filePasswd.toCharArray(), new SecureRandom()); } catch (IOException e) { e.printStackTrace(); } finally { try { if (writer != null) { writer.close(); } } catch (IOException e) { e.printStackTrace(); } } return sw.toString(); } /** * 将私钥转为PKCS#1格式私钥(没有加密) * @param privateKey * @return */ private static String privateKeyNoPwdToPKCS1(PrivateKey privateKey){ Security.addProvider(new BouncyCastleProvider()); StringWriter sw = new StringWriter(); PEMWriter writer = new PEMWriter(sw); try { writer.writeObject(privateKey); } catch (IOException e) { e.printStackTrace(); }finally{ try { if(writer !=null){ writer.close(); } } catch (IOException e) { e.printStackTrace(); } } return sw.toString(); } public static final String SIGNATURE_ALGORITHM = "SHA256withRSA"; public static final String ENCODE_ALGORITHM = "SHA-256"; public static final String PLAIN_TEXT = "test string"; /** * 签名 * @param privateKey 私钥 * @param plainText 明文 * @return */ public static byte[] sign(PrivateKey privateKey, String plainText) { MessageDigest messageDigest; byte[] signed = null; try { messageDigest = MessageDigest.getInstance(ENCODE_ALGORITHM); messageDigest.update(plainText.getBytes()); byte[] outputDigest_sign = messageDigest.digest(); System.out.println("SHA-256编码后-----》" + org.apache.commons.codec.binary.Base64.encodeBase64String(outputDigest_sign)); Signature Sign = Signature.getInstance(SIGNATURE_ALGORITHM); Sign.initSign(privateKey); Sign.update(outputDigest_sign); signed = Sign.sign(); System.out.println("SHA256withRSA签名后-----》" + org.apache.commons.codec.binary.Base64.encodeBase64String(signed)); } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) { e.printStackTrace(); } return signed; } /** * 验签 * @param publicKey 公钥 * @param plain_text 明文 * @param signed 签名 */ public static boolean verifySign(PublicKey publicKey, String plain_text, byte[] signed) { MessageDigest messageDigest; boolean SignedSuccess=false; try { messageDigest = MessageDigest.getInstance(ENCODE_ALGORITHM); messageDigest.update(plain_text.getBytes()); byte[] outputDigest_verify = messageDigest.digest(); //System.out.println("SHA-256加密后-----》" +bytesToHexString(outputDigest_verify)); Signature verifySign = Signature.getInstance(SIGNATURE_ALGORITHM); verifySign.initVerify(publicKey); verifySign.update(outputDigest_verify); SignedSuccess = verifySign.verify(signed); System.out.println("验证成功?---" + SignedSuccess); } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) { e.printStackTrace(); } return SignedSuccess; } /** * bytes[]换成16进制字符串 * * @param src * @return */ public static String bytesToHexString(byte[] src) { StringBuilder stringBuilder = new StringBuilder(""); if (src == null || src.length <= 0) { return null; } for (int i = 0; i < src.length; i++) { int v = src[i] & 0xFF; String hv = Integer.toHexString(v); if (hv.length() < 2) { stringBuilder.append(0); } stringBuilder.append(hv); } return stringBuilder.toString(); } public static void main(String[] args) throws Exception { Map<String, Object> keyMap = createKey("RSA", 2048); PublicKey publicKey = (PublicKey) keyMap.get("publicKey"); PrivateKey privateKey = (PrivateKey) keyMap.get("privateKey"); byte[] signBytes = sign(privateKey, PLAIN_TEXT); System.out.println(verifySign(publicKey, PLAIN_TEXT, signBytes)); } }
2.签发JWT示例
import com.cyq.CheckResult; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.SignatureException; import org.bouncycastle.util.encoders.Base64; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.security.Key; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.time.OffsetDateTime; import java.time.ZoneId; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * maven依赖 * * <dependency> * <groupId>io.jsonwebtoken</groupId> * <artifactId>jjwt</artifactId> * <version>0.9.0</version> * </dependency> */ /** * @author cyq */ public class JwtUtils { public static void main(String[] args) throws Exception { // 签发jwt(签名算法hmac256) System.out.println("签发jwt(签名算法hmac256):"); String jwt = createJWT("123", "jwt_hmac256", 60); System.out.println(jwt); // 解析 SecretKey secretKey = generalKey(); Claims claims = parseJWT(jwt, secretKey); System.out.println(claims); // 签发jwt(签名算法SHA256withRSA) System.out.println("\n签发jwt(签名算法SHA256withRSA):"); Map<String, Object> keyMaps = createRSAKey(2048); String jwt2 = createJWT2("456", "jwt_SHA256withRSA", 60, (RSAPrivateKey) keyMaps.get("privateKey")); System.out.println(jwt2); // 解析 Claims claims2 = parseJWT(jwt2, (RSAPublicKey) keyMaps.get("publicKey")); System.out.println(claims2); } /** * 签发JWT * @param id * @param subject 可以是JSON数据 尽可能少 * @param ttlMillis * @return String * */ public static String createJWT(String id, String subject, long ttlMillis) { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;// HmacSha256 OffsetDateTime now = OffsetDateTime.now(ZoneId.of("Asia/Shanghai")); //Date now = new Date(nowMillis); SecretKey secretKey = generalKey(); JwtBuilder builder = Jwts.builder() .setId(id) .setSubject(subject) // 主题 .setIssuer("user") // 签发者 .setIssuedAt(Date.from(now.toInstant())) // 签发时间 .signWith(signatureAlgorithm, secretKey); // 签名算法以及密匙 if (ttlMillis >= 0) { OffsetDateTime expTime = now.plusSeconds(ttlMillis); builder.setExpiration(Date.from(expTime.toInstant())); // 过期时间 } return builder.compact(); } /** * 签发JWT * @param id * @param subject 可以是JSON数据 尽可能少 * @param ttlMillis * @return String * */ public static String createJWT2(String id, String subject, long ttlMillis, RSAPrivateKey privateKey) { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.RS256;// SHA256withRSA OffsetDateTime now = OffsetDateTime.now(ZoneId.of("Asia/Shanghai")); JwtBuilder builder = Jwts.builder() .setId(id) .setSubject(subject) // 主题 .setIssuer("user") // 签发者 .setIssuedAt(Date.from(now.toInstant())) // 签发时间 .signWith(signatureAlgorithm, privateKey); // 签名算法以及密匙 if (ttlMillis >= 0) { OffsetDateTime expTime = now.plusSeconds(ttlMillis); builder.setExpiration(Date.from(expTime.toInstant())); // 过期时间 } return builder.compact(); } /** * 生成没有加密的密钥对 * @param keysize * @return * @throws Exception */ public static Map<String, Object> createRSAKey(int keysize) throws Exception { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("rsa"); keyPairGen.initialize(keysize); KeyPair keyPair = keyPairGen.generateKeyPair(); //通过对象 KeyPair 获取RSA公私钥对象RSAPublicKey RSAPrivateKey RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); //公私钥对象存入map中 Map<String, Object> keyMap = new HashMap<String, Object>(2); keyMap.put("publicKey", publicKey); keyMap.put("privateKey", privateKey); return keyMap; } public static SecretKey generalKey() { byte[] encodedKey = Base64.decode("testjwt"); SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES"); return key; } /** * * 解析JWT字符串 * @param jwt * @return * @throws Exception */ public static Claims parseJWT(String jwt, Key key) throws Exception { return Jwts.parser() .setSigningKey(key) .parseClaimsJws(jwt) .getBody(); } /** * 验证JWT * @param jwtStr * @return */ public static CheckResult validateJWT(String jwtStr, Key key) { CheckResult checkResult = new CheckResult(); Claims claims = null; try { claims = parseJWT(jwtStr, key); checkResult.setSuccess(true); checkResult.setClaims(claims); } catch (ExpiredJwtException e) { e.printStackTrace(); checkResult.setErrCode("JWT_ERRCODE_EXPIRE"); checkResult.setSuccess(false); } catch (SignatureException e) { e.printStackTrace(); checkResult.setErrCode("JWT_ERRCODE_FAIL"); checkResult.setSuccess(false); } catch (Exception e) { e.printStackTrace(); checkResult.setErrCode("JWT_ERRCODE_FAIL"); checkResult.setSuccess(false); } return checkResult; } }