哈希算法是对称算法还是非对称算法_对称加密和非对称加密原理

哈希算法是对称算法还是非对称算法_对称加密和非对称加密原理哈希算法和·Hmac算法

哈希算法( Hash )又称摘要算法( Digest ),

作用:对任意一组输入数据进行计算,得到一个固定长度的输出摘要。 哈希算法的目的:为了验证原始数据是否被篡改。 哈希算法最重要的特点就是: 相同的输入一定得到相同的输出; 不同的输入大概率得到不同的输出。

 Java字符串的 hashCode() 就是一个哈希算法,它的输入是任意字符串,输出是固定的 4 字节 int 整数

"hello".hashCode(); // 0x5e918d2
"hello, java".hashCode(); // 0x7a9d88e8
"hello, bob".hashCode(); // 0xa0dbae2f

两个相同的字符串永远会计算出相同的 hashCode ,否则基于 hashCode 定位的 HashMap 就无法正常工作。这也是为什么当我们自定义 一个 class 时,覆写 equals() 方法时我们必须正确覆写 hashCode() 方法。

哈希冲突:两个不同的内容却又相同的哈希值:

"AaAaAa".hashCode(); // 0x7460e8c0
"BBAaBB".hashCode(); // 0x7460e8c0

"通话".hashCode(); // 0x11ff03
"重地".hashCode(); // 0x11ff03

 为了避免碰撞我们输出的长度越长越好:

Java 标准库提供了常用的哈希算法,并且有一套统一的接口。我们以 MD5 算法为例,看看如何对输入计算哈希:

import java.security.MessageDigest;

public class main {
	public static void main(String[] args)  {
		// 创建一个MessageDigest实例:
        MessageDigest md = MessageDigest.getInstance("MD5");
       
        // 反复调用update输入数据:
        md.update("Hello".getBytes("UTF-8"));
        md.update("World".getBytes("UTF-8"));
        
        // 16 bytes: 68e109f0f40ca72a15e05cc22786f8e6
        byte[] results = md.digest(); 

        StringBuilder sb = new StringBuilder();
        for(byte bite : results) {
        	sb.append(String.format("%02x", bite));
        }
        
        System.out.println(sb.toString());
	}
}

使用 MessageDigest 时,我们首先根据哈希算法获取一个 MessageDigest 实例,然后,反复调用 update(byte[]) 输入数据。当输入 结束后,调用 digest() 方法获得 byte[] 数组表示的摘要,最后,把它转换为十六进制的字符串。 运行上述代码,可以得到输入 HelloWorld 的 MD5 是 68e109f0f40ca72a15e05cc22786f8e6 。

MD5:

可以校验下载文件是否为原本文件;

可以存储数据库的密码,这样一来,数据库管理员看不到用户的原始口令。即使数据库泄漏,黑客也无法拿到用户的原始口令。想要拿到用户的原始口令,必须 用暴力穷举的方法,一个口令一个口令地试,直到某个口令计算的 MD5 恰好等于指定值。 使用哈希口令时,还要注意防止彩虹表攻击。什么是彩虹表呢?上面讲到了,如果只拿到 MD5 ,从 MD5 反推明文口令,只能使 用暴力穷举的方法。然而黑客并不笨,暴力穷举会消耗大量的算力和时间。但是,如果有一个预先计算好的常用口令和它们的 MD5 的 对照表,这个表就是彩虹表。如果用户使用了常用口令,黑客从 MD5 一下就能反查到原始口令

所以我们可以进行添加操作:使用SHA-1 也是一种哈希算法,它的输出是 160 bits ,即 20 字节。 SHA-1 是由美国国家安全局开发的, SHA 算法实际上是一个系列,包括 SH A-0 (已废弃)、 SHA-1 、 SHA-256 、 SHA-512 等。

import java.security.MessageDigest;

public class main {
	public static void main(String[] args)  {
		// 创建一个MessageDigest实例:
        MessageDigest md = MessageDigest.getInstance("SHA-1");
       
        // 反复调用update输入数据:
        md.update("Hello".getBytes("UTF-8"));
        md.update("World".getBytes("UTF-8"));
        
        // 20 bytes: db8ac1c259eb89d4a131b253bacfca5f319d54f2
        byte[] results = md.digest(); 

        StringBuilder sb = new StringBuilder();
        for(byte bite : results) {
        	sb.append(String.format("%02x", bite));
        }
        
        System.out.println(sb.toString());
	}
}

类似的,计算 SHA-256 ,我们需要传入名称” SHA-256 “,计算 SHA-512 ,我们需要传入名称” SHA-512 “。 

常见的哈希算法:

MD5:  输出长度16个字节128位

SHA-1:输出长度20个字节160位

RipeMD-160:输出长度字20节160位

SHA-256:输出长度32个字节256位

SHA-512:输出长度64字节512位

Hmac算法:(密钥算法);

在前面讲到哈希算法时,我们说,存储用户的哈希口令时,要加盐存储,目的就在于抵御彩虹表攻击。我们回顾一下哈希算法: d igest = hash(input)

正是因为相同的输入会产生相同的输出,我们加盐的目的就在于,使得输入有所变化: digest = hash(salt + input) 这个 salt 可以看作是一个额外的“认证码”,同样的输入,不同的认证码,会产生不同的输出。因此,要验证输出的哈希,必须同 时提供“认证码”。

Hmac 算法就是一种基于密钥的消息认证码算法,它的全称是 Hash-based Message Authentication Code ,是一种更安全的 消息摘要算法。

Hmac 算法总是和某种哈希算法配合起来用的。例如,我们使用 MD5 算法,对应的就是 Hmac MD5 算法,它相当于“加盐”的 MD 5 : HmacMD5 ≈ md5(secure_random_key, input)

因此, HmacMD5 可以看作带有一个安全的 key 的 MD5 。

使用 HmacMD5 而不是用 MD5 加 salt ,有如下好处:

HmacMD5 使用的 key 长度是 64 字节,更安全;

Hmac 是标准算法,同样适用于 SHA-1 等其他哈希算法;

Hmac 输出和原有的哈希算法长度一致。

可见, Hmac 本质上就是把 key 混入摘要的算法。验证此哈希时,除了原始的输入数据,还要提供 key 。为了保证安全,我们 不会自己指定 key ,而是通过 Java 标准库的 KeyGenerator 生成一个安全的随机的 key 。 下面是使用 HmacMD5 的参考代码:

package com.liubatian;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;

public class woker06 {
public static void main(String[] args) throws InvalidKeyException {
	String password = "ananca";
	try {
		//生成密钥
		//密钥生成器
		KeyGenerator kergeb = KeyGenerator.getInstance("HmacMD5");
		//生成密钥
		SecretKey key = kergeb.generateKey();
		
		//获取密钥
		byte [] ket = key.getEncoded();
		System.out.println("密钥长度:"+ ket.length);
		StringBuilder ke = new StringBuilder();
		for(byte j : ket) {
			ke.append(String.format("%02x", j));
		}
		System.out.println(ke);
			//使用密钥进行加密
			//获取算法对象
			Mac mac = Mac.getInstance("HmacMD5");
			//初始化密钥
			mac.init(key);
			//更新原始内容
			mac.update(password.getBytes());
			//加密
			byte [] resulyarray = mac.doFinal();
			System.out.println("加密结果:"+ resulyarray.length+" 字节");
             StringBuilder result =new StringBuilder();
             for(byte a : resulyarray) {
            	 result.append(String.format("%02x",a));
             }
         
             System.out.println("加密结果"+result);
             //System.out.println("加密结果长度"+result.length());
				
		
	} catch (NoSuchAlgorithmException e) {
				e.printStackTrace();
	}
}
}

和 MD5 相比,使用 HmacMD5 的步骤是:

1 通过名称 HmacMD5 获取 KeyGenerator 实例;

2 通过 KeyGenerator 创建一个 SecretKey 实例;

3 通过名称 HmacMD5 获取 Mac 实例;

4 用 SecretKey 初始化Mac实例;

5 对 Mac 实例反复调用 update(byte[]) 输入数据;

6 调用 Mac 实例的 doFinal() 获取最终的哈希值。 

SecretKey 不能从 KeyGenerator 生成,而是从一个 byte[] 数组恢复:

package com.liubatian;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

public class woker07 {
public static void main(String[] args) throws InvalidKeyException {
	String password = "ananca";
	try {
		//回复密钥
		byte [] kergeb = {126, 49, 110, 126, -79, -5, 66, 34, -122, 123, 107, -63, 106, 100, -28, 67, 19, 23, 1, 23, 47, 63, 47, 109, 123, -111, -27, -121, 103, -11, 106, -26, 110, -27, 107, 40, 19, -8, 57, 20, -46, -98, -82, 102, -104, 96, 87, -16, 93, -107, 25, -56, -113, 12, -49, 96, 6, -78, -31, -17, 100, 19, -61, -58};
		//生成密钥
		SecretKey key = new SecretKeySpec(kergeb, "HmacMD5");
		
		   //加密
			
			Mac mac = Mac.getInstance("HmacMD5");
			//初始化密钥
			mac.init(key);
			//更新原始内容
			mac.update(password.getBytes());
			//加密
			byte [] resulyarray = mac.doFinal();
			System.out.println("加密结果:"+ resulyarray.length+" 字节");
             StringBuilder result =new StringBuilder();
             for(byte a : resulyarray) {
            	 result.append(String.format("%02x",a));
             }
         
             System.out.println("加密结果"+result);
             //System.out.println("加密结果长度"+result.length());
				
		
	} catch (NoSuchAlgorithmException e) {
				e.printStackTrace();
	}
}
}

恢复 SecretKey 的语句就是 new SecretKeySpec(hkey, “HmacMD5”) 。

 对称加密算法:AES加密

常见的AES加密算法:

AES:密钥长度为128,192,256字节;工作模式CBC,EBC,PCBC;填充模式NoPadding/PKCS5Padding/PKCS7Padding

IDEA:密钥长度168字节;工作模式,EBC。填充模式:PKCS5Padding/PKCS7Padding/

DES:密钥长度54,68,字节;工作模式CBC,EBC,PCBC。填充模式:NoPadding/PKCS5Padding/PKCS7Padding

 ECB:模式:

import java.security.*;
import java.util.Base64;

import javax.crypto.*;
import javax.crypto.spec.*;

public class Main {
    public static void main(String[] args) throws Exception {
        // 原文:
        String message = "Hello, world!";
        System.out.println("Message(原始信息): " + message);
        
        // 128位密钥 = 16 bytes Key:
        byte[] key = "1234567890abcdef".getBytes();
        
        // 加密:
        byte[] data = message.getBytes();
        byte[] encrypted = encrypt(key, data);
        System.out.println("Encrypted(加密内容): " + 
        					Base64.getEncoder().encodeToString(encrypted));
        
        // 解密:
        byte[] decrypted = decrypt(key, encrypted);
        System.out.println("Decrypted(解密内容): " + new String(decrypted));
    }

    // 加密:
    public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException {
    	// 创建密码对象,需要传入算法/工作模式/填充模式
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    	
        // 根据key的字节内容,"恢复"秘钥对象
        SecretKey keySpec = new SecretKeySpec(key, "AES");
        
        // 初始化秘钥:设置加密模式ENCRYPT_MODE
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        
        // 根据原始内容(字节),进行加密
        return cipher.doFinal(input);
    }

    // 解密:
    public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException {
    	// 创建密码对象,需要传入算法/工作模式/填充模式
    	Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        
    	// 根据key的字节内容,"恢复"秘钥对象
        SecretKey keySpec = new SecretKeySpec(key, "AES");
        
        // 初始化秘钥:设置解密模式DECRYPT_MODE
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        
        // 根据原始内容(字节),进行解密
        return cipher.doFinal(input);
    }
}

1 根据算法名称/工作模式/填充模式获取 Cipher 实例;

2 根据算法名称初始化一个 SecretKey 实例,密钥必须是指定长度

3 使用 SerectKey 初始化 Cipher 实例,并设置加密或解密模式;

4 传入明文或密文,获得密文或明文。 

 CBC模式:

package com.apesource.demo04;

import java.security.*;
import java.util.Base64;

import javax.crypto.*;
import javax.crypto.spec.*;

public class Main {
	public static void main(String[] args) throws Exception {
        // 原文:
        String message = "Hello, world!";
        System.out.println("Message(原始信息): " + message);
        
        // 256位密钥 = 32 bytes Key:
        byte[] key = "1234567890abcdef1234567890abcdef".getBytes();
        
        // 加密:
        byte[] data = message.getBytes();
        byte[] encrypted = encrypt(key, data);
        System.out.println("Encrypted(加密内容): " + 
				Base64.getEncoder().encodeToString(encrypted));
        
        // 解密:
        byte[] decrypted = decrypt(key, encrypted);
        System.out.println("Decrypted(解密内容): " + new String(decrypted));
    }

    // 加密:
    public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException {
        // 设置算法/工作模式CBC/填充
    	Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    	
    	// 恢复秘钥对象
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        
        // CBC模式需要生成一个16 bytes的initialization vector:
        SecureRandom sr = SecureRandom.getInstanceStrong();
        byte[] iv = sr.generateSeed(16); // 生成16个字节的随机数
        System.out.println(Arrays.toString(iv));
        IvParameterSpec ivps = new IvParameterSpec(iv); // 随机数封装成IvParameterSpec参数对象
        
        // 初始化秘钥:操作模式、秘钥、IV参数
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivps);
        
        // 加密
        byte[] data = cipher.doFinal(input);
        
        // IV不需要保密,把IV和密文一起返回:
        return join(iv, data);
    }

    // 解密:
    public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException {
        // 把input分割成IV和密文:
        byte[] iv = new byte[16];
        byte[] data = new byte[input.length - 16];
        
        System.arraycopy(input, 0, iv, 0, 16); // IV
        System.arraycopy(input, 16, data, 0, data.length); //密文
        System.out.println(Arrays.toString(iv));
        
        // 解密:
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // 密码对象
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); // 恢复秘钥
        IvParameterSpec ivps = new IvParameterSpec(iv); // 恢复IV
        
        // 初始化秘钥:操作模式、秘钥、IV参数
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivps);
        
        // 解密操作
        return cipher.doFinal(data);
    }
    
    // 合并数组
    public static byte[] join(byte[] bs1, byte[] bs2) {
        byte[] r = new byte[bs1.length + bs2.length];
        System.arraycopy(bs1, 0, r, 0, bs1.length);
        System.arraycopy(bs2, 0, r, bs1.length, bs2.length);
        return r;
    }
}

对称加密算法使用同一个密钥进行加密和解密,常用算法有 DES 、 AES 和 IDEA 等; 密钥长度由算法设计决定, AES 的密钥长度是 128 / 192 / 256 位; 使用对称加密算法需要指定算法名称、工作模式和填充模式。

非对称加密: 

简单来说就是一个密钥对;一个人有一个公钥和私钥;他将公钥公开;所有人用公钥加密将信息发给这个人,这些信息就只能用这个人的私钥解密;非常安全不会泄露:

使用RSA算法实现:

import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import javax.crypto.Cipher;

// RSA
public class Main {
	public static void main(String[] args) throws Exception {
		// 明文:
		byte[] plain = "Hello, encrypt use RSA".getBytes("UTF-8");

		// 创建公钥/私钥对:
		Human alice = new Human("Alice");

		// 用Alice的公钥加密:
		// 获取Alice的公钥,并输出
		byte[] pk = alice.getPublicKey();
		System.out.println(String.format("public key(公钥): %x", new BigInteger(1, pk)));

		// 使用公钥加密
		byte[] encrypted = alice.encrypt(plain);
		System.out.println(String.format("encrypted(加密): %x", new BigInteger(1, encrypted)));

		// 用Alice的私钥解密:
		// 获取Alice的私钥,并输出
		byte[] sk = alice.getPrivateKey();
		System.out.println(String.format("private key(私钥): %x", new BigInteger(1, sk)));

		// 使用私钥解密
		byte[] decrypted = alice.decrypt(encrypted);
		System.out.println("decrypted(解密): " + new String(decrypted, "UTF-8"));
	}
}

// 用户类
class Human {
	// 姓名
	String name;

	// 私钥:
	PrivateKey sk;

	// 公钥:
	PublicKey pk;

	// 构造方法
	public Human(String name) throws GeneralSecurityException {
		// 初始化姓名
		this.name = name;

		// 生成公钥/私钥对:
		KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
		kpGen.initialize(1024);
		KeyPair kp = kpGen.generateKeyPair();

		this.sk = kp.getPrivate();
		this.pk = kp.getPublic();
	}

	// 把私钥导出为字节
	public byte[] getPrivateKey() {
		return this.sk.getEncoded();
	}

	// 把公钥导出为字节
	public byte[] getPublicKey() {
		return this.pk.getEncoded();
	}

	// 用公钥加密:
	public byte[] encrypt(byte[] message) throws GeneralSecurityException {
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.ENCRYPT_MODE, this.pk); // 使用公钥进行初始化
		return cipher.doFinal(message);
	}

	// 用私钥解密:
	public byte[] decrypt(byte[] input) throws GeneralSecurityException {
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.DECRYPT_MODE, this.sk); // 使用私钥进行初始化
		return cipher.doFinal(input);
	}
}

 

RSA 的公钥和私钥都可以通过 getEncoded() 方法获得以 byte[] 表示的二进制数据,并根据需要保存到文件中。要从 byte[] 数组恢复公钥或私 钥,可以这么写:

非对称加密就是加密和解密使用的不是相同的密钥,只有同一个公钥-私钥对才能正常加解密;

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/204002.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 学生网上选课管理系统_选课管理系统

    学生网上选课管理系统_选课管理系统**数据库系统原理课程设计报告**学生选课管理系统(上)设计内容与要求:1、系统用户由三类组成:教师、学生和管理员。2、管理员负责的主要功能:①用户管理(老师、学生及管理员的增、删、改);②课程管理(添加、删除和修改);③选课管理(实现选课功能开放和禁止、老师成绩输入开放和禁止)。3、学生通过登录,可以查询课程的基本信息、实现选课、退课和成绩查询;4、老师通过登录,可以查看选…

    2022年10月16日
  • 分布式微服务架构的优缺点_单体架构和微服务架构

    分布式微服务架构的优缺点_单体架构和微服务架构单体架构、分布式架构、微服务优缺点单体架构特点?简单方便,高度耦合,扩展性差,适合小型项目。eg:学生管理系统。分布式架构特点?松耦合,扩展性好,但架构复杂,难度大。适合大型互联网项目,eg:京东、淘宝微服务:一种良好的分布式架构方案*优点:拆分粒度更小、服务更独立、耦合度更低*缺点:架构非常复杂,运维、监控、部署难度提高…

    2022年11月1日
  • 如何使用Vue封装组件[通俗易懂]

    如何使用Vue封装组件[通俗易懂]首先了解一下封装组件的步骤:1.props:父传子2.事件(派发和监听)this.$emit()派发事件通过@或$on来监听事件3.slot(插槽),内容分发命名插槽<slotname=”命名插槽名”></slot>例如:<slotname=”ssl”></slot>使用:<divslot=”ssl”>…..</div>想封装组件呢首先要思考:1.你想向用户暴露哪些属性(

    2022年9月24日
  • Java Bean Copy 性能大比拼

    Java Bean Copy 性能大比拼

    2021年7月3日
  • dropdownlist1 绑定 value值

    dropdownlist1 绑定 value值DropDownList1.Items.Add(newListItem(dr[“status”].ToString(),dr[“status_Id”].ToString()));里面的两个参数都必须是string类型的,第一个参数是text,第二个参数是value转载于:https://www.cnblogs.com/tiancai/archive/2011/08/1…

    2022年7月18日
  • 友善串口助手使用教程_友善串口调试助手基本功能怎么使用-友善串口调试助手使用教程…

    友善串口助手使用教程_友善串口调试助手基本功能怎么使用-友善串口调试助手使用教程…软件功能:友善串口调试助手(win7串口调试工具)是一个很好而小巧的串口调试助手,友善串口调试助手官方版支持二进制面板和TLS、支持终端窗口和远程访问,能够与串口进行通信,访问、修改串行端口,还能够自动识别、自动搜索串口。友善串口调试助手还可以用ASCII码或十六进制接收或发送任何数据或字符,也可以让用户任意设定自动发送周期,还能够把结束数据保存成文本文件,用户也可以通过友善串口调试助手发送任意大…

    2022年4月28日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号