Java 中常用的 非对称加密算法 的使用

前言

体能状态先于精神状态,习惯先于决心,聚焦先于喜好。

非对称加密

非对称加密一般需要算法支持一对公私钥的相互加解密,即公钥加密私钥可以机密,私钥加密,公钥可以解密。
一般私钥不会对外公开,公钥会对外公开。
由于非对称加密的解密过程需要更多的时间,所以一般仅用于少量数据的加密——延伸来说,可以用于验签。

KeyPairGenerator

JDK 的 java.security.KeyPairGenerator 为我们提供了生成 公私钥的方法

JDK 支持的非对称加密算法和私钥长度

在 java.security.KeyPairGenerator 的注释中可以看到,JDK 规定,所有的JAVA 平台必须支持下面的算法和秘钥长度
秘钥长度只是一个目标长度,实际产生的秘钥长度一般是近似值——比如1024,则实际的可能是1023,1218
显然,秘钥长度越长,公私钥对长生的时间也越长,加解密也需更多时间

 * <li>{@code DiffieHellman} (1024)</li>
 * <li>{@code DSA} (1024)</li>
 * <li>{@code RSA} (1024, 2048)</li>
公钥和私钥的形式

公钥和私钥都被称为秘钥
公钥和私钥本质就是一个随机的字节数组,其展示形式一般会转化为 Base64 字符串,也是存储的形式。
由于在实际使用中我们并不是以字符串形式直接使用的,所有就涉及到存储形式和使用形式之间的转化。

公钥和私钥的类和相关方法

JDK 分别为 公钥和私钥提供了Java类
java.security.PublicKey 和 java.security.PrivateKey
二者都有三个属性 秘钥的加密方法名称 Algorithm、秘钥的字节数组Encoded、秘钥的规范标准 Format

  • getAlgorithm():返回该秘钥相关的算法的标准名称,比如RSA\DSA\DiffieHellman
  • getFormat():返回秘钥私有的编码字节码的名称,如果不支持编码,则返回null。
    该编码名称需要得到 ASN.1 的认证,比如对于公钥其编码名称叫做
    SubjectPublicKeyInfo,其定义位于 X.509 标准,公钥的该值就返回 X.509
    类似的,对于私钥,其 ASN.1 对应编码名称为 PrivateKeyInfo,对应的句法规范为
    PKCS #8 ,对于私钥,该属性返回 PKCS#8
  • getEncoded():返回满足 getFormat()编码标准的字节码数组,如果 getFormat()不允许编码,则这里返回null
    public String getAlgorithm();
    public String getFormat();
    public byte[] getEncoded();

公私钥的生成、保存和恢复

非对称加密算法需要生成公钥和私钥,生成时需要指定算法和期望的私钥长度。

生成公私钥对

上文已经说到,非对称加密的公私钥对的生成有几种 RSA、DSA、DiffieHellman,并且可以指定私钥的长度——尽管生成值是一个指定长度的近似值。
下面的例子提供了两种方法,第一种方法直接返回Base64字符串格式的 公私钥,第二种方法则返回公私钥的默认格式

/**
     * 生成 非对称加密算法 公私钥对的方法-返回默认秘钥格式
     * @param algorithm 加密算法
     * @param keySize   私钥长度-实际会生成一个近似值
     * <li>{@code DiffieHellman} (1024)</li>
     * <li>{@code DSA} (1024)</li>
     * <li>{@code RSA} (1024, 2048)</li>
     * @return
     * @throws NoSuchAlgorithmException
     */
    private static Map<Class,Object> initKeyPair(String algorithm,int keySize) throws NoSuchAlgorithmException {
        //指定算法 RSA、DiffieHellman、DSA
        KeyPairGenerator keyPairGenerator=KeyPairGenerator.getInstance(algorithm);
        //指定私钥的长度——实际生成的也是近似值
        keyPairGenerator.initialize(keySize);
        //生成公私钥对
        KeyPair kPair=keyPairGenerator.generateKeyPair();
        PrivateKey privateKey=kPair.getPrivate();
        PublicKey publicKey=kPair.getPublic();

        Map<Class,Object> keyPairMap=new HashMap<Class,Object>();
        keyPairMap.put(PrivateKey.class,privateKey);
        keyPairMap.put(PublicKey.class,publicKey);
        return keyPairMap;
    }

    /**
     * 生成 非对称加密算法 公私钥对的方法-返回字符Base64 字符串格式
     * @param algorithm 加密算法
     * @param keySize   私钥长度-实际会生成一个近似值
     * <li>{@code DiffieHellman} (1024)</li>
     * <li>{@code DSA} (1024)</li>
     * <li>{@code RSA} (1024, 2048)</li>
     * @return
     * @throws NoSuchAlgorithmException
     */
    private static Map<Class,Object> initKeyPairBase64Str(String algorithm,int keySize) throws NoSuchAlgorithmException {
        //指定算法 RSA、DiffieHellman、DSA
        KeyPairGenerator keyPairGenerator=KeyPairGenerator.getInstance(algorithm);
        //指定私钥的长度——实际生成的也是近似值
        keyPairGenerator.initialize(keySize);
        //生成公私钥对
        KeyPair kPair=keyPairGenerator.generateKeyPair();
        PrivateKey privateKey=kPair.getPrivate();
        PublicKey publicKey=kPair.getPublic();

        //以 Base64 格式输出 公私钥
        String privateKeyStr= Base64.encodeBase64String(privateKey.getEncoded());

        String publicKeyStr=Base64.encodeBase64String(publicKey.getEncoded());
        System.out.println(privateKey.getEncoded().length);
        System.out.println(publicKey.getEncoded().length);
        //System.out.println("私钥:"+"algorithm:"+privateKey.getAlgorithm()+";format:"+privateKey.getFormat()+";私钥base64:"+privateKeyStr);
        //System.out.println("公钥:"+"algorithm:"+publicKey.getAlgorithm()+";format:"+publicKey.getFormat()+";公钥base64:"+publicKeyStr);

        Map<Class,Object> keyPairMap=new HashMap<Class,Object>();
        keyPairMap.put(PrivateKey.class,privateKeyStr);
        keyPairMap.put(PublicKey.class,publicKeyStr);
        return keyPairMap;
    }
公钥的恢复

公钥一般遵循 .509 规范
下面给出公钥的恢复办法

 /**
     * 由Base64字符编码恢复 公钥
     * @param pubKeyBase64Str    公钥 Base64 编码 字符串
     * @param algorithm          算法名称,如 RSA\DSA\DiffieHellman
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PublicKey restorePublicKey(String pubKeyBase64Str,String algorithm) throws NoSuchAlgorithmException, InvalidKeySpecException {
        //将Base64 字符串转化为字节数组
        byte[] keyBytes=Base64.decodeBase64(pubKeyBase64Str);
        //
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        //指定算法 比如 RSA
        KeyFactory factory = KeyFactory.getInstance(algorithm);
        //
        PublicKey publicKey = factory.generatePublic(x509EncodedKeySpec);
        return publicKey;
    }
私钥的恢复

私钥句法规范为 PKCS#8,
下面给出公钥的恢复办法

/**
     * 由Base64字符编码恢复 私钥
     * @param privateBase64Str 私钥 Base64 编码字符串
     * @param algorithm        算法名称,如 RSA\DSA\DiffieHellman
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PrivateKey restorePrivateKey(String privateBase64Str,String algorithm) throws NoSuchAlgorithmException, InvalidKeySpecException {
        //将Base64 字符串转化为字节数组
        byte[] keyBytes=Base64.decodeBase64(privateBase64Str);
        //私钥标准
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
        //指定算法,比如 RSA
        KeyFactory factory = KeyFactory.getInstance(algorithm);
        //获取私钥
        PrivateKey privateKey = factory.generatePrivate(pkcs8EncodedKeySpec);
        return privateKey;
    }

加解密过程

加密过程

加密一般使用公钥

 /**
     * 将 字符串 用指定加密算法 加密
     * @param publicKey        公钥
     * @param algorithm        算法,如 RSA
     * @param strBytes              待加密字节数组
     * @return
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     */
    public static byte[] encrypt(PublicKey publicKey,String algorithm,byte[] strBytes) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        //获取 cipher 对象,指定加密算法 比如 RSA
        Cipher cipher = Cipher.getInstance(algorithm);
        //初始化 cipher 对象,指定为加密模式,指定公钥
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(strBytes);
    }
解密过程

解密一般使用私钥

/**
     *  将 字符串 用指定加密算法 解密
     * @param privateKey     私钥
     * @param algorithm      算发,如 RSA
     * @param strBytes       待解密字节数组
     * @return
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws UnsupportedEncodingException
     */
    public static byte[] decipher(PrivateKey privateKey,String algorithm, byte[] strBytes) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException{
        Cipher cipher=Cipher.getInstance(algorithm);
        cipher.init(Cipher.DECRYPT_MODE,privateKey);
        return cipher.doFinal(strBytes);
    }

具体算法

工具类方法

在涉及到非对称加密相关的内容时,这个工具类都将起到基础的作用。
非对称加密基础工具类

import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;

import com.yeepay.g3.utils.common.StringUtils;

public class RsaUtils {
	
	/**Rsa2048 公钥*/
	private final static String RSA_2048_PUB_KEY="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC31wT9eT3jgUtIdtIV2bSMniolsfacvlVmoMX2NkL1h//sLvMTKbdFhTVhup0t47vbtnFYkUFGI0Q+ZM/IaUcvDXVaFf5G5GXity1KmbOf3G9+/LkEzty1nzkCRQTM+pZdgl1hIxBxncc4+okYxWKAuFUvgw0xjvuoh5mEqT9/9QIDAQAB";
	/**Rsa2048 私钥*/
	private final static String RSA_2048_PRI_KEY="MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALfXBP15PeOBS0h20hXZtIyeKiWx9py+VWagxfY2QvWH/+wu8xMpt0WFNWG6nS3ju9u2cViRQUYjRD5kz8hpRy8NdVoV/kbkZeK3LUqZs5/cb378uQTO3LWfOQJFBMz6ll2CXWEjEHGdxzj6iRjFYoC4VS+DDTGO+6iHmYSpP3/1AgMBAAECgYB94VqGYZ1yCZdOACZsVczeOHLtqsUNoPqDMnU62P7SdxRTWfaRWZAnp0XdLFXyFS0ODgfguF10tDNHceog9Y2KS3fHJK3JIur1Y55BqKzLlQlrAWpziaigEw+xJqiO+U67gomlCLTS6kYhFvPGXip3wOctvEh8yG1RX3exTmiVwQJBAPlWm6Aok86vHsBS8Znev1VV9nsSexrJXXkkdHv2ux2JSwQi/WNwWloolUtHFCGcyVmhNEmB7sSbR7/hmaKhiNECQQC8wGySaQIessawFnN/i+xmdoO0OeLCaR9MEce3B5dfRlTq37cfCbGCGnhSi5iRzx4/PHJGNoLPP2xFTVZ5VY3lAkA2rsTgwiVwbb2bxlUQPubNa1XsNehjvofOeq1FRp5Q4vxdwuK5fTmDjmT3pnYGzSDnlFAoUuOvoLKCpZKRNUYRAkALj4WW2hOlKbH9qwJb94f9JpkeesUmvyWJlTU0QqTE0xv0XstqfT+ABnsEI0Su+Y6StPMS1dfhNbM982Sufcz5AkB8OC1vMSQ0oYKxgS3nvNyNlTEjcveJALQAgU7vpSiA0/5LdwUFb7gCKQDL6pq8ySbZ4NV/U5xDVoBzsa5AIEVK";
	/**编码格式*/
	private final static String CHARSET="UTF-8";
	/**算法*/
	private final static String ALGORITHM="RSA";
	/**私钥对象*/
	private static PrivateKey privateKey;
	
	/**公钥对象*/
	private static PublicKey publicKey;
	static {
		try {
			 //将Base64 字符串转化为字节数组
	        byte[] keyBytes=Base64.decodeBase64(RSA_2048_PRI_KEY.getBytes(CHARSET));
	        //私钥标准
	        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
	        //指定算法,比如 RSA
	        KeyFactory factory = KeyFactory.getInstance(ALGORITHM);
	        //获取私钥
	        privateKey = factory.generatePrivate(pkcs8EncodedKeySpec);
	       
		}catch(Exception e){
			privateKey=null;
			//出现异常
		}
		
		try {
			//将Base64 字符串转化为字节数组
	        byte[] keyBytes=Base64.decodeBase64(RSA_2048_PUB_KEY.getBytes(CHARSET));
	        //公钥标准
	        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
	        //指定算法 比如 RSA
	        KeyFactory factory = KeyFactory.getInstance(ALGORITHM);
	        //获取公钥
	        publicKey = factory.generatePublic(x509EncodedKeySpec);
		}catch(Exception e) {
			publicKey=null;
			//出现异常
		}   
	}

	/**
	 * 私钥解密
	 * @param content
	 * @return
	 */
	public static String decryptByPriKey(String content) {
		//判断非空
		if(StringUtils.isBlank(content)||privateKey==null) {
			return "";
		}
		try {
			//进行解密处理
			Cipher cipher=Cipher.getInstance(ALGORITHM);
	        cipher.init(Cipher.DECRYPT_MODE,privateKey);
	        content = new String(cipher.doFinal(Base64.decodeBase64(content.getBytes(CHARSET))));
		}catch(Exception e) {
			content="";
		}
		
		return content;
	}
	
	/**
	 * 私钥加密
	 */
	public static String encryptByPriKey(String content) {

		//判断非空
		if(StringUtils.isBlank(content)||privateKey==null) {
			return "";
		}
		try {
			//进行加密处理
	        Cipher cipher = Cipher.getInstance(ALGORITHM);
	        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
			content = Base64.encodeBase64String(cipher.doFinal(content.getBytes(CHARSET)));
		}catch(Exception e) {
			content="";
		}
		
		return content;
	}
	
	/**
	 * 公钥解密
	 * @param content
	 * @return
	 */
	public static String decryptByPubKey(String content) {
		//判断非空
		if(StringUtils.isBlank(content)||publicKey==null) {
			return "";
		}
		try {
			//进行解密处理
			Cipher cipher=Cipher.getInstance(ALGORITHM);
	        cipher.init(Cipher.DECRYPT_MODE,publicKey);
	        content = new String(cipher.doFinal(Base64.decodeBase64(content.getBytes(CHARSET))));
		}catch(Exception e) {
			content="";
		}
		
		return content;
	}
	/**
	 * 公钥加密
	 */
	public static String encryptByPubKey(String content) {

		//判断非空
		if(StringUtils.isBlank(content)||publicKey==null) {
			return "";
		}
		try {
			//进行加密处理
	        Cipher cipher = Cipher.getInstance(ALGORITHM);
	        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
			content = Base64.encodeBase64String(cipher.doFinal(content.getBytes(CHARSET)));
		}catch(Exception e) {
			content="";
		}
		
		return content;
	}
	
	public static void main(String[] args) {
		String content="123456";
		System.out.println("内容:"+content);
		String contentEnByPub="";
		System.out.println("公钥加密:"+(contentEnByPub=RsaUtils.encryptByPubKey(content)));
		System.out.println("私钥解密:"+(content=RsaUtils.decryptByPriKey(contentEnByPub)));
		
		String content2="123456";
		System.out.println("内容2:"+content2);
		String contentEnByPri="";
		System.out.println("私钥加密:"+(contentEnByPri=RsaUtils.encryptByPriKey(content2)));
		System.out.println("公钥解密:"+(content2=RsaUtils.decryptByPubKey(contentEnByPri)));
	}

结果展示

内容:123456
公钥加密:XVivWsPPy8PwxTsfAwOmbENV4hXDW8YJcPf05bzsB4Z3/dd/kMiYgS18lKd6qDPq9ytDu6QwIy4cDBHZUq0RhX1Rx9NPbhDe7hYPltVgOP0rdRuKNXLr3GDuLTyOBcWtQq0xF13Kn+Vd6COIFrlSOdU96PGypqGJyBTW74GWKcc=
私钥解密:123456
内容2:123456
私钥加密:SsCdprB4FtPOa6SBVPMltIb+YpU4rCxG4bDZHPdOIW2U6sLsjDtoz2nkS30vAl51NQgdu4FN9I2ylixla1PNviJUNILmWR6vVXXKCe1o0fn4mIFvfNNoBiHoT9XYHomIyyFJ6T/Dd68hDSYQx1HrekjY/r7opfXv6tEJGekmCKs=
公钥解密:123456

RSA
  • RSA密码体制是一种公钥密码体制,加密算法公开,以分配的密钥作为加密解密的关键。一般来说,在一对公私钥中,公钥和私钥都可以用来加密和解密,即公钥加密能且只能被对应的私钥进行解密,私钥加密能且只能被对应的公钥进行解密。但我们一般都用公钥加密,私钥解密,而且生成的私钥往往会比公钥蕴含了更多的信息量。(这里说的加密肯定是可逆的,不然直接销毁就可以了没必要再去加密,加密是为了保障数据的安全和验证身份。)
  • RSA 会限制加解密字符串长度,加密字符串长度小于等于117 ,解密长度小于等于128
    RFC2246:美国政府不允许包含 大于 512位key的 RSA 加密模块的软件的出口,但是并没有限制大于 512位key的RSA 算法用于数字签名。
  • 加解密过程使用字节数组,如果要用于打印,为了保证不是乱码,一般通过Base64作为过渡展示或保存加密结果的字节数组,即 原字符串-字节数组-加密-加密结果字节数组-加密结果Base64字符串-加密结果字节数组-解密-解密结果字节数组-原字符串
  • 非对称加密算法效率低,一般仅用于签名验证。-查看 加密验签
 public static void main(String [] args) throws NoSuchAlgorithmException {
        String algorithm="RSA";
        int keySize=1024;
        String str="123456789abc";
        String charset="utf8";

        Map<Class,Object> keyPairMap=initKeyPair(algorithm,keySize);
        PublicKey publicKey=(PublicKey)keyPairMap.get(PublicKey.class);
        PrivateKey privateKey=(PrivateKey) keyPairMap.get(PrivateKey.class);

        //加密过程
        try {
            System.out.println("待加密字符串:str="+str);
            //将加密结果转化为Base64编码
            String result= Base64.encodeBase64String(AsymmetricEncryptionUtil.encrypt(publicKey,algorithm,str.getBytes()));
            //加密后得Base64字符串
            System.out.println("加密后的字节数组转化为 Base64展示="+result);
            //将加密的Base64字符串解密-将解密字节数组转Base64字符串
            String ostr= new String(AsymmetricEncryptionUtil.decipher(privateKey,algorithm,Base64.decodeBase64(result)));
            System.out.println("原字符串="+ostr);
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        }

    }
  • 结果
待加密字符串:str=123456789abc
加密后的字节数组转化为 Base64展示=BVSxdlkXI8xeqYBQylr6Uo1pinLpdSx6Ujj7Iou3ld6xj7uaq/6Z8hqD2jJaXKxNoLwN61df6vmC6aRGsJTTnnZ9W7zRiX/emuWzJw2XYXaEVq2jN9/HNWZFZH/6/1HHpET62Ngqb1bxLnBlF2EttT9GYDwaNmO9BqvFTS0Q0qM=
原字符串=123456789abc
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值