/* * (C) Copyright 2015-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * ohun@live.cn (夜色) */ package com.mpush.tools.crypto; import com.mpush.api.Constants; import com.mpush.tools.common.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import java.io.ByteArrayOutputStream; import java.math.BigInteger; import java.security.*; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; import java.security.spec.X509EncodedKeySpec; /** * RSA公钥/私钥/签名工具包 * 字符串格式的密钥在未在特殊说明情况下都为BASE64编码格式 * 由于非对称加密速度极其缓慢,一般文件不使用它来加密而是使用对称加密 * 非对称加密算法可以用来对对称加密的密钥加密,这样保证密钥的安全也就保证了数据的安全 */ public final class RSAUtils { private static final Logger LOGGER = LoggerFactory.getLogger(RSAUtils.class); /** * 密钥位数 */ public static final int RAS_KEY_SIZE = 1024; /** * 加密算法RSA */ public static final String KEY_ALGORITHM = "RSA"; /** * 填充方式 */ public static final String KEY_ALGORITHM_PADDING = "RSA/ECB/PKCS1Padding"; /** * 签名算法 */ public static final String SIGNATURE_ALGORITHM = "MD5withRSA"; /** * RSA最大解密密文大小 */ private static final int MAX_DECRYPT_BLOCK = 128; /** * RSA最大加密明文大小 */ private static final int MAX_ENCRYPT_BLOCK = 128 - 11; /** * 生成公钥和私钥 * * @param rsaKeySize key size * * @return 公钥和私钥 */ public static Pair<RSAPublicKey, RSAPrivateKey> genKeyPair(int rsaKeySize) { try { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM); keyPairGen.initialize(rsaKeySize); KeyPair keyPair = keyPairGen.generateKeyPair(); RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); return Pair.of(publicKey, privateKey); } catch (NoSuchAlgorithmException e) { LOGGER.error("getKeys ex ", e); } return null; } /** * 编码密钥,便于存储 * * @param key 密钥 * @return base64后的字符串 * @throws Exception Exception */ public static String encodeBase64(Key key) throws Exception { return Base64Utils.encode(key.getEncoded()); } /** * 从字符串解码私钥 * * @param key 密钥 * @return base64后的字符串 * @throws Exception Exception */ public static PrivateKey decodePrivateKey(String key) throws Exception { byte[] keyBytes = Base64Utils.decode(key); PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); return keyFactory.generatePrivate(pkcs8KeySpec); } /** * 从字符串解码公钥 * * @param publicKey 公钥 * @return 公钥 * @throws Exception Exception */ public static PublicKey decodePublicKey(String publicKey) throws Exception { byte[] keyBytes = Base64Utils.decode(publicKey); X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); return keyFactory.generatePublic(x509KeySpec); } /** * 用私钥对信息生成数字签名 * * @param data 已加密数据 * @param privateKey 私钥(BASE64编码) * @return 私钥 * @throws Exception Exception */ public static String sign(byte[] data, String privateKey) throws Exception { Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initSign(decodePrivateKey(privateKey)); signature.update(data); return Base64Utils.encode(signature.sign()); } /** * 校验数字签名 * * @param data 已加密数据 * @param publicKey 公钥(BASE64编码) * @param sign 数字签名 * @return 是否通过校验 * @throws Exception Exception */ public static boolean verify(byte[] data, String publicKey, String sign) throws Exception { Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initVerify(decodePublicKey(publicKey)); signature.update(data); return signature.verify(Base64Utils.decode(sign)); } /** * 使用模和指数生成RSA公钥 * 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding, * 不同JDK默认的补位方式可能不同,如Android默认是RSA * /None/NoPadding】 * * @param modulus 模 * @param exponent 指数 * @return 公钥 */ public static RSAPublicKey getPublicKey(String modulus, String exponent) { try { BigInteger b1 = new BigInteger(modulus); BigInteger b2 = new BigInteger(exponent); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2); return (RSAPublicKey) keyFactory.generatePublic(keySpec); } catch (Exception e) { LOGGER.error("getPublicKey ex modulus={}, exponent={}", modulus, exponent, e); throw new CryptoException("Get PublicKey ex", e); } } /** * 使用模和指数生成RSA私钥 * 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding, * 不同JDK默认的补位方式可能不同,如Android默认是RSA * /None/NoPadding】 * * @param modulus 模 * @param exponent 指数 * @return 私钥 */ public static RSAPrivateKey getPrivateKey(String modulus, String exponent) { try { BigInteger b1 = new BigInteger(modulus); BigInteger b2 = new BigInteger(exponent); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(b1, b2); return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); } catch (Exception e) { LOGGER.error("getPrivateKey ex modulus={}, exponent={}", modulus, exponent, e); throw new CryptoException("Get PrivateKey ex", e); } } /** * 公钥加密 * * @param data 待加密数据 * @param publicKey 公钥 * @return 加密后的值 */ public static byte[] encryptByPublicKey(byte[] data, RSAPublicKey publicKey) { try { Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_PADDING); cipher.init(Cipher.ENCRYPT_MODE, publicKey); // 模长 int key_len = publicKey.getModulus().bitLength() / 8; // 加密数据长度 <= 模长-11 //如果明文长度大于模长-11则要分组加密 return doFinal(cipher, data, key_len - 11); } catch (Exception e) { LOGGER.error("encryptByPublicKey ex", e); throw new CryptoException("RSA encrypt ex", e); } } /** * 私钥解密 * * @param data 待加密数据 * @param privateKey 私钥 * @return 解密后的值 */ public static byte[] decryptByPrivateKey(byte[] data, RSAPrivateKey privateKey) { try { Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_PADDING); cipher.init(Cipher.DECRYPT_MODE, privateKey); //模长 int key_len = privateKey.getModulus().bitLength() / 8; //如果密文长度大于模长则要分组解密 return doFinal(cipher, data, key_len); } catch (Exception e) { LOGGER.error("decryptByPrivateKey ex", e); throw new CryptoException("RSA decrypt ex", e); } } /** * 注意:RSA加密明文最大长度117字节, * 解密要求密文最大长度为128字节, * 所以在加密和解密的过程中需要分块进行。 * * @param cipher 密钥 * @param data 待处理的数据 * @return 处理后的值 * @throws BadPaddingException * @throws IllegalBlockSizeException */ private static byte[] doFinal(Cipher cipher, byte[] data, int key_len) throws BadPaddingException, IllegalBlockSizeException { int inputLen = data.length, offset = 0; byte[] tmp; ByteArrayOutputStream out = new ByteArrayOutputStream(getTmpArrayLength(inputLen)); while (inputLen > 0) { tmp = cipher.doFinal(data, offset, Math.min(key_len, inputLen)); out.write(tmp, 0, tmp.length); offset += key_len; inputLen -= key_len; } return out.toByteArray(); } private static int getTmpArrayLength(int L) { int S = MAX_DECRYPT_BLOCK; while (S < L) S <<= 1; return S; } /** * 私钥解密 * * @param data 已加密数据 * @param privateKey 私钥(BASE64编码) * @return 解密后的值 * @throws Exception Exception */ public static byte[] decryptByPrivateKey(byte[] data, String privateKey) throws Exception { PrivateKey key = decodePrivateKey(privateKey); Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_PADDING); cipher.init(Cipher.DECRYPT_MODE, key); return doFinal(cipher, data, MAX_DECRYPT_BLOCK); } /** * 公钥解密 * * @param data 已加密数据 * @param publicKey 公钥(BASE64编码) * @return 解密后的值 * @throws Exception Exception */ public static byte[] decryptByPublicKey(byte[] data, String publicKey) throws Exception { PublicKey key = decodePublicKey(publicKey); Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_PADDING); cipher.init(Cipher.DECRYPT_MODE, key); return doFinal(cipher, data, MAX_DECRYPT_BLOCK); } /** * 公钥加密 * * @param data 源数据 * @param publicKey 公钥(BASE64编码) * @return 加密后的值 * @throws Exception Exception */ public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception { PublicKey key = decodePublicKey(publicKey); // 对数据加密 Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_PADDING); cipher.init(Cipher.ENCRYPT_MODE, key); return doFinal(cipher, data, MAX_ENCRYPT_BLOCK); } /** * 私钥加密 * * @param data 源数据 * @param privateKey 私钥(BASE64编码) * @return 加密后的值 * @throws Exception Exception */ public static byte[] encryptByPrivateKey(byte[] data, String privateKey) throws Exception { PrivateKey key = decodePrivateKey(privateKey); Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_PADDING); cipher.init(Cipher.ENCRYPT_MODE, key); return doFinal(cipher, data, MAX_ENCRYPT_BLOCK); } private static void test() { Pair<RSAPublicKey, RSAPrivateKey> pair = RSAUtils.genKeyPair(RAS_KEY_SIZE); //生成公钥和私钥 RSAPublicKey publicKey = pair.key; RSAPrivateKey privateKey = pair.value; //模 String modulus = publicKey.getModulus().toString(); //公钥指数 String public_exponent = publicKey.getPublicExponent().toString(); //私钥指数 String private_exponent = privateKey.getPrivateExponent().toString(); //明文 byte[] ming = "123456789".getBytes(Constants.UTF_8); System.out.println("明文:" + new String(ming, Constants.UTF_8)); //使用模和指数生成公钥和私钥 RSAPrivateKey priKey = RSAUtils.getPrivateKey(modulus, private_exponent); RSAPublicKey pubKey = RSAUtils.getPublicKey(modulus, public_exponent); System.out.println("privateKey=" + priKey); System.out.println("publicKey=" + pubKey); //加密后的密文 byte[] mi = RSAUtils.encryptByPublicKey(ming, pubKey); System.out.println("密文:" + new String(mi, Constants.UTF_8)); //解密后的明文 ming = RSAUtils.decryptByPrivateKey(mi, priKey); System.out.println("解密:" + new String(ming, Constants.UTF_8)); } public static void main(String[] args) throws Exception { int keySize = RAS_KEY_SIZE; if (args.length > 0) keySize = Integer.parseInt(args[0]); if (keySize < RAS_KEY_SIZE) keySize = RAS_KEY_SIZE; Pair<RSAPublicKey, RSAPrivateKey> pair = RSAUtils.genKeyPair(keySize); //生成公钥和私钥 RSAPublicKey publicKey = pair.key; RSAPrivateKey privateKey = pair.value; System.out.println("key generate success!"); System.out.println("privateKey=" + RSAUtils.encodeBase64(privateKey)); System.out.println("publicKey=" + RSAUtils.encodeBase64(publicKey)); //明文 byte[] ming = "这是一段测试文字。。。。".getBytes(Constants.UTF_8); System.out.println("明文:" + new String(ming, Constants.UTF_8)); //加密后的密文 byte[] mi = RSAUtils.encryptByPublicKey(ming, publicKey); System.out.println("密文:" + new String(mi, Constants.UTF_8)); //解密后的明文 ming = RSAUtils.decryptByPrivateKey(mi, privateKey); System.out.println("解密:" + new String(ming, Constants.UTF_8)); } }