openssl 加解密以及国密算法

Posted qianbo_insist

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了openssl 加解密以及国密算法相关的知识,希望对你有一定的参考价值。

国密算法

国密即国家密码局认定的国产密码算法。主要有SM1,SM2,SM3,SM4。密钥长度和分组长度均为128位。

1、SM1 为对称加密。其加密强度与AES相当。该算法不公开,调用该算法时,需要通过加密芯片的接口进行调用。
2、SM2为非对称加密,基于ECC。该算法已公开。由于该算法基于ECC,故其签名速度与秘钥生成速度都快于RSA。ECC 256位(SM2采用的就是ECC 256位的一种)安全强度比RSA 2048位高,但运算速度快于RSA。
3、SM3 消息摘要。可以用MD5作为对比理解。该算法已公开。校验结果为256位,本身是散列算法,官方叫杂凑类算法,根据SHA256算法改进而来。
4、SM4 无线局域网标准的分组数据算法。对称加密,密钥长度和分组长度均为128位。

5、SM7适用于非接触式IC卡的对称算法,秘钥长度128bit
6、SM9是标识算法,支持加密、签名、交换。

国密芯片算法一般是对称算法,密钥不公开,当使用特定的芯片进行SM1或其他国密算法加密时,若用多个线程调用加密卡的API时,要考虑芯片对于多线程的支持情况。

生成sm2密钥对

openssl ecparam -genkey -name SM2 -out SM2PrivateKey.pem
openssl ec -in SM2PrivateKey.pem -pubout -out SM2PublicKey.pem

AES 加密

堆成算法比较成熟的是AES算法,AES加密数据块和密钥长度可以是128比特、192比特、256比特中的任意一个。实际上,国密算法一般都是根据这种算法改进产生的。

AES加密有很多轮的重复和变换。大致步骤如下:

1、密钥扩展(KeyExpansion),

2、初始轮(Initial Round),

3、重复轮(Rounds),每一轮又包括:SubBytes、ShiftRows、MixColumns、AddRoundKey,

4、最终轮(Final Round),最终轮没有MixColumns。

1、使用AES加解密数据

省略,这个比较简单

使用RSA加解密数据

RSA 主要是公钥和私钥的概念,产生一个私钥
$ openssl genrsa -out Key.pem -f4 2048
从私钥可以导出公钥
$ openssl rsa -in Key.pem -pubout -out Key_pub.pem

python 比较方便,使用python实例一下

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding

#加密函数
def encrypt(src_file_name, dst_file_name, public_key_file_name):
"""
对原始数据文件使用指定的公钥进行加密,并将加密输出到目标文件中
:param src_file_name: 原始数据文件
:param dst_file_name: 加密输出文件
:param public_key_file_name: 用于加密的公钥
:return: 加密结果的bytes数组
"""
#读取数据,需要加密的内容
data_file = open(src_file_name, 'rb')
data = data_file.read()
data_file.close()

#读取公钥文件
key_file = open(public_key_file_name, 'rb')
key_data = key_file.read()
key_file.close()

#加载公钥
public_key = serialization.load_pem_public_key(
key_data,
backend=default_backend()
)

#使用公钥对原始数据进行加密,使用PKCS#1 v1.5的填充方式
out_data = public_key.encrypt(
data,
padding.PKCS1v15()
)

#将加密结果输出到目标文件中
#write encrypted data
out_data_file = open(dst_file_name, 'wb')
out_data_file.write(out_data)
out_data_file.close()

# 返回加密结果
return out_data
# 解密函数
def decrypt(src_file_name, dst_file_name, private_key_file_name):
"""
对原始数据文件使用指定的私钥进行解密,并将结果输出到目标文件中
:param src_file_name: 原始数据文件
:param dst_file_name: 解密输出文件
:param private_key_file_name: 用于解密的私钥
:return: 解密结果的bytes数组
"""
# 读取原始数据
data_file = open(src_file_name, 'rb')
data = data_file.read()
data_file.close()

# 读取私钥数据
key_file = open(private_key_file_name, 'rb')
key_data = key_file.read()
key_file.close()

# 从私钥数据中加载私钥
private_key = serialization.load_pem_private_key(
key_data,
password=None,
backend=default_backend()
)

# 使用私钥对数据进行解密,使用PKCS#1 v1.5的填充方式
out_data = private_key.decrypt(
data,
padding.PKCS1v15()
)

# 将解密结果输出到目标文件中
out_data_file = open(dst_file_name, 'wb')
out_data_file.write(out_data)
out_data_file.close()

# 返回解密结果
return out_data

if __name__ == "__main__":
data_file_name = r'msg.bin'
encrypted_file_name = r'msg.bin.encrypted'
decrypted_file_name = r'msg.bin.decrypted'

private_key_file_name = r'Key.pem'
public_key_file_name = r'Key_pub.pem'

# 先对数据加密
data = encrypt(data_file_name, encrypted_file_name, public_key_file_name)
# 打印加密结果
print("encrypted data:")
dump_hex(data)

# 对数据进行解密
data = decrypt(encrypted_file_name, decrypted_file_name, private_key_file_name)
# 打印解密结果
print("decrypted data:")
dump_hex(data)

java AES

import java.security.Key;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

public class AESCoder 

   private static final String KEY_ALGORITHM = "AES";
   private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";//默认的加密算法

   public static byte[] initSecretKey() 

       //返回生成指定算法密钥生成器的 KeyGenerator 对象
       KeyGenerator kg = null;
       try 
           kg = KeyGenerator.getInstance(KEY_ALGORITHM);
        catch (NoSuchAlgorithmException e) 
           e.printStackTrace();
           return new byte[0];
       
       //初始化此密钥生成器,使其具有确定的密钥大小
       //AES 要求密钥长度为 128
       kg.init(128);
       //生成一个密钥
       SecretKey  secretKey = kg.generateKey();
       return secretKey.getEncoded();
   

   private static Key toKey(byte[] key)
       //生成密钥
       return new SecretKeySpec(key, KEY_ALGORITHM);
   

   public static byte[] encrypt(byte[] data,Key key) throws Exception
       return encrypt(data, key,DEFAULT_CIPHER_ALGORITHM);
   

   public static byte[] encrypt(byte[] data,byte[] key) throws Exception
       return encrypt(data, key,DEFAULT_CIPHER_ALGORITHM);
   

   public static byte[] encrypt(byte[] data,byte[] key,String cipherAlgorithm) throws Exception
       //还原密钥
       Key k = toKey(key);
       return encrypt(data, k, cipherAlgorithm);
   

   public static byte[] encrypt(byte[] data,Key key,String cipherAlgorithm) throws Exception
       //实例化
       Cipher cipher = Cipher.getInstance(cipherAlgorithm);
       //使用密钥初始化,设置为加密模式
       cipher.init(Cipher.ENCRYPT_MODE, key);
       //执行操作
       return cipher.doFinal(data);
   

   public static byte[] decrypt(byte[] data,byte[] key) throws Exception
       return decrypt(data, key,DEFAULT_CIPHER_ALGORITHM);
   

   public static byte[] decrypt(byte[] data,Key key) throws Exception
       return decrypt(data, key,DEFAULT_CIPHER_ALGORITHM);
   

   public static byte[] decrypt(byte[] data,byte[] key,String cipherAlgorithm) throws Exception
       //还原密钥
       Key k = toKey(key);
       return decrypt(data, k, cipherAlgorithm);
   

   public static byte[] decrypt(byte[] data,Key key,String cipherAlgorithm) throws Exception
       //实例化
       Cipher cipher = Cipher.getInstance(cipherAlgorithm);
       //使用密钥初始化,设置为解密模式
       cipher.init(Cipher.DECRYPT_MODE, key);
       //执行操作
       return cipher.doFinal(data);
   

   private static String  showByteArray(byte[] data)
       if(null == data)
           return null;
       
       StringBuilder sb = new StringBuilder("");
       for(byte b:data)
           sb.append(b).append(",");
       
       sb.deleteCharAt(sb.length()-1);
       sb.append("");
       return sb.toString();
   

   public static void main(String[] args) throws Exception 
       byte[] key = initSecretKey();
       System.out.println("key:"+showByteArray(key));
       Key k = toKey(key); //生成秘钥
       String data ="AES数据";
       System.out.println("加密前数据: string:"+data);
       System.out.println("加密前数据: byte[]:"+showByteArray(data.getBytes()));
       System.out.println();
       byte[] encryptData = encrypt(data.getBytes(), k);//数据加密
       System.out.println("加密后数据: byte[]:"+showByteArray(encryptData));
//       System.out.println("加密后数据: hexStr:"+Hex.encodeHexStr(encryptData));
       System.out.println();
       byte[] decryptData = decrypt(encryptData, k);//数据解密
       System.out.println("解密后数据: byte[]:"+showByteArray(decryptData));
       System.out.println("解密后数据: string:"+new String(decryptData));
   

国密加密过程

加解密都使用openssl 去做,openssl封装比较好,可以直接使用

unsigned char* t, *hm;
    BIGNUM* rand;
    EC_POINT* rG, *rK;
    BIGNUM *rKx, *rKy, *rGx, *rGy;

    unsigned char bK[65] = 0;
    unsigned char C3[33] = 0;

    rG = EC_POINT_new(this->mGroup);
    rK = EC_POINT_new(this->mGroup);
    rand = BN_new();

    BN_rand_range(rand, this->z);
    EC_POINT_mul(this->mGroup, rG, NULL,
        this->mGP, rand, this->ctx);

    rGx = BN_new();
    rGy = BN_new();
    if(!EC_POINT_get_affine_coordinates_GFp(this->mGroup, 
        rG, rGx, rGy, this->ctx))
    
        return -3;
    

    BN_bn2bin(rGx, pd);
    BN_bn2bin(rGy, &pd[32]);

    //[k]PB=(x2,y2)
    EC_POINT_mul(this->mGroup, rK, NULL, 
        EC_KEY_get0_public_key(this->mKey), 
        rand, this->ctx);

    rKx = BN_new();
    rKy = BN_new();
    if(!EC_POINT_get_affine_coordinates_GFp(this->mGroup, 
        rK, rKx, rKy, this->ctx))
    
        return -3;
    

    //t=KDF(x2||y2, klen)   
    BN_bn2bin(rKx, bK);
    BN_bn2bin(rKy, &bK[32]);

    t = new BYTE[elen + 1];
    memset(t, 0, elen + 1);

    this->mKDF(bK, 64, elen, t);

    for (int i = elen; i--;)
    
        t[i] = t[i]^pe[i];
    

    //C3 = Hash(x2||M||y2)
    hm = new unsigned char[elen + 65];
    memset(hm, 0, elen + 65);

    memcpy(hm, bK, 32);
    memcpy(&hm[32], pe, elen);
    memcpy(&hm[elen + 32], &bK[32], 32);

    hash(hm, elen + 64, C3, "sha256");

    //C = C1||C2||C3
    memcpy(&pd[64], t,  elen);
    memcpy(&pd[64 + elen], C3, 32);

    delete[] t;
    delete[] hm;

    t = NULL;
    hm = NULL;

    EC_POINT_free(rG);
    EC_POINT_free(rK);

    return 0;

国密解密过程

unsigned char* t, *c2, *hm;
    unsigned char bC1x[65] = 0;
    unsigned char bC1y[65] = 0;
    unsigned char bK[65] = 0;
    unsigned char u[33] = 0; 

    unsigned int mlen, hm_len;  

    EC_POINT *rG, *rK;
    BIGNUM *C1x, *C1y, *rKx, *rKy;

    //取出rG
    C1x = BN_new();
    C1y = BN_new();

    memcpy(&bC1x[32], pe, 32);
    memcpy(&bC1y[32], &pe[32], 32);

    BN_bin2bn(bC1x, 64, C1x);
    BN_bin2bn(bC1y, 64, C1y);

    rG = EC_POINT_new(this->mGroup);
    if(!EC_POINT_set_affine_coordinates_GFp(this->mGroup, 
        rG, C1x, C1y, this->ctx))
    
        EC_POINT_free(rG);
        return -1;
    

    //求得rK
    rK = EC_POINT_new(this->mGroup);
    EC_POINT_mul(this->mGroup, rK, NULL, rG, 
        EC_KEY_get0_private_key(this->mKey), 
        this->ctx);

    rKx = BN_new();
    rKy = BN_new();
    if(!EC_POINT_get_affine_coordinates_GFp(this->mGroup, 
        rK, rKx, rKy, this->ctx))
    
        EC_POINT_free(rG);
        EC_POINT_free(rK);
        return -2;
    

    //求取hv 解密 
    BN_bn2bin(rKx, bK);
    BN_bn2bin(rKy, &bK[32]);

    mlen = elen - 96;

    c2 = new unsigned char[mlen + 1];
    memset(c2, 0, mlen + 1);
    memcpy(c2以上是关于openssl 加解密以及国密算法的主要内容,如果未能解决你的问题,请参考以下文章

国密SM4算法加密解密实现以及与Spring Security集成实现

国密算法加密解密加签验签

国密算法的ekey的使用--简述

SM2 国密算法工具QT版,彻底搞懂国密算法的使用

OpenSSL之EVP用法

OPENSSL RSA加密与解密