安全散列算法SHA (Secure Hash Algorithm)是美国国家标准和技术局发布的国家标准FIPS PUB 180-1,通常称为SHA-1。其对长度不超过264二进制位的消息产生160位的消息摘要输出,按512比特块处理其输入。java
SHA是一种数据加密算法,该算法通过加密专家多年来的发展和改进已日益完善,如今已成为公认的最安全的散列算法之一,并被普遍使用。git
该算法的思想是接收一段明文,而后以一种不可逆的方式将它转换成一段(一般更小)密文,也能够简单的理解为取一串输入码(称为预映射或信息),并把它们转化为长度较短、位数固定的输出序列即散列值(也称为信息摘要或信息认证代码)的过程。散列函数值能够说时对明文的一种“指纹”或是“摘要”因此对散列值的数字签名就能够视为对此明文的数字签名。算法
消息摘要(Message Digest)又称为数字摘要(Digital Digest)。它是一个惟一对应一个消息或文本的固定长度的值,它由一个单向Hash加密函数对消息进行做用而产生。若是消息在途中改变了,则接收者经过对收到消息的新产生的摘要与原摘要比较,就可知道消息是否被改变了。所以消息摘要保证了消息的完整性。数组
消息摘要采用单向Hash函数将需加密的明文"摘要"成一串128bit的密文,这一串密文亦称为数字指纹(Finger Print),它有固定的长度,且不一样的明文摘要成密文,其结果老是不一样的,而一样的明文其摘要一定一致。这样这串摘要即可成为验证实文是不是"真身"的"指纹"了。安全
摘要:GOST3411
,Keccak
,MD2
,MD4
,MD5
,RIPEMD128
,RIPEMD160
,RIPEMD256
,RIPEMD320
,SHA-1
,SHA-224
,SHA-256
,SHA-384
,SHA-512
,SHA3
,Tiger
和Whirlpool
。app
公钥和私钥就是俗称的不对称加密方式,是从之前的对称加密(使用用户名与密码)方式的提升。ide
下面用电子邮件的方式说明一下原理:
使用公钥与私钥的目的就是实现安全的电子邮件,必须实现以下目的:函数
AES
, Blowfish
, Camellia
, CAST5
, CAST6
,ChaCha
, DES
, DESede
, GOST28147
, HC-128
, HC-256
, IDEA
, ISAAC
, Noekeon
, RC2
, RC4
, RC5-32
, RC5-64
, RC6
, Rijndael
, Salsa20
, SEED
, Serpent
, Skipjack
, TEA/XTEA
, Threefish
, Tnepres
, Twofish
, VMPC
and XSalsa20
.CBC
,CFB
,CTS
,GOFB
,OFB
,OpenPGPCFB
和SIC(或CTR)
。ISO10126d2
, ISO7816d4
, PKCS-5/7
, TBC
, X.923
, and Zero Byte
.ElGamal
, DSA
, ECDSA
, NaccacheStern
and RSA (with blinding)
.ISO9796d1
, OAEP
, and PKCS-1
.注: 上述需求对应于防火墙、加密、数字签名、身份认证等技术,但其关键在于数字签名技术。post
数字签名是经过一个单向函数对要传送的报文进行处理获得的用以认证报文来源并核实报文是否发生变化的一个字母数字串。测试
实现数字签名有不少方法,目前数字签名采用较多的是公钥加密技术,如基于RSA Date Security 公司的PKCS( Public Key Cryptography Standards )、Digital Signature Algorithm、x.50九、PGP(Pretty Good Privacy)。
1994年美国标准与技术协会公布了数字签名标准(DSS)而使公钥加密技术普遍应用。公钥加密系统采用的是非对称加密算法。
MD2withRSA
, MD4withRSA
,MD5withRSA
, RIPEMD128withRSA
, RIPEMD160withECDSA
, RIPEMD160withRSA
, RIPEMD256withRSA
, SHA-1withRSA
, SHA-224withRSA
, SHA-256withRSAandMGF1
, SHA-384withRSAandMGF1
, SHA-512withRSAandMGF1
, SHA-1withDSA
, and SHA-1withECDSA
对于长度小于2^64位的消息,SHA1会产生一个160位(40个字符)的消息摘要。当接收到消息的时候,这个消息摘要能够用来验证数据的完整性。在传输的过程当中,数据极可能会发生变化,那么这时候就会产生不一样的消息摘要。
代码以下:
import javax.crypto.Cipher; import java.io.*; import java.security.*; import java.util.Base64; /** * @author: mmzsit * @date: 2018年10月24日 * @Description: * 博客地址:https://blog.mmzsblog.cn * @version V1.0 */ public class EncryptUtil { public static void main(String[] args) { ObjectInputStream inputStream = null; //参数字符串 String userName="测试test0->1"; String orderId="测试ID123456"; String price="666"; //构建用于签名和传输的字符串 StringBuffer bufferStr =new StringBuffer(); bufferStr.append("userName=").append(userName) .append("&orderId=").append(orderId) .append("&price=").append(price); //将构建的字符串转化为String类型 String localStr =bufferStr.toString(); //签名算法加密 try { //随机生成秘钥对 // 检查是否存在这对密钥,不然生成这些密钥 if (!areKeysPresent()) { // 使用RSA算法生成一对密钥,并存储在指定路径的指定文件中 generateKey(); } //服务端数字签名开始 //第一步:用SHA-1算出原文的摘要 byte[] shaDigest = shaEncrypt(localStr); System.out.println("原文本内容:\n"+localStr); String shaStr = new String(shaDigest,"UTF-8"); System.out.println("原文本内容SHA-1算法后:\n"+shaStr); //第二步:使用私钥对原文进行加密 //读取文件中的私钥 inputStream = new ObjectInputStream(new FileInputStream(PRIVATE_KEY_FILE)); final PrivateKey privateKey = (PrivateKey) inputStream.readObject(); //使用私钥加密 byte[] rsaBytes = rsaEncrypt(shaDigest,privateKey); //第三步:对密文进行BASE64编码 byte[] base64Str = Base64.getEncoder().encode(rsaBytes); String base64enCode=new String(base64Str,"UTF-8"); System.out.println("加密后的内容:\n"+base64enCode); //签名加密完成数据传输到客户端 //客户端验证签名开始 //获取原文 String receiveStr=localStr; //第一步:使用Base64解码密文 byte[] bese64Decoded =Base64.getDecoder().decode(base64enCode.getBytes("UTF-8")); //第二步:使用公钥对密文进行解码 //读取文件中的公钥 inputStream = new ObjectInputStream(new FileInputStream(PUBLIC_KEY_FILE)); final PublicKey publicKey = (PublicKey) inputStream.readObject(); //使用公钥解密 byte[] rsaDecode = rsaDecrypt(bese64Decoded,publicKey); //公钥解密后的结果 String base64denCode=new String(rsaDecode,"utf-8"); System.out.println("公钥解密后的结果:\n"+base64denCode); //第三步:验签 //读取解密后的摘要 String sha1=Base64.getEncoder().encodeToString(rsaDecode); //使用Sha-1对原文计算摘要 MessageDigest md =MessageDigest.getInstance("SHA-1"); String sha2=Base64.getEncoder().encodeToString(md.digest(receiveStr.getBytes("utf-8"))); //用Sha-1对原文计算摘要结果和解密后的明文比对 if(sha1.equals(sha2)) { System.out.println("验签成功"); } else { System.out.println("验签失败"); } System.out.println("字符串sha1:\n"+sha1); System.out.println("字符串sha2:\n"+sha2); }catch (Exception e) { e.printStackTrace(); } } /** * 用于保存加密算法名称的字符串 */ public static final String ALGORITHM = "RSA"; /** * * 用于保存加密填充名称的字符串 * 若是不填写,那么RSA/NONE/NoPadding就是Bouncy Castle 的默认 RSA 实现 * 备用: */ public static final String PADDING = "RSA/ECB/PKCS1Padding"; /** * 用于保存安全提供程序名称的字符串 */ // public static final String PROVIDER = "BC"; /** * 用于保存私钥文件名称的字符串 */ public static final String PRIVATE_KEY_FILE = "d:/Temp/private.key"; /** * 用于保存公钥文件名称的字符串 */ public static final String PUBLIC_KEY_FILE = "d:/Temp/public.key"; /** * 假设最高安全性(即4096位RSA密钥或更大)是很是安全 * 使用1024字节生成包含一对私钥和公钥的密钥。 * 将该组密钥存储在Prvate.key和Public.key文件中。 * * @throws NoSuchAlgorithmException * @throws IOException * @throws FileNotFoundException */ public static void generateKey() { try { Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); final KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM); //final KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM, PROVIDER); //密钥位数 keyGen.initialize(1024); //密钥对 final KeyPair key = keyGen.generateKeyPair(); File privateKeyFile = new File(PRIVATE_KEY_FILE); File publicKeyFile = new File(PUBLIC_KEY_FILE); // 建立文件夹存储私钥 if (privateKeyFile.getParentFile() != null) { privateKeyFile.getParentFile().mkdirs(); } privateKeyFile.createNewFile(); // 建立文件夹存储公钥 if (publicKeyFile.getParentFile() != null) { publicKeyFile.getParentFile().mkdirs(); } publicKeyFile.createNewFile(); // 建立文件夹保存公钥 ObjectOutputStream publicKeyOS = new ObjectOutputStream( new FileOutputStream(publicKeyFile)); publicKeyOS.writeObject(key.getPublic()); publicKeyOS.close(); // 建立文件夹保存私钥 ObjectOutputStream privateKeyOS = new ObjectOutputStream( new FileOutputStream(privateKeyFile)); privateKeyOS.writeObject(key.getPrivate()); privateKeyOS.close(); } catch (Exception e) { e.printStackTrace(); } } /** * 检查是否已生成一对公钥和私钥 * * @return boolean 返回是否生成秘钥对的标识 */ public static boolean areKeysPresent() { File privateKey = new File(PRIVATE_KEY_FILE); File publicKey = new File(PUBLIC_KEY_FILE); if (privateKey.exists() && publicKey.exists()) { return true; } return false; } /** * 使用公钥解密数据 * * @param text 待解密文本 * @param key 公钥 * @return 解密文本 * @throws java.lang.Exception */ public static byte[] rsaDecrypt(byte[] text, PublicKey key) { byte[] cipherText = null; try { // 获取RSA密码对象并打印提供程序 // Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); final Cipher cipher = Cipher.getInstance(PADDING); //final Cipher cipher = Cipher.getInstance(PADDING, PROVIDER); // 使用公钥,ENCRYPT_MODE表示为解密模式 cipher.init(Cipher.DECRYPT_MODE, key); cipherText = cipher.doFinal(text); } catch (Exception e) { e.printStackTrace(); } return cipherText; } /** * 使用私钥加密数据 * * @param text 待加密文本 * @param key 私钥 * @return 加密后的数据 * @throws java.lang.Exception */ public static byte[] rsaEncrypt(byte[] text, PrivateKey key) { byte[] dectyptedText = null; try { // //获取RSA密码对象并打印提供程序 // Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); final Cipher cipher = Cipher.getInstance(PADDING); //final Cipher cipher = Cipher.getInstance(PADDING, PROVIDER); // 使用私钥加密文本 cipher.init(Cipher.ENCRYPT_MODE, key); dectyptedText = cipher.doFinal(text); } catch (Exception ex) { ex.printStackTrace(); } return dectyptedText; } /** * 使用sha-1对摘要进行加密 * @param text 签名的原始文本 */ public static byte[] shaEncrypt(String text) { //建立消息摘要算法的类 MessageDigest md = null; //因为接收加密后的摘要的字节数组 byte[] shaDigest = null; try { //使用getInstance("算法")来得到消息摘要 md = MessageDigest.getInstance("SHA-1"); //将摘要转化为UTF-8格式的字节数组 byte[] plainText = text.getBytes("UTF-8"); //使用指定的 byte 数组更新摘要 md.update(plainText); //得出SHA-1算法加密后的结果 shaDigest=md.digest(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return shaDigest; } }