加密算法

本文详细介绍了Java中加密与解密的基本概念和技术实现,包括对称加密与非对称加密的不同应用场景,以及单向加密(摘要算法)的工作原理。通过具体示例展示了AES和RSA的加密解密过程,并解释了如何利用MD5进行单向加密,同时提供了加盐值增强安全性的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       加密,是以某种特殊的算法改变原有的信息数据。加密是数据安全必要手段。加密必备要素:密钥、算法。算法是手段,数据的安全是基于密钥。与加密相对的即解密。

      JDK中带有部分加密算法的实现类,主要的是java.security和javax.crypto包下的类,还可以使用Bouncy Castle(丰富JDK中加密算法的不足)jar包是:bcprov-jdk15on-1.57.jar和Commons Codec(简化JDK中加密的操作)jar包是:commons-codec-1.10.jar

       加密分类

  • 双向加密    -可逆加密,在需要时可反解为明文
  • 单项加密    -不可逆加密,无法反解

       双向加密

  • 对称加密    -解密公用密钥
  • 非对称加密  -加密使用公钥,解密使用私钥

双向加密

       双向加密分对称加密、非对称加密。对称加密公用密钥,效率高,但安全性不及非对称加密。通常在使用对称加密时,对密钥进行非对称加密后分配。

对称加密

      常用的对称加密DES、IDEA、RC2、RC4、SKIPJACK、RC5、AES...另外Base64‘加密’(编码)也是可逆。

       AES加密示例

package encry;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
 
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
 
public class EncrypAES {
	//KeyGenerator 提供对称密钥生成器的功能,支持各种算法
	private KeyGenerator keygen;
	//SecretKey 负责保存对称密钥
	private SecretKey deskey;
	//Cipher负责完成加密或解密工作
	private Cipher c;
	//该字节数组负责保存加密的结果
	private byte[] cipherByte;
	
	public EncrypAES() throws NoSuchAlgorithmException, NoSuchPaddingException{
		Security.addProvider(new com.sun.crypto.provider.SunJCE());
		//实例化支持DES算法的密钥生成器(算法名称命名需按规定,否则抛出异常)
		keygen = KeyGenerator.getInstance("AES");
		//生成密钥
		deskey = keygen.generateKey();
		//生成Cipher对象,指定其支持的DES算法
		c = Cipher.getInstance("AES");
	}
	
	/**
	 * 对字符串加密
	 * 
	 * @param str
	 * @return
	 * @throws InvalidKeyException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 */
	public byte[] Encrytor(String str) throws InvalidKeyException,
			IllegalBlockSizeException, BadPaddingException {
		// 根据密钥,对Cipher对象进行初始化,ENCRYPT_MODE表示加密模式
		c.init(Cipher.ENCRYPT_MODE, deskey);
		byte[] src = str.getBytes();
		// 加密,结果保存进cipherByte
		cipherByte = c.doFinal(src);
		return cipherByte;
	}
 
	/**
	 * 对字符串解密
	 * 
	 * @param buff
	 * @return
	 * @throws InvalidKeyException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 */
	public byte[] Decryptor(byte[] buff) throws InvalidKeyException,
			IllegalBlockSizeException, BadPaddingException {
		// 根据密钥,对Cipher对象进行初始化,DECRYPT_MODE表示加密模式
		c.init(Cipher.DECRYPT_MODE, deskey);
		cipherByte = c.doFinal(buff);
		return cipherByte;
	}
 
	/**
	 * @param args
	 * @throws NoSuchPaddingException 
	 * @throws NoSuchAlgorithmException 
	 * @throws BadPaddingException 
	 * @throws IllegalBlockSizeException 
	 * @throws InvalidKeyException 
	 */
	public static void main(String[] args) throws Exception {
		EncrypAES de1 = new EncrypAES();
		String msg ="郭XX-搞笑相声全集";
		byte[] encontent = de1.Encrytor(msg);
		byte[] decontent = de1.Decryptor(encontent);
		System.out.println("明文是:" + msg);
		System.out.println("加密后:" + new String(encontent));
		System.out.println("解密后:" + new String(decontent));
	}
 
}

       使用AES加密时可能有java.security.InvalidKeyException: Illegal key size异常,解决办法,http://www.cnblogs.com/milton/p/5058566.html

非对称加密

    常用算法RSA、Elgamal、背包算法、Rabin、HD、ECC(椭圆曲线加密算法)。其中RSA应用最为广泛。

    RSA示例

import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
 
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
 
public class EncrypRSA {
	
	/**
	 * 加密
	 * @param publicKey
	 * @param srcBytes
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchPaddingException
	 * @throws InvalidKeyException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 */
	protected byte[] encrypt(RSAPublicKey publicKey,byte[] srcBytes) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{
		if(publicKey!=null){
			//Cipher负责完成加密或解密工作,基于RSA
			Cipher cipher = Cipher.getInstance("RSA");
			//根据公钥,对Cipher对象进行初始化
			cipher.init(Cipher.ENCRYPT_MODE, publicKey);
			byte[] resultBytes = cipher.doFinal(srcBytes);
			return resultBytes;
		}
		return null;
	}
	
	/**
	 * 解密 
	 * @param privateKey
	 * @param srcBytes
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchPaddingException
	 * @throws InvalidKeyException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 */
	protected byte[] decrypt(RSAPrivateKey privateKey,byte[] srcBytes) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{
		if(privateKey!=null){
			//Cipher负责完成加密或解密工作,基于RSA
			Cipher cipher = Cipher.getInstance("RSA");
			//根据公钥,对Cipher对象进行初始化
			cipher.init(Cipher.DECRYPT_MODE, privateKey);
			byte[] resultBytes = cipher.doFinal(srcBytes);
			return resultBytes;
		}
		return null;
	}
 
	/**
	 * @param args
	 * @throws NoSuchAlgorithmException 
	 * @throws BadPaddingException 
	 * @throws IllegalBlockSizeException 
	 * @throws NoSuchPaddingException 
	 * @throws InvalidKeyException 
	 */
	public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
		EncrypRSA rsa = new EncrypRSA();
		String msg = "郭XX-精品相声";
		//KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
		KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
		//初始化密钥对生成器,密钥大小为1024位
		keyPairGen.initialize(1024);
		//生成一个密钥对,保存在keyPair中
		KeyPair keyPair = keyPairGen.generateKeyPair();
		//得到私钥
		RSAPrivateKey privateKey = (RSAPrivateKey)keyPair.getPrivate();				
		//得到公钥
		RSAPublicKey publicKey = (RSAPublicKey)keyPair.getPublic();
		
		//用公钥加密
		byte[] srcBytes = msg.getBytes();
		byte[] resultBytes = rsa.encrypt(publicKey, srcBytes);
		
		//用私钥解密
		byte[] decBytes = rsa.decrypt(privateKey, resultBytes);
		
		System.out.println("明文是:" + msg);
		System.out.println("加密后是:" + new String(resultBytes));
		System.out.println("解密后是:" + new String(decBytes));
	}
 
}

单向加密

    单向加密通常指消息摘要算法。消息摘要算法有MD(Message Digest)、SHA(Secure Hash Algorithm)、MAC(Message Authentication Code),主要作用是验证数据的完整性,是数字签名的核心算法。

        摘要算法其基本原理,对原文摘要成一个大数,大数转为字符串(如16进制式的字符串),不同的摘要算法体现在如何转为一个大数。如MD算法进行摘要为128bit大数(输出32为16进制的数字(0-F),因为1位16进制数代表4位二进制数)。

          

摘要算法长度
算法摘要长度实现方
MD2128JDK
MD4128Bouncy Castle
MD5128JDK
SHA-1160JDK
SHA-224224Bouncy Castle
SHA-256256JDK
SHA-384384JDK
SHA-512512JDK
HmacMD2128Bouncy Castle
HmacMD4128Bouncy Castle
HmacMD5128JDK
HmacSHA1160JDK
HmacSHA224224Bouncy Castle
HmacSHA256256JDK
HmacSHA384384JDK
HmacSHA512512JDK

    MD5加密示例

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
 
public class EncrypMD5 {
	
	public byte[] eccrypt(String info) throws NoSuchAlgorithmException{
		//根据MD5算法生成MessageDigest对象
		MessageDigest md5 = MessageDigest.getInstance("MD5");
		byte[] srcBytes = info.getBytes();
		//使用srcBytes更新摘要
		md5.update(srcBytes);
		//完成哈希计算,得到result
		byte[] resultBytes = md5.digest();
		return resultBytes;
	}
	
	
	public static void main(String args[]) throws NoSuchAlgorithmException{
		String msg = "郭XX-精品相声技术";
		EncrypMD5 md5 = new EncrypMD5();
		byte[] resultBytes = md5.eccrypt(msg);
		
		System.out.println("密文是:" + new String(resultBytes));
		System.out.println("明文是:" + msg);
	}
 
}

      实际上MD5实际使用时,会加点盐值,所谓的加盐值,实际上就是在大数字节数组中加入盐值数组,因此加密出来要大于32位。另外值得一提的是,MessageDigest的多次update,类似于连接作用,比如md5.update({1,2})+md5.update({3,4})等同于md5.update({1,2,3,4})。

       加盐值示例

package encry;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;

import org.apache.commons.codec.digest.DigestUtils;

public class Salt {


	    private static final String HEX_NUMS_STR="0123456789ABCDEF";
	    private static final Integer SALT_LENGTH = 12;

	    /**
	     * 将16进制字符串转换成字节数组
	     * @param hex
	     * @return
	     */
	    public static byte[] hexStringToByte(String hex) {
	        int len = (hex.length() / 2);
	        byte[] result = new byte[len];
	        char[] hexChars = hex.toCharArray();
	        for (int i = 0; i < len; i++) {
	            int pos = i * 2;
	            result[i] = (byte) (HEX_NUMS_STR.indexOf(hexChars[pos]) << 4
	                    | HEX_NUMS_STR.indexOf(hexChars[pos + 1]));
	        }
	        return result;
	    }


	    /**
	     * 将指定byte数组转换成16进制字符串
	     * @param b
	     * @return
	     */
	    public static String byteToHexString(byte[] b) {
	        StringBuffer hexString = new StringBuffer();
	        for (int i = 0; i < b.length; i++) {
	            String hex = Integer.toHexString(b[i] & 0xFF);
	            if (hex.length() == 1) {
	                hex = '0' + hex;
	            }
	            hexString.append(hex.toUpperCase());
	        }
	        return hexString.toString();
	    }

	    /**
	     * 验证口令是否合法
	     * @param password
	     * @param passwordInDb
	     * @return
	     * @throws NoSuchAlgorithmException
	     * @throws UnsupportedEncodingException
	     */
	    public static boolean validPassword(String password, String passwordInDb)
	            throws NoSuchAlgorithmException, UnsupportedEncodingException {
	        //将16进制字符串格式口令转换成字节数组
	        byte[] pwdInDb = hexStringToByte(passwordInDb);
	        //声明盐变量
	        byte[] salt = new byte[SALT_LENGTH];
	        //将盐从中保存的口令字节数组中提取出来
	        System.arraycopy(pwdInDb, 0, salt, 0, SALT_LENGTH);
	        //创建消息摘要对象
	        MessageDigest md = MessageDigest.getInstance("MD5");
	        //将盐数据传入消息摘要对象
	        md.update(salt);
	        //将口令的数据传给消息摘要对象
	        md.update(password.getBytes("UTF-8"));
	        //生成输入口令的消息摘要
	        byte[] digest = md.digest();
	        //声明一个保存数据库中口令消息摘要的变量
	        byte[] digestInDb = new byte[pwdInDb.length - SALT_LENGTH];
	        //取得数据库中口令的消息摘要
	        System.arraycopy(pwdInDb, SALT_LENGTH, digestInDb, 0, digestInDb.length);
	        //比较根据输入口令生成的消息摘要和数据库中消息摘要是否相同
	        if (Arrays.equals(digest, digestInDb)) {
	            //口令正确返回口令匹配消息
	            return true;
	        } else {
	            //口令不正确返回口令不匹配消息
	            return false;
	        }
	    }


	    /**
	     * 获得加密后的16进制形式口令
	     * @param password
	     * @return
	     * @throws NoSuchAlgorithmException
	     * @throws UnsupportedEncodingException
	     */
	    public static String getEncryptedPwd(String password)
	            throws NoSuchAlgorithmException, UnsupportedEncodingException {
	        //声明加密后的口令数组变量
	        byte[] pwd = null;
	        //随机数生成器
	        SecureRandom random = new SecureRandom();
	        //声明盐数组变量
	        byte[] salt = new byte[SALT_LENGTH];
	        //将随机数放入盐变量中
	        random.nextBytes(salt);

	        //声明消息摘要对象
	        MessageDigest md = null;
	        //创建消息摘要
	        md = MessageDigest.getInstance("MD5");
	        //将盐数据传入消息摘要对象
	         md.update(salt);
	        //将口令的数据传给消息摘要对象
	        md.update(password.getBytes("UTF-8"));
	        //获得消息摘要的字节数组
	        byte[] digest = md.digest();

	        //因为要在口令的字节数组中存放盐,所以加上盐的字节长度
	        pwd = new byte[digest.length + SALT_LENGTH];
	        //将盐的字节拷贝到生成的加密口令字节数组的前12个字节,以便在验证口令时取出盐
	        System.arraycopy(salt, 0, pwd, 0, SALT_LENGTH);
	        //将消息摘要拷贝到加密口令字节数组从第13个字节开始的字节
	        System.arraycopy(digest, 0, pwd, SALT_LENGTH, digest.length);
	        //将字节数组格式加密后的口令转化为16进制字符串格式的口令
	        return byteToHexString(pwd);
	    }
}

数字签名 

       几乎所有的数字签名方案都要和快速高效的摘要算法(Hash函数)一起使用,当公钥算法与摘要算法结合起来使用时,便构成了一种有效地数字签名方案。

这个过程是:首先用摘要算法对消息进行摘要,然后在把摘要值用信源的私钥加密;接收方先把接收的明文用同样的摘要算法摘要,形成“准签体”,然后再把准签体与用信源的公钥解密出的“签体”进行比较,如果相同就认为消息是完整的,否则消息不完整。

这种方法使公钥加密只对消息摘要进行操作,因为一种摘要算法的摘要消息长度是固定的,而且都比较“短”(相对于消息而言),正好符合公钥加密的要求。这样效率得到了提高,而其安全性也并未因为使用摘要算法而减弱。

参考:

java常用算法:https://blog.youkuaiyun.com/MJM_49/article/details/77429883

加密算法:https://blog.youkuaiyun.com/shenggaofei/article/details/52333687

java加密:https://www.cnblogs.com/lz2017/p/6917049.html

四种基本加密: http://www.runoob.com/w3cnote/java-encryption.html

MD5加密原理: https://www.cnblogs.com/foxclever/p/7668369.html

SHA加密: https://blog.youkuaiyun.com/jingcheng345413/article/details/54969292

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值