文章目录
国密即国家密码局认定的国产密码算法,即商用密码,主要用于对不涉及国家秘密内容但又具有敏感性的内部信息、行政事务信息、经济信息等进行加密保护。。具体包括
- SM1,对称加密算法,加密强度为128位,采用硬件实现
- SM2为国家密码管理局公布的公钥算法,其加密强度为256位
- SM3,密码杂凑算法,杂凑值长度为32字节,和SM2算法同期公布,参见《国家密码管理局公告(第 22 号)》
- SMS4,对称加密算法,随WAPI标准一起公布,可使用软件实现,加密强度为128位。
分组密码算法
- 概念:将明文数据按固定长度进行分组,然后在同一密钥控制下逐组进行加密,从而将各个明文分组变换成一个等长的密文分组的密码。其中二进制明文分组的长度称为该分组密码的分组规模。
- 原则:
- 必须实现起来比较简单,知道密钥时加密和脱密都十分容易,适合硬件和(或)软件实现.
- 加脱密速度和所消耗的资源和成本较低,能满足具体应用范围的需要.
- 遵循混淆和扩散
- 混淆原则:就是将密文、明文、密钥三者之间的统计关系和代数关系变得尽可能复杂,使得敌手即使获得了密文和明文,也无法求出密钥的任何信息;即使获得了密文和明文的统计规律,也无法求出明文的任何信息。
- 扩散原则:就是应将明文的统计规律和结构规律散射到相当长的一段统计中去。也就是说让明文中的每一位影响密文中的尽可能多的位,或者说让密文中的每一位都受到明文中的尽可能多位的影响。例如对英文消息M=m1,m2…m3 的加密操作:
其中ord(m)是求字母对应的序号,chr(i)是求序号i对应的字母,密文字母y是由明文中k个连续的字母相加而得
- Feistel密码结构:加密算法的输入是分组长为2w的明文和一个密钥K 。将每组明文分成左右两半 L0和R0 ,在进行完n轮迭代后,左右两半再合并到一起以产生密文分组。其第i轮迭代的输入为前轮输出的函数:
结构图:
国际DES
其中明文分组长为64比特,密钥长为56比特。图的左边是明文的处理过程,有三个阶段,首先是一个初始置换IP,用于重排明文分组的64比特数据。然后是具有相同功能的16轮变换,每轮中都有置换和代换运算,第16轮变换的输出分为左右两半,并被交换次序。最后再经过一个逆初始置换IP^-1(为IP的逆)从而产生64比特的密文。除初始置换和逆初始置换外,DES的结构和图3-3所示的Feistel密码结构完全相同。算法结构图:
- 初始置换与逆初始置换:表示第58位换到第一位2. 选择扩展运算:将红框中的部分重复,已达到扩展的目的
轮结构:
其中S盒如下:输入6位,输出4位
JAVA代码:
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
public class DesUtils {
private static final String DES = "DES";
private static final String ***KEY*** = "3YxxxxxxxxxZF"; //自定义
private DesUtils() {}
private static byte[] encrypt(byte[] src, byte[] key) throws Exception {
SecureRandom sr = new SecureRandom();
DESKeySpec dks = new DESKeySpec(key);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
SecretKey secretKey = keyFactory.generateSecret(dks);
Cipher cipher = Cipher.getInstance(DES);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, sr);
return cipher.doFinal(src);
}
private static byte[] decrypt(byte[] src, byte[] key) throws Exception {
SecureRandom sr = new SecureRandom();
DESKeySpec dks = new DESKeySpec(key);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
SecretKey secretKey = keyFactory.generateSecret(dks);
Cipher cipher = Cipher.getInstance(DES);
cipher.init(Cipher.DECRYPT_MODE, secretKey, sr);
return cipher.doFinal(src);
}
private static String byte2hex(byte[] b) {
String hs = "";
String temp = "";
for (int n = 0; n < b.length; n++) {
temp = (java.lang.Integer.toHexString(b[n] & 0XFF));
if (temp.length() == 1)
hs = hs + "0" + temp;
else
hs = hs + temp;
}
return hs.toUpperCase();
}
private static byte[] hex2byte(byte[] b) {
if ((b.length % 2) != 0)
throw new IllegalArgumentException("length not even");
byte[] b2 = new byte[b.length / 2];
for (int n = 0; n < b.length; n += 2) {
String item = new String(b, n, 2);
b2[n / 2] = (byte) Integer.parseInt(item, 16);
}
return b2;
}
private static String decode(String src, String key) {
String decryptStr = "";
try {
byte[] decrypt = decrypt(hex2byte(src.getBytes()), key.getBytes());
decryptStr = new String(decrypt);
} catch (Exception e) {
e.printStackTrace();
}
return decryptStr;
}
private static String encode(String src, String key){
byte[] bytes = null;
String encryptStr = "";
try {
bytes = encrypt(src.getBytes(), key.getBytes());
} catch (Exception ex) {
ex.printStackTrace();
}
if (bytes != null)
encryptStr = byte2hex(bytes);
return encryptStr;
}
/**
* 解密
*/
public static String decode(String src) {
return decode(src, KEY);
}
/**
* 加密
*/
public static String encode(String src) {
return encode(src, KEY);
}
//测试方法main
public static void main(String[] args) {
String ss = "dawng";
String encodeSS = encode(ss);
System.out.println(encodeSS);
String decodeSS = decode(encodeSS);
System.out.println(decodeSS);
}
}
AES Rijndael
线性混合层:确保多轮之上的高度扩散。
非线性层:将具有最优的“最坏情况非线性特性”的S盒并行使用。
密钥加层:单轮子密钥简单地异或到中间状态上,实现一次性掩盖。
为了使加密算法和解密算法在结构上更加接近,最后一轮的线性混合层与前面各轮的线性混合层不同,这类似于DES的最后一轮不做左右交换一样。可以证明这种设计不以任何方式提高或降低该密码的安全性。
算法的中间结果也须分组,称算法中间结果的分组为状态,所有的操作都在状态上进行。状态可以用以字节为元素的矩阵阵列表示,该阵列有4行,列数记为Nb,Nb等于分组长度除以32。
种子密钥类似地用一个以字节为元素的矩阵阵列表示,该阵列有4行,列数记为Nk,Nk等于分组长度除以32。
Rijndael的轮函数由四个不同的计算部件组成,分别是:字节代换(ByteSub)、行移位(ShiftRow)、列混合(MixColumn)、密钥加(AddRoundKey)。
-
字节代换:字节代换是非线形变换,独立地对状态的每个字节进行。代换表(即S-盒)是可逆的,由以下两个变换的合成得到:
1. 仿射变换:
2. 字节代换: -
行移位:不同状态行的位移量不同
-
列混合:将状态阵列的每个列视为GF(2^8)上的多项式,再与一个固定的多项式c(x)进行模x4+1乘法。当然要求c(x)是模x4+1可逆的多项式,否则列混合变换就是不可逆的,因而会使不同的输入分组对应的输出分组可能相同。Rijndael的设计者给出的c(x)为(系数用16进制数表示):
c(x)是与x4+1互素的,因此是模x4+1可逆的。 -
密钥加:密钥加是将轮密钥简单地与状态进行逐比特异或。轮密钥由种子密钥通过密钥编排算法得到,轮密钥长度等于分组长度Nb。
国产SM4
算法结构图:
SM4密码算法的基本运算有模2加和循环移位。
① 模2加:记为⊕,为32位逐比特异或运算。
② 循环移位:<<< i,把32位字循环左移i位。
基本密码部件
- S盒:S盒是以字节为单位的非线性替换,其密码学作用是混淆,它的输入和输出都是8位的字节。设输入字节为a ,输出字节为b, 则S盒的运算可表示为:S(a)=b
- 非线性变换r:以字为单位的,它由4个S盒并置构成
- 线性变换部件L:以字为处理单位,输入输出都是32位的字,它的密码学作用是扩散。设 的输入为字 ,输出为字 ,则
- 合成变换T:由非线性变换和线性变换复合而成,数据处理的单位是字
轮函数:轮函数由上述基本密码部件构成。设轮函数 的输入为4个32位字 ,共128位,轮密钥为一个32位的字 。输出也是一个32位的字
化简得:
加密算法可表示如下:
为了与解密算法需要的顺序一致,同时也与人们的习惯顺序一致,在加密算法之后还需要一个反序处理 :
解密算法:
与加密算法之后需要一个反序处理同样的道理,在解密算法之后也需要一个反序处理 :
密钥扩展算法
SM4算法加密时输入128位的密钥,采用32轮迭代结构,每一轮使用一个32位的轮密钥,共使用32个轮密钥。使用密钥扩展算法,从加密密钥产生出32个轮密钥。
- 常数FK
- 固定参数:共使用32个固定参数CK,每个CK是一个字,其产生规则如下:设CK<i,j>为CK的第j字节,CK=(CK(i,0),CK(i,1),CK(i,2),CK(i,3))
- 算法:
设输入的加密密钥为MK=(MK<0>,MK<1>,MK<2>,MK<3>) ,输出轮密钥为rk< i>,i∈[0,31] ,密钥扩展算法可描述如下,其中 K< i> ,i∈[0,35] 为中间数据:
与轮函数唯一不同的是线性变换部件L改为了
未完待续。。。。。后面都是大纲
公钥密码算法
公钥密码学是非对称的,它使用两个独立的密钥,即密钥分为公钥和私钥,因此称双密钥体制。双钥体制的公钥可以公开,因此称为公钥算法
国际RSA
RSA算法既可用于加密,又可用于数字签名,已得到广泛采用,并被许多标准化组织(如ISO、ITU、IETF和SWIFT等)接纳。目前许多国家标准仍采用RSA算法或它的变型。
- 费马小定理:如果p是一个质数,而整数a不是p的倍数,则有a^(p-1)≡1(mod p)。
- 欧拉算法:若r0>r1,我们可计算q使得r0=q⋅r1+r2,其中r2<r1,则gcd(r0,r1)=gcd(r1,r2)。
- 扩展欧拉算法:给定两个正整数r0和r1,存在 s和t使得s⋅r0+t⋅r1=gcd(r0,r1),其中s和t是整数。
- 欧拉函数:对于正整数m,小于m的整数中与m互质的数的数目为Φ(m),Φ称为欧拉函数。
- 欧拉定理:若a为整数,m为正整数,且a与m互质,则a^Φ(m)≡1 mod m
RSA算法的实现如下:
- 寻找出两个大素数p和q
- 计算出n=pq 和φ(n)=(p-1)(q-1)
- 选择一个随机数e,gcd(e,φ(n))=1.
- 使用辗转相除法计算d=e^-1(modφ(n))
- 在目录中公开n和e作为公钥
正确性证明见:https://blog.csdn.net/Krone_/article/details/84868865
java代码:
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import org.apache.commons.codec.binary.Base64;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class RSAUtils{
public static final String KEY_ALGORITHM="RSA";
public static final String SIGNATURE_ALGORITHM="MD5withRSA";
private static final int KEY_SIZE=1024; //长度和加密时一致
private static final String PUBLIC_KEY="RSAPublicKey";
private static final String PRIVATE_KEY="RSAPrivateKey";
public static String str_pubK = "MIGxxxxxxxxxxxxx8="; //和前端加密的私钥对应起来
/**
* 使用getPublicKey得到公钥,返回类型为PublicKey
* @param base64 String to PublicKey
* @throws Exception
*/
public static PublicKey getPublicKey(String key) throws Exception {
byte[] keyBytes;
keyBytes = (new BASE64Decoder()).decodeBuffer(key);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(keySpec);
return publicKey;
}
/**
* 转换私钥
* @param base64 String to PrivateKey
* @throws Exception
*/
public static PrivateKey getPrivateKey(String key) throws Exception {
byte[] keyBytes;
keyBytes = (new BASE64Decoder()).decodeBuffer(key);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
return privateKey;
}
//***************************签名和验证*******************************
public static byte[] sign(byte[] data) throws Exception{
PrivateKey priK = getPrivateKey(str_priK);
Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
sig.initSign(priK);
sig.update(data);
return sig.sign();
}
public static boolean verify(byte[] data,byte[] sign) throws Exception{
PublicKey pubK = getPublicKey(str_pubK);
Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
sig.initVerify(pubK);
sig.update(data);
return sig.verify(sign);
}
//************************加密解密**************************
public static byte[] encrypt(byte[] bt_plaintext)throws Exception{
PublicKey publicKey = getPublicKey(str_pubK);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] bt_encrypted = cipher.doFinal(bt_plaintext);
return bt_encrypted;
}
public static byte[] decrypt(byte[] bt_encrypted)throws Exception{
PrivateKey privateKey = getPrivateKey(str_priK);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] bt_original = cipher.doFinal(bt_encrypted);
return bt_original;
}
//********************main函数:加密解密和签名验证*********************
public static void main(String[] args) throws Exception {
String str_plaintext = "这是一段用来测试密钥转换的明文";
System.err.println("明文结果:"+str_plaintext);
byte[] bt_cipher = encrypt(str_plaintext.getBytes());
System.out.println("加密后结果:"+Base64.encodeBase64String(bt_cipher));
byte[] bt_original = decrypt(bt_cipher);
String str_original = new String(bt_original);
System.out.println("解密结果:"+str_original);
String str="被签名的内容";
System.err.println("\n原文:"+str);
byte[] signature=sign(str.getBytes());
System.out.println("产生签名值:"+Base64.encodeBase64String(signature));
boolean status=verify(str.getBytes(), signature);
System.out.println("验证结果:"+status);
}
}
国产SM2(椭圆曲线算法)
SM2算法使用的方程为:y2= x3 + ax + b
摘要算法
MD5
摘要加密(MD5):消息摘要算法的主要特征是加密过程不需要密钥,并且经过加密的数据无法被解密,目前可以被解密逆向的只有CRC32算法,只有输入相同的明文数据经过相同的消息摘要算法才能得到相同的密文。
注:项目配置一个shiro的jar即可
导入 import org.apache.shiro.crypto.hash.Md5Hash;
注册时对用户输入的密码继续MD5加密,将加密的密码存入数据库
// 密码 盐值 搅拌次数
Md5Hash mh = new Md5Hash(Password,"yanzhi",3);
Password = mh.toString();
登录时对用户登录的密码进行相同加密,用加密后的值与数据库中加密的密码继续匹配
Md5Hash mh = new Md5Hash(Password,"yanzhi",3);
Password = mh.toString();
国产SM3
广泛应用在数字签名,消息认证,数据完整性检测等领域。摘要函数通常被认为需要满足三个基本特性:碰撞稳固性,原根稳固性和第二原根稳固性。
SM3是在SHA-256基础上改进实现的一种算法。SM3算法采用Merkle-Damgard结构,消息分组长度为512位,摘要值长度为256位。