前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【C++】基础:加密算法介绍与部分实现

【C++】基础:加密算法介绍与部分实现

作者头像
DevFrank
发布2024-07-24 15:12:02
1340
发布2024-07-24 15:12:02
举报
文章被收录于专栏:C++开发学习交流

一、介绍

通信加密技术是信息加密技术的重要分支,是网络安全的重要组成部分。

加密就是通过密码算法对数据进行转化,使之成为没有正确密钥任何人都无法读懂的报文。而这些以无法读懂的形式出现的数据一般被称为密文。为了读懂报文,密文必须重新转变为它的最初形式——明文,而含有以数学方式转换报文的双重密码就是密钥。

通信加密技术早期采用单向散列(hash)算法,即明文加密成密文后,不能再解密成明文。这种算法一般用来存储密码等信息,验证密码时,需要将密码转成的密文与数据库中密文进行对比。知名的单向散列算法有MD5、CRC32和SHA-256。

但如果我们只想在传输过程加密,收到数据后还想将其转换成明文怎么办呢?

为了解决上述问题,对称加密算法出现了。明文通过对称加密算法的密钥加密成密文,密文又可以通过对称加密算法的密钥解密成明文,由于加密和解密过程使用同一把密钥,所以称为对称加密。服务端和客户端之间的加密就常用到对称加密算法。知名的对称加密算法有DES和AES。

但是这个密钥放在哪里呢?如果定义一个变量,客户端和服务端各放一份,我们在发送前肯定要对明文加密,别人很容易通过OD调试器追踪到密钥;如果只放在服务端,第一次通信时发过去,那别人也可以通过对客户端抓包找到密钥;还有就是把密钥也加密发过去,那密钥的密钥总是明文吧,那就成了套娃。

为了解决以上问题,非对称加密算法出现了。非对称加密算法很神奇,它采用了一对密钥,即公钥和私钥。对一串明文,如果你用公钥加密,就只能用私钥解密,而用私钥加密,就只能用公钥解密。但非对称加密过程很复杂,所以处理很慢,比对称加密慢几百倍,用于网络传输不现实。知名的非对称加密算法有RSA和ECC。

对称加密不能确保安全,非对称加密不能保证效率,那怎么办呢?

为了解决这个问题,混合加密策略出现了,即用非对称加密来解决对称加密的密钥传输,密钥传输完成后,A和B间采用对称加密算法通信。对称加密算法的密钥由服务端A生成,A把对称加密算法的密钥用B的公钥加密,B收到后用自己的私钥解密,即“公钥加密,私钥解密”,得到对称加密算法的密钥,AB都有对称加密的密钥了,双方就可以正常进行对称加密通信了。混合加密策略解决了窃听的问题,此外,为了防止被逆向跟踪,最好再搭配反调试+异步回调+多线程+时钟扰乱,增大逆向难度。

除了窃听,常见的安全问题还有篡改和冒充。

要解决这个问题,就来到了下面的数字签名技术。数字签名技术使用了hash算法和非对称加密算法。利用私钥加密,公钥如果能成功解密得到摘要,可以证明发送方的身份,防止冒充;对消息内容进行hash,得到摘要,经过摘要对比,可以检测是否被篡改。

服务端A向客户端B发送数据:先对明文使用hash算法,生成摘要(digest),对这个摘要用A的私钥(唯一性,防止冒充)加密,生成数字签名(signature),将“明文+数字签名”发送给B。

客户端B接收数据:对数字签名用A的公钥解密,得到摘要1;再对明文用hash算法,得到摘要2;对比摘要1和摘要2,若两者一致,说明数据确实是A发过来的。

问题都解决了吗?还没有。非对称加密时要先将A和B的公钥发送给对方,在这个公钥分发的过程中,可能会出现“中间人劫持”现象,即中间人C截获A和B的公钥,并将自己伪装成B和A分别和A和B通信,这样中间人C获得了完整的数据。

那么如何安全地分发公钥呢?即证明A的公钥确实是A的,B的公钥确实是B的呢?

现实世界中有公证处,而网络世界中也有这样一种具有公信力的组织,叫CA,它可以给服务端A颁发一个数字证书,上面有A的信息,包含A的公钥,这样如果拿到了证书,也就拿到了可信任的A的公钥。

那么,证书要怎么安全传输呢? 如果证书被篡改了怎么办? 现在的情况是,需要一种既要能判断公钥是否冒充,还要能判断数据是否被篡改的手段, 这不就是刚刚介绍的数字签名技术。具体操作其实就是把数字签名中的"明文"部分替换为 “数字证书”。客户端B向服务端A发送请求时,服务端A会返回自己数字证书给客户端B。CA公钥是内置在客户电脑中的。

以上就是对通信加密技术的简单介绍。

为了信息安全,我国也开发了国产密码算法,简称国密算法,是由国家密码局认定的拥有自主知识产权的密码算法,在目前应用广泛的有SM2、SM3、SM4

目前为止,共计有近200多种加密算法,按国际惯例,将这近200种算法按照双方收发的密钥是否相同的标准划分为两大类:一种是常规算法(也叫私钥加密算法或对称加密算法),其特征是收信方和发信方使用相同的密钥,即加密密钥和解密密钥是相同或等价的。常规密码的优点是有很强的保密强度,且经受住时间的检验和攻击,但其密钥必须通过安全的途径传送。因此,其密钥管理成为系统安全的重要因素。知名的私钥加密算法有:DES、3DES和AES。

另一种是公钥加密算法(也叫非对称加密算法),其特征是收信方和发信方使用的密钥互不相同,而且几乎不可能从加密密钥推导解密密钥。比较著名的公钥密码算法有:RSA、DSA和ECC等。公钥密码的优点是可以适应网络的开放性要求,且密钥管理问题也较为简单,尤其可方便的实现数字签名和验证。但其算法复杂,加密数据的速率较低。

纵观这两种算法,一个从DES到3DES再到ADES,一个从RSA到ECC。其发展角度无不是从密钥的简单性,成本的低廉性,管理的简易性,算法的复杂性,保密的安全性以及计算的快速性这几个方面去考虑。因此,未来算法的发展也必定是从这几个角度出发的,而且在实际操作中往往把这两种算法结合起来,或许将来一种集两种算法有点于一身的新型算法将会出现,到那时,数据通信必将更加快捷与安全。

1. 单向散列/哈希算法

单向散列函数(也称Hash函数)指的是根据输入消息计算后,输出固定长度数值的算法,输出数值也称为“散列值”或“消息摘要”,其长度通常在128~256位之间。

常用的单向散列算法有MD5、SHA-x系列、CRC系列等

MD5算法是指输入任意长度的信息,经过处理,输出为128位的信息(数字指纹);不同的输入得到不同的结果(唯一性);根据128位的输出结果不可能反推出输入的信息(不可逆)。对安全性要求不高的软件常用MD5算法。

CRC也叫循环冗余校验码,是数据通信领域常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算。我们都知道,反码求和法用的是加法,而CRC算法用的是“除法”,这里不是十进制除法,而是二进制的异或取余。CRC8最终的余数是8位,如果不满8位需要在高位补0,CRC32最终的余数则是32位。车辆通信报文校验常用CRC算法。

SHA算法是在MD4算法的基础上演变而来,由美国国家安全局设计,有SHA-1和SHA-2两个系列。安全性要求一般时采用SHA-1,安全性要求较高时则至少采用SHA-256算法,SHA-256是指任意长度的消息文件,通过SHA-256算法加密,最终得到的密文都是256位(32字节),通常用一个长度为64的十六进制字符串来表示。https签名算法使用的是带RSA加密的SHA-256算法。

SM3算法:哈希算法,适用于商用密码应用中的数字签名和验证消息认证码的生成与验证以及随机数的生成,可满足多种密码应用的安全需求。用于替换MD5、SHA-1等国际算法。为了保证杂凑算法的安全性,其产生的杂凑值的长度不应太短,例如MD5输出128比特杂凑值,输出长度太短,影响其安全性。SHA-1算法的输出长度为160比特,SM3算法的输出长度为256比特,因此SM3算法的安全性要高于MD5算法和SHA-1算法。

2. 对称加密算法

对称加密算法是应用较早的加密算法。在对称加密算法中,数据发信方将明文和加密密钥一起经过特殊加密算法处理后,使其变成复杂的加密密文发送出去。收信方收到密文后,若想解读原文,则需要使用加密用过的密钥及相同算法的逆算法对密文进行解密,才能使其恢复成可读明文。在对称加密算法中,使用的密钥只有一个,发收信双方都使用这个密钥对数据进行加密和解密,这就要求解密方事先必须知道加密密钥。

DES是IBM公司1977年为美国政府研制的一种算法。DES是以56位密钥为基础的密码块加密技术。它的加密过程一般如下:

  1. 一次性把64位明文块打乱置换;
  2. 把64位明文块拆成两个32位块;
  3. 用机密DES密钥把每个32位块打乱位置16次;
  4. 使用初始置换的逆置换。

但后来,DES被容易地破解,因此,美国推出DES的改进版本——三重加密(triple Data Encryption Standard,3DES)即在使用过程中,收发双方都用三把密钥进行加解密,无疑这种3*56式的加密算法大大提升了密码的安全性,按现在计算机的运算速度,这种破解几乎是不可能的。但是我们在为数据提供强有力的安全保护的同时,也要花更多的时间来对信息进行三次加密和对每个密层进行解密。同时在这种前提下,使用这种密钥的双发都必须拥有3个密钥,如果丢失了其中任何一把,其余两把都成了无用的密钥。这样私钥的数量一下又提升了3倍,这显然不是我们想看到的。

于是美国国家标准与技术研究所推出了一个新的保密措施来保护金融交易。高级加密标准(Advanced Encryption Standard,AES)美国国家技术标准委员会(NIST)在2000年10月选定了比利时的研究成果“Rijndael”作为AES的基础。AES内部有更简洁精确的数学算法,而加密数据只需一次通过。AES被设计成高速,坚固的安全性能,而且能够支持各种小型设备。AES与3DES相比,不仅是安全性能有重大差别,使用性能和资源有效利用上也有很大差别。

SM4算法:对称加密算法。主要用于无线局域网标准,用于替换DES/AES等算法。要保证一个对称密码算法的安全性的基本条件是其具备足够的密钥长度,SM4算法与AES算法具有相同的密钥长度分组长度128比特,因此在安全性上高于3DES算法。

3. 非对称加密算法

非对称加密算法是一种密钥的保密方法。非对称加密算法需要两个密钥:公开密钥 (publickey:简称公钥)和私有密钥(privatekey:简称私钥)。公钥与私钥是一对,如果用公钥对数据进行加密,只有用对应的私钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法称为非对称加密算法。

面对在执行过程中如何使用和分享密钥及保持其机密性等问题,1975年Whitefield Diffe和Marti Hellman提出了公开的密钥密码技术的概念,被称为Diffie-Hellman技术。从此公钥加密算法便产生了。

由于采取了公共密钥,密钥的管理和分发就变得简单多了,对于一个n个用户的网络来说,只需要2n个密钥便可达到密度。同时使得公钥加密法的保密性全部集中在及其复杂的数学问题上,它的安全性因而也得到了保证。但是在实际运用中,公共密钥加密算法并没有完全的取代私钥加密算法。其重要的原因是它的实现速度远远赶不上私钥加密算法。又因为它的安全性,所以常常用来加密一些重要的文件。

根据所基于的数学难题来分类,有以下三类系统目前被认为是安全和有效的:大整数因子分解系统(代表性的有RSA)、椭圆曲线离散对数系统(ECC)和离散对数系统(代表性的有DSA)。

RSA系统是公钥系统的最具有典型意义的算法,大多数使用公钥密码进行加密和数字签名的产品和标准使用的都是RSA算法。它的具体算法如下:

  1. 找两个非常大的质数,越大越安全。把这两个质数叫做P和Q;
  2. 找一个能满足下列条件得数字E:A.是一个奇数;B.小于P×Q;C.与(P-1)×(Q-1)互质,只是指E和该方程的计算结果没有相同的质数因子;
  3. 计算出数值D,满足下面性质:((D×E)-1)能被(P-1)×(Q-1)整除。

公开密钥对是(P×Q,E);私人密钥是D;公开密钥是E。 解密函数是: 假设T是明文,C是密文。 加密函数用公开密钥E和模P×Q; 加密信息=(TE)模P×Q。 解密函数用私人密钥D和模P×Q; 解密信息=(CD)模P×Q。 椭圆曲线加密技术(ECC)是建立在单向函数(椭圆曲线离散对数)的基础上,由于它比RAS使用得离散对数要复杂得多,而且该单向函数比RSA得要难,所以与RSA相比,它有安全性高、计算量小、存储空间占用小、占用带宽低等优点。

在实现非对称加密RSA算法前,服务端A和客户端B都要先用openssl(开源密码库)生成自己的公钥和私钥。命令如下:

代码语言:javascript
复制
genrsa -out private_key.pem 2048 # 制作私钥
rsa -in private_key.pem -pubout -out public_key.pem # 制作公钥

注意,公钥是公开的,因此程序开始A和B会传输各自的公钥给对方。

通过RSA算法传输数据的流程如下:

A发送数据给B:A先把明文用B的公钥进行加密,这样除了B的私钥,其他都解不了密,B收到后,用自己的私钥解密,得到明文。

B发送数据给A:B把明文用A的公钥进行加密,A收到后,用自己的私钥解密得到明文。

规则就是:你要发给谁,就用谁的公钥加密,这样他收到后,只有他自己的私钥能解密。

远程ssh连接git账户时,生成密钥的算法就是RSA算法。

SM2算法:非对称加密算法。基于椭圆曲线密码(ECC)的公钥密码算法标准,提供数字签名、密钥交换和公钥加密。用于替换RSA等国际算法。SM2算法与RSA算法不同的是,SM2算法是基于椭圆曲线上点群离散对数难题,相对于RSA算法,256位的SM2密码强度已经比2048位的RSA密码强度要高。

二、加密算法实现

1. 造轮子

自己编写相关加密算法。

AES算法

一个基于C++的AES实现:https://github.com/Urban82/Aes256 aes.h

代码语言:javascript
复制
#ifndef AES_H
#define AES_H

/**
 * 参数 p: 明文的字符串数组。
 * 参数 plen: 明文的长度,长度必须为16的倍数。
 * 参数 key: 密钥的字符串数组。
 */
void aes(char *p, int plen, char *key);

/**
 * 参数 c: 密文的字符串数组。
 * 参数 clen: 密文的长度,长度必须为16的倍数。
 * 参数 key: 密钥的字符串数组。
 */
void deAes(char *c, int clen, char *key);

#endif

aes.cpp

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aes.h"

void aes(char *p, int plen, char *key);
void deAes(char *c, int clen, char *key);
/**
 * S盒
 */
static const int S[16][16] = { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
	0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
	0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
	0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
	0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
	0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
	0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
	0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
	0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
	0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
	0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
	0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
	0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
	0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
	0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };

/**
 * 逆S盒
 */
static const int S2[16][16] = { 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
	0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
	0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
	0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
	0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
	0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
	0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
	0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
	0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
	0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
	0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
	0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
	0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
	0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
	0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
	0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };

/**
 * 获取int数据的低8位的左4个位
 */
static int getLeft4Bit(int num) {
	int left = num & 0x000000f0;
	return left >> 4;
}

/**
 * 获取int数据的低8位的右4个位
 */
static int getRight4Bit(int num) {
	return num & 0x0000000f;
}
/**
 * 根据索引,从S盒中获得元素
 */
static int getNumFromSBox(int index) {
	int row = getLeft4Bit(index);
	int col = getRight4Bit(index);
	return S[row][col];
}

/**
 * 把一个字符转变成整型
 */
static int getIntFromChar(char c) {
	int result = (int) c;
	return result & 0x000000ff;
}

/**
 * 把16个字符转变成4X4的数组,
 * 该矩阵中字节的排列顺序为从上到下,
 * 从左到右依次排列。
 */
static void convertToIntArray(char *str, int pa[4][4]) {
	int k = 0;
	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++) {
			pa[j][i] = getIntFromChar(str[k]);
			k++;
		}
}

/**
 * 打印4X4的数组
 */
static void printArray(int a[4][4]) {
	for(int i = 0; i < 4; i++){
		for(int j = 0; j < 4; j++)
			printf("a[%d][%d] = 0x%x ", i, j, a[i][j]);
		printf("\n");
	}
	printf("\n");
}

/**
 * 打印字符串的ASSCI,
 * 以十六进制显示。
 */
static void printASSCI(char *str, int len) {
	for(int i = 0; i < len; i++)
		printf("0x%x ", getIntFromChar(str[i]));
	printf("\n");
}

/**
 * 把连续的4个字符合并成一个4字节的整型
 */
static int getWordFromStr(char *str) {
	int one = getIntFromChar(str[0]);
	one = one << 24;
	int two = getIntFromChar(str[1]);
	two = two << 16;
	int three = getIntFromChar(str[2]);
	three = three << 8;
	int four = getIntFromChar(str[3]);
	return one | two | three | four;
}

/**
 * 把一个4字节的数的第一、二、三、四个字节取出,
 * 入进一个4个元素的整型数组里面。
 */
static void splitIntToArray(int num, int array[4]) {
	int one = num >> 24;
	array[0] = one & 0x000000ff;
	int two = num >> 16;
	array[1] = two & 0x000000ff;
	int three = num >> 8;
	array[2] = three & 0x000000ff;
	array[3] = num & 0x000000ff;
}

/**
 * 将数组中的元素循环左移step位
 */
static void leftLoop4int(int array[4], int step) {
	int temp[4];
	for(int i = 0; i < 4; i++)
		temp[i] = array[i];

	int index = step % 4 == 0 ? 0 : step % 4;
	for(int i = 0; i < 4; i++){
		array[i] = temp[index];
		index++;
		index = index % 4;
	}
}

/**
 * 把数组中的第一、二、三和四元素分别作为
 * 4字节整型的第一、二、三和四字节,合并成一个4字节整型
 */
static int mergeArrayToInt(int array[4]) {
	int one = array[0] << 24;
	int two = array[1] << 16;
	int three = array[2] << 8;
	int four = array[3];
	return one | two | three | four;
}

/**
 * 常量轮值表
 */
static const unsigned int Rcon[10] = { 0x01000000, 0x02000000,
	0x04000000, 0x08000000,
	0x10000000, 0x20000000,
	0x40000000, 0x80000000,
	0x1b000000, 0x36000000 };
/**
 * 密钥扩展中的T函数
 */
static int T(int num, int round) {
	int numArray[4];
	splitIntToArray(num, numArray);
	leftLoop4int(numArray, 1);//字循环

	//字节代换
	for(int i = 0; i < 4; i++)
		numArray[i] = getNumFromSBox(numArray[i]);

	int result = mergeArrayToInt(numArray);
	return result ^ Rcon[round];
}

//密钥对应的扩展数组
static int w[44];

/**
 * 扩展密钥,结果是把w[44]中的每个元素初始化
 */
static void extendKey(char *key) {
	for(int i = 0; i < 4; i++)
		w[i] = getWordFromStr(key + i * 4);

	for(int i = 4, j = 0; i < 44; i++) {
		if( i % 4 == 0) {
			w[i] = w[i - 4] ^ T(w[i - 1], j);
			j++;//下一轮
		}else {
			w[i] = w[i - 4] ^ w[i - 1];
		}
	}

}

/**
 * 轮密钥加
 */
static void addRoundKey(int array[4][4], int round) {
	int warray[4];
	for(int i = 0; i < 4; i++) {

		splitIntToArray(w[ round * 4 + i], warray);

		for(int j = 0; j < 4; j++) {
			array[j][i] = array[j][i] ^ warray[j];
		}
	}
}

/**
 * 字节代换
 */
static void subBytes(int array[4][4]){
	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++)
			array[i][j] = getNumFromSBox(array[i][j]);
}

/**
 * 行移位
 */
static void shiftRows(int array[4][4]) {
	int rowTwo[4], rowThree[4], rowFour[4];
	//复制状态矩阵的第2,3,4行
	for(int i = 0; i < 4; i++) {
		rowTwo[i] = array[1][i];
		rowThree[i] = array[2][i];
		rowFour[i] = array[3][i];
	}
	//循环左移相应的位数
	leftLoop4int(rowTwo, 1);
	leftLoop4int(rowThree, 2);
	leftLoop4int(rowFour, 3);

	//把左移后的行复制回状态矩阵中
	for(int i = 0; i < 4; i++) {
		array[1][i] = rowTwo[i];
		array[2][i] = rowThree[i];
		array[3][i] = rowFour[i];
	}
}

/**
 * 列混合要用到的矩阵
 */
static const int colM[4][4] = { 2, 3, 1, 1,
	1, 2, 3, 1,
	1, 1, 2, 3,
	3, 1, 1, 2 };

static int GFMul2(int s) {
	int result = s << 1;
	int a7 = result & 0x00000100;

	if(a7 != 0) {
		result = result & 0x000000ff;
		result = result ^ 0x1b;
	}

	return result;
}

static int GFMul3(int s) {
	return GFMul2(s) ^ s;
}

static int GFMul4(int s) {
	return GFMul2(GFMul2(s));
}

static int GFMul8(int s) {
	return GFMul2(GFMul4(s));
}

static int GFMul9(int s) {
	return GFMul8(s) ^ s;
}

static int GFMul11(int s) {
	return GFMul9(s) ^ GFMul2(s);
}

static int GFMul12(int s) {
	return GFMul8(s) ^ GFMul4(s);
}

static int GFMul13(int s) {
	return GFMul12(s) ^ s;
}

static int GFMul14(int s) {
	return GFMul12(s) ^ GFMul2(s);
}

/**
 * GF上的二元运算
 */
static int GFMul(int n, int s) {
	int result;

	if(n == 1)
		result = s;
	else if(n == 2)
		result = GFMul2(s);
	else if(n == 3)
		result = GFMul3(s);
	else if(n == 0x9)
		result = GFMul9(s);
	else if(n == 0xb)//11
		result = GFMul11(s);
	else if(n == 0xd)//13
		result = GFMul13(s);
	else if(n == 0xe)//14
		result = GFMul14(s);

	return result;
}
/**
 * 列混合
 */
static void mixColumns(int array[4][4]) {

	int tempArray[4][4];

	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++)
			tempArray[i][j] = array[i][j];

	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++){
			array[i][j] = GFMul(colM[i][0],tempArray[0][j]) ^ GFMul(colM[i][1],tempArray[1][j]) 
				^ GFMul(colM[i][2],tempArray[2][j]) ^ GFMul(colM[i][3], tempArray[3][j]);
		}
}
/**
 * 把4X4数组转回字符串
 */
static void convertArrayToStr(int array[4][4], char *str) {
	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++)
			*str++ = (char)array[j][i];	
}
/**
 * 检查密钥长度
 */
static int checkKeyLen(int len) {
	if(len == 4)
		return 1;
	else
		return 0;
}

/**
 * 参数 p: 明文的字符串数组。
 * 参数 plen: 明文的长度。
 * 参数 key: 密钥的字符串数组。
 */
void aes(char *p, int plen, char *key){

	int keylen = strlen(key);
	if(plen == 0 || plen % 4 != 0) {
		printf("明文字符长度必须为4的倍数!\n");
		exit(0);
	}

	if(!checkKeyLen(keylen)) {
		printf("密钥字符长度错误!长度必须为16、24和32。当前长度为%d\n",keylen);
		exit(0);
	}

	extendKey(key);//扩展密钥
	int pArray[4][4];

	for(int k = 0; k < plen; k += 16) {	
		convertToIntArray(p + k, pArray);

		addRoundKey(pArray, 0);//一开始的轮密钥加

		for(int i = 1; i < 10; i++){//前9轮

			subBytes(pArray);//字节代换

			shiftRows(pArray);//行移位

			mixColumns(pArray);//列混合

			addRoundKey(pArray, i);

		}

		//第10轮
		subBytes(pArray);//字节代换

		shiftRows(pArray);//行移位

		addRoundKey(pArray, 10);

		convertArrayToStr(pArray, p + k);
	}
}
/**
 * 根据索引从逆S盒中获取值
 */
static int getNumFromS1Box(int index) {
	int row = getLeft4Bit(index);
	int col = getRight4Bit(index);
	return S2[row][col];
}
/**
 * 逆字节变换
 */
static void deSubBytes(int array[4][4]) {
	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++)
			array[i][j] = getNumFromS1Box(array[i][j]);
}
/**
 * 把4个元素的数组循环右移step位
 */
static void rightLoop4int(int array[4], int step) {
	int temp[4];
	for(int i = 0; i < 4; i++)
		temp[i] = array[i];

	int index = step % 4 == 0 ? 0 : step % 4;
	index = 3 - index;
	for(int i = 3; i >= 0; i--) {
		array[i] = temp[index];
		index--;
		index = index == -1 ? 3 : index;
	}
}

/**
 * 逆行移位
 */
static void deShiftRows(int array[4][4]) {
	int rowTwo[4], rowThree[4], rowFour[4];
	for(int i = 0; i < 4; i++) {
		rowTwo[i] = array[1][i];
		rowThree[i] = array[2][i];
		rowFour[i] = array[3][i];
	}

	rightLoop4int(rowTwo, 1);
	rightLoop4int(rowThree, 2);
	rightLoop4int(rowFour, 3);

	for(int i = 0; i < 4; i++) {
		array[1][i] = rowTwo[i];
		array[2][i] = rowThree[i];
		array[3][i] = rowFour[i];
	}
}
/**
 * 逆列混合用到的矩阵
 */
static const int deColM[4][4] = { 0xe, 0xb, 0xd, 0x9,
	0x9, 0xe, 0xb, 0xd,
	0xd, 0x9, 0xe, 0xb,
	0xb, 0xd, 0x9, 0xe };

/**
 * 逆列混合
 */
static void deMixColumns(int array[4][4]) {
	int tempArray[4][4];

	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++)
			tempArray[i][j] = array[i][j];

	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++){
			array[i][j] = GFMul(deColM[i][0],tempArray[0][j]) ^ GFMul(deColM[i][1],tempArray[1][j]) 
				^ GFMul(deColM[i][2],tempArray[2][j]) ^ GFMul(deColM[i][3], tempArray[3][j]);
		}
}
/**
 * 把两个4X4数组进行异或
 */
static void addRoundTowArray(int aArray[4][4],int bArray[4][4]) {
	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 4; j++)
			aArray[i][j] = aArray[i][j] ^ bArray[i][j];
}
/**
 * 从4个32位的密钥字中获得4X4数组,
 * 用于进行逆列混合
 */
static void getArrayFrom4W(int i, int array[4][4]) {
	int index = i * 4;
	int colOne[4], colTwo[4], colThree[4], colFour[4];
	splitIntToArray(w[index], colOne);
	splitIntToArray(w[index + 1], colTwo);
	splitIntToArray(w[index + 2], colThree);
	splitIntToArray(w[index + 3], colFour);

	for(int i = 0; i < 4; i++) {
		array[i][0] = colOne[i];
		array[i][1] = colTwo[i];
		array[i][2] = colThree[i];
		array[i][3] = colFour[i];
	}

}

/**
 * 参数 c: 密文的字符串数组。
 * 参数 clen: 密文的长度。
 * 参数 key: 密钥的字符串数组。
 */
void deAes(char *c, int clen, char *key) {

	int keylen = strlen(key);
	if(clen == 0 || clen % 4 != 0) {
		printf("密文字符长度必须为4的倍数!现在的长度为%d\n",clen);
		exit(0);
	}

	if(!checkKeyLen(keylen)) {
		printf("密钥字符长度错误!长度必须为16、24和32。当前长度为%d\n",keylen);
		exit(0);
	}

	extendKey(key);//扩展密钥
	int cArray[4][4];
	for(int k = 0; k < clen; k += 16) {
		convertToIntArray(c + k, cArray);


		addRoundKey(cArray, 10);

		int wArray[4][4];
		for(int i = 9; i >= 1; i--) {
			deSubBytes(cArray);

			deShiftRows(cArray);

			deMixColumns(cArray);
			getArrayFrom4W(i, wArray);
			deMixColumns(wArray);

			addRoundTowArray(cArray, wArray);
		}

		deSubBytes(cArray);

		deShiftRows(cArray);

		addRoundKey(cArray, 0);

		convertArrayToStr(cArray, c + k);

	}
}

参考main.cpp实现:

代码语言:javascript
复制
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <iostream>

#include "aes.h"

#define MAXLEN 1024

//获取字符串
void getString(char *str, int len){
	int slen = read(0, str, len);
	for(int i = 0; i < slen; i++,str++){
		if(*str == '\n'){
			*str = '\0';
			break;
		}
	}
}

//输出ASCII
void printASCII(char *str, int len) {
	int c;
	for(int i = 0; i < len; i++) {
		c = (int)*str++;
		c = c & 0x000000ff;//保留低8位
		printf("0x%x ", c);
	}
	printf("\n");
}

/**
 * 从标准输入中读取用户输入的字符串
 */
void readPlainText(char *str, int *len) {
	int plen;
	while(1) {
		getString(str, MAXLEN);
		plen = strlen(str);
		if(plen != 0 && plen % 4 == 0) {
			printf("你输入的明文为:%s\n", str);
			break;
		}else{
			printf("明文字符长度必须为4的倍数,现在的长度为%d\n", plen);
		}
	}
	*len = plen;
}
/**
 * 把字符串写进文件
 */
void writeStrToFile(char *str, int len, char *fileName) {
	FILE *fp;
	fp = fopen(fileName, "wb");
	for(int i = 0; i < len; i++)
		putc(str[i], fp);
	fclose(fp);
}

//字符串写进文件
void aesStrToFile(char *key) {

	char p[MAXLEN];
	int plen;
	printf("请输入你的明文,明文字符长度必须为4的倍数\n");
	readPlainText(p,&plen);
	printf("进行AES加密..................\n");

	aes(p, plen, key);//AES加密

	printf("加密完后的明文的ASCII为:\n");
	printASCII(p, plen);
	char fileName[64];
	printf("请输入你想要写进的文件名,比如'test.txt':\n");
	if(scanf("%s", fileName) == 1) {	
		writeStrToFile(p, plen, fileName);
		printf("已经将密文写进%s中了,可以在运行该程序的当前目录中找到它。\n", fileName);
	}
}
/**
 * 从文件中读取字符串
 */
int readStrFromFile(char *fileName, char *str) {
	FILE *fp = fopen(fileName, "rb");
	if(fp == NULL) {
		printf("打开文件出错,请确认文件存在当前目录下!\n");
		exit(0);
	}

	int i;
	for(i = 0; i < MAXLEN && (str[i] = getc(fp)) != EOF; i++);

	if(i >= MAXLEN) {
		printf("解密文件过大!\n");
		exit(0);
	}

	str[i] = '\0';
	fclose(fp);
	return i;
}

//解密文件
void deAesFile(char *key) {
	char fileName[64];
	char c[MAXLEN];//密文字符串
	printf("请输入要解密的文件名,该文件必须和本程序在同一个目录\n");
	if(scanf("%s", fileName) == 1) {
		int clen = readStrFromFile(fileName, c);
		printf("开始解密.........\n");
		deAes(c, clen, key);
		printf("解密后的明文ASCII为:\n");
		printASCII(c, clen);
		printf("明文为:%s\n", c);
		writeStrToFile(c,clen,fileName);
		printf("现在可以打开%s来查看解密后的密文了!\n",fileName);
	}
}

//加密文件
void aesFile(char *key) {
	char fileName[64];
	char fileP[MAXLEN];

	printf("请输入要加密的文件名,该文件必须和本程序在同一个目录\n");
	if(scanf("%s", fileName) == 1) {
		readStrFromFile(fileName, fileP);
		int plen = strlen(fileP);
		printf("开始加密.........\n");
		printf("加密前文件中字符的ASCII为:\n");
		printASCII(fileP, plen);

		aes(fileP, plen, key);//开始加密

		printf("加密后的密文ASCII为:\n");
		printASCII(fileP, plen);
		writeStrToFile(fileP,plen,fileName);
		printf("已经将加密后的密文写进%s中了\n",fileName);
	}
}

int main(int argc, char const *argv[]) {
	clock_t start,finish;
	start = clock();

	char key[17];
	printf("请输入4个字符的密钥:\n");
	int klen;
	while(1){
		getString(key,17);
		klen = strlen(key);
		if(klen != 4){
			printf("请输入4个字符的密钥,当前密钥的长度为%d\n",klen);
		}else{
			printf("你输入的密钥为:%s\n",key);
			break;
		}
	}

	printf("输入's'表示要加密输入的字符串,并将加密后的内容写入到文件\n");
	printf("请输入要功能选项并按回车,输入'f'表示要加密文件\n");
	printf("输入'p'表示要解密文件\n");
	char c;
	if(scanf("%s",&c) == 1) {
		if(c == 's')
			aesStrToFile(key);//用AES加密字符串,并将字符串写进文件中
		else if(c == 'p')
			deAesFile(key);//把文件中的密文解密,并写回文件中
		else if(c == 'f')//用AES加密文件
			aesFile(key);
	}

	finish = clock();
	std::cout << std::endl<<"the time cost is:" << double(finish - start) / CLOCKS_PER_SEC << std::endl;

	return 0;
}

编译: Windows可在VS2017下不依赖其他库实现; Linux编译g++ -o aes -static aes.cpp main.cpp

SM4算法

一个SM的算法:https://github.com/tonyonce2017/SM4

代码语言:javascript
复制
#ifndef _SM4_H
#define _SM4_H
#include<stdio.h>
#include<time.h>
#include<iomanip>
#include<stdlib.h>

typedef unsigned int word;

// 密钥扩展
// param:   
//		mk[4]: 加密密钥
// return: 轮密钥
word* keyExpansion(word mk[4]);

// 迭代
// param¿
//		x[4]: 加密结果
//		mk[4]:密钥 128 bits
//		flag: true:加密  flase:解密
void encryption(word x[4], word mk[4], bool flag);

// sm4加密,CBC模式
// param¿
//		result:加密结果
// 		arr	  :待加密数据
//		bits  :待加密数据的bit数
// 		mk[4] :密钥,128 bits
// return: 加密结果result的bit数
unsigned int sm4_encryption(word*& result, word*& arr, unsigned int bits, word mk[4]);

// sm4解密,CBC模式
// param¿ 
//		result: 解密结果
//		arr   : 待解密数据
//		bits  : 待解密数据的bit数
//      mk[4] : 密钥,128 bits
// return: 解密结果result的bit数
unsigned int sm4_decryption(word*& result, word*& arr, unsigned int bits, word mk[4]);



const word S[] = {
	0xD6, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6, 0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05, //0
	0x2B, 0x67, 0x9A, 0x76, 0x2A, 0xBE, 0x04, 0xC3, 0xAA, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99, //1
	0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A, 0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62, //2
	0xE4, 0xB3, 0x1C, 0xA9, 0xC9, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA, 0x75, 0x8F, 0x3F, 0xA6, //3
	0x47, 0x07, 0xA7, 0xFC, 0xF3, 0x73, 0x17, 0xBA, 0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0x4F, 0xA8, //4
	0x68, 0x6B, 0x81, 0xB2, 0x71, 0x64, 0xDA, 0x8B, 0xF8, 0xEB, 0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35, //5
	0x1E, 0x24, 0x0E, 0x5E, 0x63, 0x58, 0xD1, 0xA2, 0x25, 0x22, 0x7C, 0x3B, 0x01, 0x21, 0x78, 0x87, //6
	0xD4, 0x00, 0x46, 0x57, 0x9F, 0xD3, 0x27, 0x52, 0x4C, 0x36, 0x02, 0xE7, 0xA0, 0xC4, 0xC8, 0x9E, //7
	0xEA, 0xBF, 0x8A, 0xD2, 0x40, 0xC7, 0x38, 0xB5, 0xA3, 0xF7, 0xF2, 0xCE, 0xF9, 0x61, 0x15, 0xA1, //8
	0xE0, 0xAE, 0x5D, 0xA4, 0x9B, 0x34, 0x1A, 0x55, 0xAD, 0x93, 0x32, 0x30, 0xF5, 0x8C, 0xB1, 0xE3, //9
	0x1D, 0xF6, 0xE2, 0x2E, 0x82, 0x66, 0xCA, 0x60, 0xC0, 0x29, 0x23, 0xAB, 0x0D, 0x53, 0x4E, 0x6F, //A
	0xD5, 0xDB, 0x37, 0x45, 0xDE, 0xFD, 0x8E, 0x2F, 0x03, 0xFF, 0x6A, 0x72, 0x6D, 0x6C, 0x5B, 0x51, //B
	0x8D, 0x1B, 0xAF, 0x92, 0xBB, 0xDD, 0xBC, 0x7F, 0x11, 0xD9, 0x5C, 0x41, 0x1F, 0x10, 0x5A, 0xD8, //C
	0x0A, 0xC1, 0x31, 0x88, 0xA5, 0xCD, 0x7B, 0xBD, 0x2D, 0x74, 0xD0, 0x12, 0xB8, 0xE5, 0xB4, 0xB0, //D
	0x89, 0x69, 0x97, 0x4A, 0x0C, 0x96, 0x77, 0x7E, 0x65, 0xB9, 0xF1, 0x09, 0xC5, 0x6E, 0xC6, 0x84, //E
	0x18, 0xF0, 0x7D, 0xEC, 0x3A, 0xDC, 0x4D, 0x20, 0x79, 0xEE, 0x5F, 0x3E, 0xD7, 0xCB, 0x39, 0x48  //F
};

#define ShiftLeft(x,k) (x>>(32-k)|(x<<k))
#define L(x) (x^ShiftLeft(x,2)^ShiftLeft(x,10)^ShiftLeft(x,18)^ShiftLeft(x,24))//¿¿¿¿L
#define LL(x) (x^ShiftLeft(x,13)^ShiftLeft(x,23))//¿¿¿¿¿¿¿¿¿
#define F(x) (T(x))
#define lamda(x) ((S[x >> 24 & 0xff] << 24) | (S[x >> 16 & 0xff] << 16) | (S[x >> 8 & 0xff] << 8) | S[x & 0xff])//¿¿¿¿¿

//密钥扩展函数
word* keyExpansion(word mk[4]) {
	word* rk = new word[32];
	word Fk[4] = { 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc };
	word CK[32], k[4];
	for (int i = 0; i < 32; i++) {
		CK[i] = 0;
		for (int j = 0; j < 4; j++)
			CK[i] = CK[i] * 256 + ((4 * i + j) * 7) % 256;
	}
	for (int i = 0; i < 4; i++) {
		k[i] = mk[i] ^ Fk[i];
	}
	word temp;
	for (int i = 0; i < 32; i++) {
		temp = k[1] ^ k[2] ^ k[3] ^ CK[i];
		temp = lamda(temp);
		temp = k[0] ^ LL(temp);
		k[0] = k[1]; k[1] = k[2]; k[2] = k[3]; k[3] = temp;
		rk[i] = temp;
	}
	return rk;
}

//迭代
void encryption(word x[4], word mk[4], bool flag) {
	word temp;
	word* rk = keyExpansion(mk);
	for (int i = 0; i < 32; i++) {
		temp = x[1] ^ x[2] ^ x[3];
		if (flag == false) temp ^= rk[31 - i];
		else temp ^= rk[i];
		temp = lamda(temp);
		temp = L(temp) ^ x[0];
		x[0] = x[1]; x[1] = x[2]; x[2] = x[3]; x[3] = temp;
	}
	temp = x[0]; x[0] = x[3]; x[3] = temp;
	temp = x[1]; x[1] = x[2]; x[2] = temp;
	delete[] rk;
}

//sm4加密函数,CBC模式
unsigned int sm4_encryption(word*& result, word*& arr, unsigned int bits, word mk[4]) {//return bits
	word* temp;
	unsigned int temp_size;
	unsigned int s = 128 - bits % 128;//s:填充的比特数
	if (s == 0)s = 128;
	else if (s == 1)s += 128;
	temp_size = 4 + ((bits + s) >> 5);

	temp = new unsigned int[temp_size];

	int i;
	for (i = 4; i < 4 + ((bits + 31) >> 5); i++)
		temp[i] = arr[i - 4];
	for (; i < temp_size; i++)
		temp[i] = 0;
	temp[i - 1] |= s;//用零填充,最后8比特用s填充

	srand((int)time(0));
	for (i = 0; i < 4; i++)
		temp[i] = rand();//前128比特为初始向量
	result = new unsigned int[temp_size];
	for (i = 0; i < 4; i++)
		result[i] = temp[i];
	for (i = 4; i < temp_size; i += 4) {
		result[i] = temp[i] ^ result[i - 4];
		result[i + 1] = temp[i + 1] ^ result[i - 3];
		result[i + 2] = temp[i + 2] ^ result[i - 2];
		result[i + 3] = temp[i + 3] ^ result[i - 1];
		encryption(&result[i], mk, true);
	}
	return temp_size * 32;
}

//sm4解密,CBC模式
unsigned int sm4_decryption(word*& result, word*& arr, unsigned int bits, word mk[4]) {
	unsigned int size = bits / 32 - 4;
	result = new unsigned int[size];
	unsigned int i;
	for (i = 0; i < size; i += 4) {
		result[i] = arr[i + 4];
		result[i + 1] = arr[i + 5];
		result[i + 2] = arr[i + 6];
		result[i + 3] = arr[i + 7];
		encryption(&result[i], mk, false);
		result[i] ^= arr[i];
		result[i + 1] ^= arr[i + 1];
		result[i + 2] ^= arr[i + 2];
		result[i + 3] ^= arr[i + 3];
	}
	i = result[size - 1] & 0xff;
	return size * 32 - i;
}

#endif
RSA算法
代码语言:javascript
复制
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include<string.h>
#include <math.h>
#include<algorithm>
#include <time.h>

using namespace std;
typedef long long ll;

// e是公钥
// d是私钥
 
ll e, d, n;

ll gcd(ll a, ll b)  //求最大公约数
{
    ll c = 0;
    if(a<b) swap(a,b);
    c = b;
    do
    {
        b = c;
        c = a%b;
        a = b;
    }
    while (c != 0);
    return b;
}

// 0不是 1是 
ll isPrime(ll i) //判断i是否是素数
{
    ll flag=0;
    for(ll a=2; a<i; a++)
    {
        if(i%a==0)
        {
            flag=1;
            break;
        }
    }
    if(flag==1) return 0;
    else return 1;
    
}

ll myPow(ll a, ll b, ll n)  //求a^b mod n
{
    ll y;

    /*使用二进制平方乘法计算 pow(a,b) % n*/
    y=1;

    while(b != 0)
    {
        /*对于b中的每个1,累加y*/

        if(b & 1)
            y = (y*a) % n;

        /*对于b中的每一位,计算a的平方*/
        a = (a*a) % n;

        /*准备b中的下一位*/
        b = b>>1;
    }

    return y;
}

void extgcd(ll a,ll b,ll& d,ll& x,ll& y) 
{
    if(!b)
    {
        d=a;
        x=1;
        y=0;
    }
    else
    {
        extgcd(b,a%b,d,y,x);
        y-=x*(a/b);
    }
}

ll ModularInverse(ll a,ll b)  //获取(1/a)mod b的结果
{
    ll d,x,y;
    extgcd(a,b,d,x,y);
    return d==1?(x+b)%b:-1;
    
}

void KeyGeneration()  //获取公钥密钥
{
    ll p, q;
    ll phi_n;

    do
    {
        do
            p = rand();
        while (p % 2 == 0);

    }
    while (!isPrime(p)); 	// 得到素数 p 
    do
    {
        do
            q = rand();
        while (q % 2 == 0);
    }
    while (!isPrime(q)); 	// 得到素数q 

    n = p * q;
    phi_n = (p - 1) * (q - 1);

    do
        e = rand() % (phi_n - 2) + 2; // 1 < e < phi_n
    while (gcd(e, phi_n) != 1);

    d = ModularInverse(e,phi_n);
}

// 一位一位地输出加密的结果 
ll Encryption(ll value)  //加密
{
    ll cipher;
    cipher = myPow(value, e, n);
    cout<<cipher;
    return cipher;
}

// 一位一位地输出解 密的结果 
void Decryption(ll value)  //解密
{
    ll decipher;
    decipher = myPow(value, d, n);
   	cout<<decipher;
}
int main()
{
	clock_t start,finish;
	start = clock();
	// 对6位的数字进行稳定加密 
	ll num;
	cout<<"请输入要加密的明文数字,enter结束"<<endl;
	//while(cin>>num){
	cin >> num;
		ll de;
		cout<<"输入的明文为"<<num<<endl; 
	    KeyGeneration();  //获取公钥密钥
	    cout<<"加密密钥:"<<e<<endl; 
	    cout<<"加密结果为:";
		de = Encryption( num );
		cout<<"\n私钥为:"<<d;
//		cout<<"de="<<de<<endl;
		cout<<"\n解密结果"; 
		Decryption(de);	
		cout<<"\n-------------"<<endl;
	//}
	finish = clock();
	cout <<endl<<"the time cost is:" << double(finish - start) / CLOCKS_PER_SEC<<endl;
    return 0;
}
MD5
代码语言:javascript
复制
#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <time.h>

using namespace std;

#define A 0x67452301
#define B 0xefcdab89
#define C 0x98badcfe
#define D 0x10325476

const char str16[] = "0123456789abcdef";

const unsigned int T[] = {
    0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
    0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,
    0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,
    0x6b901122,0xfd987193,0xa679438e,0x49b40821,
    0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,
    0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
    0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,
    0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,
    0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,
    0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,
    0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
    0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,
    0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,
    0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,
    0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,
    0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391 };//行*列16*4

const unsigned int s[] = { 7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,
                                                    5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
                                                    4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,
                                                    6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21};//行*列4*16

class MD5 {
private:
    unsigned int tempA, tempB, tempC, tempD, strlength;
public:
    MD5() {
        tempA = A;
        tempB = B;
        tempC = C;
        tempD = D;
        strlength = 0;
    }
    // F函数
    unsigned int F(unsigned int b, unsigned int c, unsigned int d) {
        return (b & c) | ((~b) & d);
    }
    // G函数
    unsigned int G(unsigned int b, unsigned int c, unsigned int d) {
        return (b & d) | (c & (~d));
    }
    // H函数
    unsigned int H(unsigned int b, unsigned int c, unsigned int d) {
        return b ^ c ^ d;
    }
    // I函数
    unsigned int I(unsigned int b, unsigned int c, unsigned int d) {
        return c ^ (b | (~d));
    }
    // 移位操作函数
    unsigned int shift(unsigned int a, unsigned int n) {
        return (a << n) | (a >> (32 - n));
    }
    // 编码函数(重要)
    string encode(string src) {
        vector<unsigned int> rec = padding(src);//填充字符串
        for(unsigned int i = 0; i < strlength/16; i++) {
            unsigned int num[16];
            for(int j = 0; j < 16; j++) {
                num[j] = rec[i*16+j];
            }
            iterateFunc(num, 16);//循环压缩
        }
        return format(tempA) + format(tempB) + format(tempC) + format(tempD);//整理输出
    }
    // 循环压缩
    void iterateFunc(unsigned int* X, int size = 16) {
        unsigned int a = tempA,
                     b = tempB,
                     c = tempC,
                     d = tempD,
                     rec = 0,
                     g, k;
        for(int i = 0; i < 64; i++) {
            if(i < 16) {
                // F迭代
                g = F(b, c, d);
                k = i;
            }
            else if(i < 32) {
                // G迭代
                g = G(b, c, d);
                k = (1 + 5*i) % 16;
            }
            else if(i < 48) {
                // H迭代
                g = H(b, c, d);
                k = (5 + 3*i) % 16;
            }
            else {
                // I迭代
                g = I(b, c, d);
                k = (7*i) % 16;
            }
            rec = d;
            d = c;
            c = b;
            b = b + shift(a + g + X[k] + T[i], s[i]);
            a = rec;
        }
        tempA += a;
        tempB += b;
        tempC += c;
        tempD += d;
    }
    // 填充字符串
    vector<unsigned int> padding(string src) {
        // 以512位,64个字节为一组
        unsigned int num = ((src.length() + 8) / 64) + 1;
        vector<unsigned int> rec(num*16);
        strlength = num*16;
        for(unsigned int i = 0; i < src.length(); i++){
            // 一个unsigned int对应4个字节,保存4个字符信息
            rec[i>>2] |= (int)(src[i]) << ((i % 4) * 8);
        }
        // 补充1000...000
        rec[src.length() >> 2] |= (0x80 << ((src.length() % 4)*8));
        // 填充原文长度
        rec[rec.size()-2] = (src.length() << 3);
        return rec;
    }
    // 整理输出
    string format(unsigned int num) {
        string res = "";
        unsigned int base = 1 << 8;
        for(int i = 0; i < 4; i++) {
            string tmp = "";
            unsigned int b = (num >> (i * 8)) % base & 0xff;
            for(int j = 0; j < 2; j++) {
                tmp = str16[b%16] + tmp;
                b /= 16;
            }
            res += tmp;
        }
        return res;
    }
};

int main() {
	clock_t start,finish;
	start = clock();

    MD5 test;
    string a = "";
    cout << "请输入密码: ";
    getline(cin,a);
    cout << "MD5单向加密result: " << test.encode(a) << endl;

	finish = clock();
	cout <<endl<<"the time cost is:" << double(finish - start) / CLOCKS_PER_SEC<<endl;
}

//解密网站:https://www.cmd5.com/
SHA-1
代码语言:javascript
复制
#include <iostream>
#include <string>
#include <bitset>
#include <math.h>
#include <time.h>

using namespace std;

long long pad[75] = { 0 };
int lenth_pad = 0;

bitset<32> Kt[4] = { 0x5A827999,0x6ED9EBA1,0x8F1BBCDC,0xCA62C1D6 };
bitset<32> Ht[5] = { 0x67452301,0xEFCDAB89,0x98BADCFE,0x10325476,0xC3D2E1F0 };

//Ft函数
bitset<32> Ft(int t, bitset<32> B, bitset<32> C, bitset<32> D)
{
	if (0 <= t && t <= 19)
	{
		return ((B&C) | ((~B)&D));
	}
	else if (20 <= t && t <= 39)
	{
		return (B^C^D);
	}
	else if (40 <= t && t <= 59)
	{
		return ((B&C) | (B&D) | (C&D));
	}
	else if (60 <= t && t <= 79)
	{
		return (B^C^D);
	}
}

//字符串的拓展函数
void SHA_PAD(string x)
{
	long long l = x.length();
	long long i = 0;
	pad[i] = 128;
	i++;
	l += 1;
	if ((l * 8) % 512 != 448)
	{
		for (; (l * 8) % 512 != 448; l++)
		{
			i++;
		}
	}
	l = x.length();
	l = l * 8;
	int a = 1;
	pad[i + 7] = l % 256;
	while (l / 256 > 0)
	{
		l = l / 256;
		pad[i + 7 - a] = l % 256;
		a++;
	}
	for (; a < 8; a++)
	{
		pad[i + 7 - a] = 0;
	}
	lenth_pad = i + 7;
}

//循环左移函数
bitset<32> ROTL(int s, bitset<32> W)
{
	bitset<32>m = W << s;
	for (int i = 0; i < s; i++)
	{
		m[i] = W[32 - s + i];
	}
	return m;
}

//模2^32加函数
bitset<32> ADD(bitset<32>a, bitset<32>b)
{
	bitset<32> c;
	int d = 0;
	for (int i = 0; i < 32; i++)
	{
		c[i] = (a[i] + b[i] + d) % 2;
		if (a[i] + b[i] + d >= 2)
		{
			d = 1;
		}
		else if (a[i] + b[i] + d < 2)
		{
			d = 0;
		}
	}
	return c;
}

//循环体
void DO(bitset<512> M)
{
	bitset<32> Wt[80] = { 0 };
	for (int i = 0; i < 16; i++)
	{
		for (int n = 0; n < 32; n++)
		{
			Wt[i][n] = M[(15 - i) * 32 + n];
		}
	}
	for (int t = 16; t <= 79; t++)
	{
		Wt[t] = ROTL(1, Wt[t - 3] ^ Wt[t - 8] ^ Wt[t - 14] ^ Wt[t - 16]);
	}
	bitset<32> A = Ht[0];
	bitset<32> B = Ht[1];
	bitset<32> C = Ht[2];
	bitset<32> D = Ht[3];
	bitset<32> E = Ht[4];
	for (int t = 0; t <= 79; t++)
	{
		bitset<32> temp = ADD(ADD(ADD(ROTL(5, A), Ft(t, B, C, D)), ADD(E, Wt[t])), Kt[t/20]);
		E = D;
		D = C;
		C = ROTL(30, B);
		B = A;
		A = temp;
	}
	Ht[0] = ADD(Ht[0], A);
	Ht[1] = ADD(Ht[1], B);
	Ht[2] = ADD(Ht[2], C);
	Ht[3] = ADD(Ht[3], D);
	Ht[4] = ADD(Ht[4], E);
}

int main()
{
  clock_t start,finish;
  start = clock();

	string x;
	cout << "please enter the text:";
	getline(cin, x);
	SHA_PAD(x);
	long long l = 0;
	//循环体
	while (l < x.length())
	{
		long long i = l;
		string Sx;
		for (; (i < 64 + l) && (i < x.length()); i++)
		{
			bitset<8> mark = int(x[i]);
			Sx += mark.to_string();
		}
		l += 64;
		if (i == x.length())
		{
			if(lenth_pad<=63)
				for (int n = 0; n <= lenth_pad; n++)
				{
					bitset<8> mark = pad[n];
					Sx += mark.to_string();
				}
			else
			{
				int n = 0;
				for (; i < l; i++)
				{
					bitset<8> mark = pad[n];
					n++;
					Sx += mark.to_string();
				}
				bitset<512>Mx(Sx);
				DO(Mx);
				string last_Sx;
				for (; n <= lenth_pad; n++)
				{
					bitset<8> mark = pad[n];
					last_Sx += mark.to_string();
				}
				bitset<512>last_Mx(last_Sx);
				DO(last_Mx);
				break;
			}
		}
		bitset<512> Mx(Sx);
		DO(Mx);
	}
	
	//输出
	cout << "The hash of text is:";
	for (int i = 0; i < 5; i++)
	{
		string s = Ht[i].to_string();
		for (int n = 0; n < 32;)
		{
			int result = 0;
			for (int m = 0; m < 4; m++)
			{
				result += (s[n + m] - '0')*pow(2, 3 - m);
			}
			cout << hex << result;
			n += 4;
		}
	}

	cout << "\n";
	//getchar();
  finish = clock();
  cout << "the time cost is: " << double(finish-start) / CLOCKS_PER_SEC << endl;
	return 0;
}
sm3
代码语言:javascript
复制
#include <iostream>
#include <string>
#include <cmath>
#include <time.h>
using namespace std;

//二进制转换为十六进制函数实现
string BinToHex(string str) {
	string hex = "";//用来存储最后生成的十六进制数
	int temp = 0;//用来存储每次四位二进制数的十进制值
	while (str.size() % 4 != 0) {//因为每四位二进制数就能够成为一个十六进制数,所以将二进制数长度转换为4的倍数
		str = "0" + str;//最高位添0直到长度为4的倍数即可
	}
	for (int i = 0; i < str.size(); i += 4) {
		temp = (str[i] - '0') * 8 + (str[i + 1] - '0') * 4 + (str[i + 2] - '0') * 2 + (str[i + 3] - '0') * 1;//判断出4位二进制数的十进制大小为多少
		if (temp < 10) {//当得到的值小于10时,可以直接用0-9来代替
			hex += to_string(temp);
		}
		else {//当得到的值大于10时,需要进行A-F的转换
			hex += 'A' + (temp - 10);
		}
	}
	return hex;
}

//十六进制转换为二进制函数实现
string HexToBin(string str) {
	string bin = "";
	string table[16] = { "0000","0001","0010","0011","0100","0101","0110","0111","1000","1001","1010","1011","1100","1101","1110","1111" };
	for (int i = 0; i < str.size(); i++) {
		if (str[i] >= 'A'&&str[i] <= 'F') {
			bin += table[str[i] - 'A' + 10];
		}
		else {
			bin += table[str[i] - '0'];
		}
	}
	return bin;
}

//二进制转换为十进制的函数实现
int BinToDec(string str) {
	int dec = 0;
	for (int i = 0; i < str.size(); i++) {
		dec += (str[i] - '0')*pow(2, str.size() - i - 1);
	}
	return dec;
}

//十进制转换为二进制的函数实现
string DecToBin(int str) {
	string bin = "";
	while (str >= 1) {
		bin = to_string(str % 2) + bin;
		str = str / 2;
	}
	return bin;
}

//十六进制转换为十进制的函数实现
int HexToDec(string str) {
	int dec = 0;
	for (int i = 0; i < str.size(); i++) {
		if (str[i] >= 'A'&&str[i] <= 'F') {
			dec += (str[i] - 'A' + 10)*pow(16, str.size() - i - 1);
		}
		else {
			dec += (str[i] - '0')*pow(16, str.size() - i - 1);
		}
	}
	return dec;
}

//十进制转换为十六进制的函数实现
string DecToHex(int str) {
	string hex = "";
	int temp = 0;
	while (str >= 1) {
		temp = str % 16;
		if (temp < 10 && temp >= 0) {
			hex = to_string(temp) + hex;
		}
		else {
			hex += ('A' + (temp - 10));
		}
		str = str / 16;
	}
	return hex;
}

string padding(string str) {//对数据进行填充 
	string res = "";
	for (int i = 0; i < str.size(); i++) {//首先将输入值转换为16进制字符串
		res += DecToHex((int)str[i]);
	}
	cout << "输入字符串的ASCII码表示为:" << endl;
	for (int i = 0; i < res.size(); i++) {
		cout << res[i];
		if ((i + 1) % 8 == 0) {
			cout << "  ";
		}
		if ((i + 1) % 64 == 0 || (i + 1) == res.size()) {
			cout << endl;
		}
	}
	cout << endl;
	int res_length = res.size() * 4;//记录的长度为2进制下的长度
	res += "8";//在获得的数据后面添1,在16进制下相当于是添加8
	while (res.size() % 128 != 112) {
		res += "0";//“0”数据填充
	}
	string res_len = DecToHex(res_length);//用于记录数据长度的字符串
	while (res_len.size() != 16) {
		res_len = "0" + res_len;
	}
	res += res_len;
	return res;
}

string LeftShift(string str, int len) {//实现循环左移len位功能
	string res = HexToBin(str);
	res = res.substr(len) + res.substr(0, len);
	return BinToHex(res);
}

string XOR(string str1, string str2) {//实现异或操作
	string res1 = HexToBin(str1);
	string res2 = HexToBin(str2);
	string res = "";
	for (int i = 0; i < res1.size(); i++) {
		if (res1[i] == res2[i]) {
			res += "0";
		}
		else {
			res += "1";
		}
	}
	return BinToHex(res);
}

string AND(string str1, string str2) {//实现与操作
	string res1 = HexToBin(str1);
	string res2 = HexToBin(str2);
	string res = "";
	for (int i = 0; i < res1.size(); i++) {
		if (res1[i] == '1' && res2[i] == '1') {
			res += "1";
		}
		else {
			res += "0";
		}
	}
	return BinToHex(res);
}

string OR(string str1, string str2) {//实现或操作
	string res1 = HexToBin(str1);
	string res2 = HexToBin(str2);
	string res = "";
	for (int i = 0; i < res1.size(); i++) {
		if (res1[i] == '0' && res2[i] == '0') {
			res += "0";
		}
		else {
			res += "1";
		}
	}
	return BinToHex(res);
}

string NOT(string str) {//实现非操作
	string res1 = HexToBin(str);
	string res = "";
	for (int i = 0; i < res1.size(); i++) {
		if (res1[i] == '0') {
			res += "1";
		}
		else {
			res += "0";
		}
	}
	return BinToHex(res);
}

char binXor (char str1, char str2) {//实现单比特的异或操作
	return str1 == str2 ? '0' : '1';
}

char binAnd(char str1, char str2) {//实现单比特的与操作
	return (str1 == '1'&&str2 == '1') ? '1' : '0';
}

string ModAdd(string str1, string str2) {//mod 2^32运算的函数实现
	string res1 = HexToBin(str1);
	string res2 = HexToBin(str2);
	char temp = '0';
	string res = "";
	for (int i = res1.size() - 1; i >= 0; i--) {
		res = binXor(binXor(res1[i], res2[i]), temp) + res;
		if (binAnd(res1[i], res2[i]) == '1') {
			temp = '1';
		}
		else {
			if (binXor(res1[i], res2[i]) == '1') {
				temp = binAnd('1', temp);
			}
			else {
				temp = '0';
			}
		}
	}
	return BinToHex(res);
}

string P1(string str) {//实现置换功能P1(X)
	return XOR(XOR(str, LeftShift(str, 15)), LeftShift(str, 23));
}

string P0(string str) {//实现置换功能P0(X)
	return XOR(XOR(str, LeftShift(str, 9)), LeftShift(str, 17));
}

string T(int j) {//返回Tj常量值的函数实现
	if (0 <= j && j <= 15) {
		return "79CC4519";
	}
	else {
		return "7A879D8A";
	}
}

string FF(string str1, string str2, string str3, int j) {//实现布尔函数FF功能
	if (0 <= j && j <= 15) {
		return XOR(XOR(str1, str2), str3);
	}
	else {
		return OR(OR(AND(str1, str2), AND(str1, str3)), AND(str2, str3));
	}
}

string GG(string str1, string str2, string str3, int j) {//实现布尔函数GG功能
	if (0 <= j && j <= 15) {
		return XOR(XOR(str1, str2), str3);
	}
	else {
		return OR(AND(str1, str2), AND(NOT(str1), str3));
	}
}
string extension(string str) {//消息扩展函数
	string res = str;//字符串类型存储前68位存储扩展字W值
	for (int i = 16; i < 68; i++) {//根据公式生成第17位到第68位的W值
		res += XOR(XOR(P1(XOR(XOR(res.substr((i-16)*8,8), res.substr((i - 9) * 8, 8)), LeftShift(res.substr((i - 3) * 8, 8), 15))), LeftShift(res.substr((i - 13) * 8, 8), 7)), res.substr((i - 6) * 8, 8));
	}
	cout << "扩展后的消息:" << endl;
	cout << "W0,W1,……,W67的消息:" << endl;
	for (int i = 0; i < 8; i++) {
		for (int j = 0; j < 8; j++) {
			cout << res.substr(i * 64 + j * 8, 8) << "  ";
		}
		cout << endl;
	}
	cout << res.substr(512, 8) << "  " << res.substr(520, 8) << "  " << res.substr(528, 8) << "  " << res.substr(536, 8) << endl;
	cout << endl;
	for (int i = 0; i < 64; i++) {//根据公式生成64位W'值
		res += XOR(res.substr(i * 8, 8), res.substr((i + 4) * 8, 8));
	}
	cout << "W0',W1',……,W63'的消息:" << endl;
	for (int i = 0; i < 8; i++) {
		for (int j = 0; j < 8; j++) {
			cout << res.substr(544+i * 64 + j * 8, 8) << "  ";
		}
		cout << endl;
	}
	cout << endl;
	return res;
}

string compress(string str1, string str2) {//消息压缩函数
	string IV = str2;
	string A = IV.substr(0, 8), B = IV.substr(8, 8), C = IV.substr(16, 8), D = IV.substr(24, 8), E = IV.substr(32, 8), F = IV.substr(40, 8), G = IV.substr(48, 8), H = IV.substr(56, 8);
	string SS1 = "", SS2 = "", TT1 = "", TT2 = "";
	cout << "迭代压缩中间值: " << endl;
	cout << "    A         B         C         D         E         F        G         H " << endl;
	cout << A << "  " << B << "  " << C << "  " << D << "  " << E << "  " << F << "  " << G << "  " << H << endl;
	for (int j = 0; j < 64; j++) {
		SS1 = LeftShift(ModAdd(ModAdd(LeftShift(A, 12), E), LeftShift(T(j), (j%32))), 7);
		SS2 = XOR(SS1, LeftShift(A, 12));
		TT1 = ModAdd(ModAdd(ModAdd(FF(A, B, C, j), D), SS2), str1.substr((j + 68) * 8, 8));
		TT2 = ModAdd(ModAdd(ModAdd(GG(E, F, G, j), H), SS1), str1.substr(j * 8, 8));
		D = C;
		C = LeftShift(B, 9);
		B = A;
		A = TT1;
		H = G;
		G = LeftShift(F, 19);
		F = E;
		E = P0(TT2);
		cout << A << "  " << B << "  " << C << "  " << D << "  " << E << "  " << F << "  " << G << "  " << H << endl;
	}
	string res = (A + B + C + D + E + F + G + H);
	cout << endl;
	return res;
}

string iteration(string str) {//迭代压缩函数实现
	int num = str.size() / 128;
	cout << "消息经过填充之后共有 " + to_string(num) + " 个消息分组。" << endl;
	cout << endl;
	string V = "7380166F4914B2B9172442D7DA8A0600A96F30BC163138AAE38DEE4DB0FB0E4E";
	string B = "", extensionB = "", compressB = "";
	for (int i = 0; i < num; i++) {
		cout << "第 " << to_string(i+1) << " 个消息分组:" << endl;
		cout << endl;
		B = str.substr(i * 128, 128);
		extensionB = extension(B);
		compressB = compress(extensionB, V);
		V = XOR(V, compressB);
	}
	return V;
}

int main() {//主函数
    clock_t start, finish;
    //clock_t为CPU时钟计时单元数
    start = clock();
    //clock()函数返回此时CPU时钟计时单元数
	string str[2];
	str[0] = "abcdabcdabcdabcd";
	str[1] = "1234567812345678";
	for (int num = 0; num < 2; num++) {
		cout << "示例 " + to_string(num + 1) + " :输入消息为字符串: " + str[num] << endl;
		cout << endl;
		string paddingValue = padding(str[num]);
		cout << "填充后的消息为:" << endl;
		for (int i = 0; i < paddingValue.size() / 64; i++) {
			for (int j = 0; j < 8; j++) {
				cout << paddingValue.substr(i * 64 + j * 8, 8) << "  ";
			}
			cout << endl;
		}
		cout << endl;
		string result = iteration(paddingValue);
		cout << "杂凑值:" << endl;
		for (int i = 0; i < 8; i++) {
			cout << result.substr(i * 8, 8) << "  ";
		}
		cout << endl;
		cout << endl;
	}

    finish = clock();
    //clock()函数返回此时CPU时钟计时单元数
    cout <<endl<<"the time cost is:" << double(finish - start) / CLOCKS_PER_SEC<<endl;
    //finish与start的差值即为程序运行花费的CPU时钟单元数量,再除每秒CPU有多少个时钟单元,即为程序耗时
	return 0;
}
2. openssl

OpenSSL is written in C, and has an Apache style license. It is distinguished by its support for the SSL and TLS protocols, as well as a family of command line applications.

openssl现在的状态不佳,自从心脏出血事件之后,可谓是墙倒众人推,网上也有好多人说它的代码写的很差,没有良好的代码规范,基于这些理由,又是项目中使用的,所以,还是不去冒这个险了。

地址:http://www.openssl.org/

OpenSSL是一个安全套接字层密码库,其包括常用的密码算法、常用的密钥生成和证书封装管理功能及SSL协议,并提供了丰富的应用程序以供测试。

OpenSSL 是一个开源项目,其组成主要包括一下三个组件:

  • openssl:多用途的命令行工具
  • libcrypto:加密算法库
  • libssl:加密模块应用库,实现了ssl及tls

openssl安装参考:https://blog.csdn.net/weixin_39274753/article/details/107958283

apt安装:sudo apt-get install openssl

查看版本:openssl version -a

GMSSL是北京大学开发的国密算法库,基于OpenSSL分支,安装参考:http://t.csdn.cn/BNtu5

在最新的OpenSSL 1.1.1以上版本,已经对国密算法提供了支持。windows中安装预编译版本即可。

OpenSSL、GmSSL这俩提供的国密算法库,代码实现是不一样的,属于条条大路通罗马。

在安装GmSSL时,编译成静态库,并安装到指定的目录下,如/usr/local/gmssl,与系统中OpenSSL路径保持独立。

代码语言:javascript
复制
cd GmSSL-master/
./config --prefix=/usr/local/gmssl --openssldir=/usr/local/gmssl no-shared
make
make install
vim ~/.bashrc
export PATH=$PATH:/usr/local/gmssl/bin
source ~/.bashrc
gmssl version -a

应用如下:

代码语言:javascript
复制
1.单向散列加密应用
OpenSSL单向加密的子命令为dgst,其语法如下:
openssl dgst [-md5|-md4|-md2|-sha1|-sha|-mdc2|-ripemd160|-dss1] [-c] [-d] [-hex] [-binary] [-out filename] [-sign filename] [-keyform arg] [-passin arg] [-verify filename] [-prverify filename] [-signature filename] [-hmac key] [file…]
其常用的选项为:
[-md5|-md4|-md2|-sha1|-sha|-mdc2|-ripemd160|-dss1]:指定一种单向加密算法;
-out FILENAME:将加密的内容保存到指定的文件中;
单向加密除了 openssl dgst 工具还有: md5sum,sha1sum,sha224sum,sha256sum ,sha384sum,sha512sum
生成指定文件的特征码案例:openssl dgst -md5 /tmp/fstab
md5sum /tmp/fstab
echo hello,world | md5sum
echo hello,world | openssl dgst -md5
2.对称加密算法应用
OpenSSL一共提供了8种对称加密算法,其中7种是分组加密算法,仅有的一种流加密算法是RC4。这7种分组加密算法分别是AES、DES、Blowfish、CAST、IDEA、RC2、RC5。
	利用OpenSSL作对称加密需要使用其子命令enc,其用法为:
openssl enc -ciphername [-in filename] [-out filename] [-pass arg] [-e] [-d] [-a/-base64] [-A] [-k password] [-kfile filename] [-K key] [-iv IV] [-S salt] [-salt] [-nosalt] [-z] [-md] [-p] [-P] [-bufsize number] [-nopad] [-debug] [-none] [-engine id]
其中常用的选项为:
-e:加密;
-d:解密;
-ciphername:ciphername为相应的对称加密算法名字,如-des3、-ase128、-cast、-blowfish等等。
-a/-base64:使用base-64位编码格式;
-salt:自动插入一个随机数作为文件内容加密,默认选项;
-in FILENAME:指定要加密的文件的存放路径;
-out FILENAME:指定加密后的文件的存放路径;
加密字符串案例:echo "hello,world" | openssl enc -aes128 -e -a –salt -in
加解密文件案例:openssl enc -des3 -e -a -in /etc/fstab -out /tmp/fstab
openssl enc -d -des3 -a -salt -in /tmp/fstab
3.非对称加密算法应用
OpenSSL一共实现了4种非对称加密算法,包括DH算法、RSA算法、DSA算法和椭圆曲线算法(EC)。DH算法一般用户密钥交换。RSA算法既可以用于密钥交换,也可以用于数字签名。
利用openssl命令的子命令genrsa生成私钥,然后再使用子命令rsa私钥中提取公钥。
genrsa的语法如下:
openssl genrsa [-out filename] [-passout arg] [-des] [-des3] [-idea] [-f4] [-3] [-rand file(s)] [-engine id] [numbits]
常用选项:
-out FILENAME:将生成的私钥保存至指定的文件中;
[-des] [-des3] [-idea]:指定加密算法;
numbits:指明生成的私钥大小,默认是512;
通常来说秘钥文件的权限一般只能由管理员访问,因此可以结合umask命令来设置生成的密钥文件的权限,如:
(umask 077;openssl genrsa -out CA.key 4096)
随后可利用rsa子命令生成的私钥文件中提取公钥,rsa子命令的语法为:
openssl rsa [-inform PEM|NET|DER] [-outform PEM|NET|DER] [-in filename] [-passin arg] [-out filename] [-passout arg] [-sgckey] [-des] [-des3] [-idea] [-text] [-noout] [-modulus] [-check] [-pubin] [-pubout] [-engine id]
常用选项为:
-in FILENAME:指明私钥文件的存放路径;
-out FILENAME:指明将公钥的保存路径;
-pubout:根据提供的私钥,从中提取出公钥;
使用案例:openssl rsa -pubout -in CA.key
利用公钥加密、私钥解密数据:
使用rsautl进行加密和解密操作,语法如下:
openssl rsautl [-in file] [-out file] [-inkey file] [-pubin] [-certin] [-sign] [-verify] [-encrypt] [-decrypt] [-pkcs] [-ssl] [-raw] [-hexdump] [-asn1parse]     
openssl rsautl -h
Usage: rsautl [options]
-in file        input file 输入文件
-out file       output file 输出文件
-inkey file     input key 指定私有密钥文件,格式是RSA私有密钥文件
-keyform arg    private key format - default PEM 指定私钥格式
-pubin          input is an RSA public 指定输入的是RSA公钥
-certin         input is a certificate carrying an RSA public key 指定输入的是证书文件
-ssl            使用SSLv2的填充方式
-raw            不进行填充
-pkcs           使用V1.5的填充方式(默认)
-oaep           使用OAEP的填充方式
-sign           使用私钥做签名
-verify         使用公钥认证签名
-encrypt        使用公钥加密
-decrypt        使用私钥解密
-hexdump        以16进制打印
-engine e       指定三方库或者硬件设备
-passin arg    pass phrase source 传递密码来源
操作示例:
/*创建需要加密的文件*/
echo "123456789 hello world!" > plain.txt
/*生成RSA密钥,采用des3对称加密私钥*/
openssl genrsa -des3 -passout pass:123456 -out RSA.pem
/*提取公钥*/
openssl rsa -in RSA.pem -passin pass:123456 -pubout -out pub.pem
/*使用RSA作为密钥进行加密,实际上使用其中的公钥进行加密*/
openssl rsautl -encrypt -in plain.txt -inkey RSA.pem -passin pass:123456 -out enc.txt
/*使用RSA作为密钥进行解密,实际上使用其中的私钥进行解密*/
openssl rsautl -decrypt -in enc.txt -inkey RSA.pem -passin pass:123456 -out replain.txt
4.生成随机数
openssl命令也支持生成随机数,其子命令为rand,对应的语法为:
openssl rand [-out file] [-rand file(s)] [-base64] [-hex] num
常用选项有:
-base64:以base64编码格式输出;
-hex:使用十六进制编码格式;
-out FILE:将生成的内容保存在指定的文件中;
使用案例:openssl rand  -base64  10
5.加密密码
OpenSSL还支持生成密码的hash离散值,其子命令为passwd,语法如下:
openssl passwd [-crypt] [-1] [-apr1] [-salt string] [-in file] [-stdin] [-noverify] [-quiet] [-table] {password}
常用选项为:
-salt STRING:添加随机数;
-in FILE:对输入的文件内容进行加密;
-stdin:对标准输入的内容进行加密;
生成密码的hash值案例:openssl passwd -1 -salt 123456 PASSWORD
6. 数字签名
和非对称加密相反,如果是用私钥进行加密,公钥解密叫做数字签名,因为私钥只有一份,用公钥解密出来验证确认是你用这个私钥做的签名,这就是签名和验证。
先用pkcs8子命令提取出pkcs8格式的私钥,rsa默认生成pkcs1格式的私钥,当然也可以直接使用默认的来做签名和验证,在用java等一些开发中需要要求私钥是pkcs8格式,pkcs8子命令格式以及参数如下:
openssl pkcs8 
[-inform PEM|DER] [-outform PEM|DER] [-in filename] [-passin arg] [-out filename]   
[-passout arg] [-topk8] [-noiter] [-nocrypt] [-nooct] [-embed] [-nsdb] [-v2 alg] 
[-v1 alg] [-engine id]  
参数说明:
-inform PEM|DER :输入文件格式,DER或者PEM格式。DER格式采用ASN1的DER标准格式。一般用的多的都是PEM格式,就是base64编码格式。
-outform DER|PEM :输出文件格式,DER或者PEM格式。
-in filename :输入的密钥文件,默认为标准输入。如果密钥被加密,会提示输入一个密钥口令。
-passin arg :输入文件口令保护来源。
-out filename :输出文件,默认为标准输出。如果任何加密操作已经执行,会提示输入一个密钥值。输出的文件名字不能和输入的文件名一样。
-passout arg :输出文件口令保护来源。
-topk8 :通常的是输入一个pkcs8文件和传统的格式私钥文件将会被写出。设置了此选项后,位置转换过来:输入一个传统格式的私钥文件,输出一个PKCS#8格式的文件。
-noiter :MAC保护计算次数为1。
-nocrypt :PKCS#8密钥产生或输入一般用一个适当地密钥来加密PKCS#8 EncryptedPrivateKeyInfo结构。设置了此选项后,一个不加密的PrivateKeyInfo结构将会被输出。这个选项一直不加密私钥文件,在绝对必要的时候才能够使用。某些软件例如一些JAVA代码签名软件使用不加密的私钥文件。
-nooct :这个选项产生的RSA私钥文件是一个坏的格式,一些软件将会使用。特别的是,私钥文件必须附上一个八位组字符串,但是一些软件仅仅包含本身的结构体没有使八位组字符串所环绕。不采用八位组表示私钥。
-embed :这个选项产生的RSA私钥文件是一个坏的格式。在私钥结构体中采用嵌入式DSA参数格式。在这个表单中,八位组字符串包含了ASN1 SEQUENCE中的两种结构:一个SEQUENCE包含了密钥参数,一个ASN1 INTEGER包含私钥值。
-nsdb :这个选项产生的RSA私钥文件是一个坏的格式并兼容了Netscape私钥文件数据库。采用NetscapeDB的DSA格式。
-v2 alg :采用PKCS#5 v2.0,并指定加密算法,默认的是PKCS#8私钥文件被叫做B<pbeWithMD5AndDES-CBC>(该算法用56字节的DES加密但是在PKCS#5 v1.5中有更加强壮的加密算法)的加密算法用口令进行加密。用B<-v2>选项,PKCS#5 v2.0相关的算法将会被使用,可以是des3(168字节)和rc2(128字节),推荐des3。
-v1 alg :采用PKCS#5 v1.5或pkcs12,并指定加密算法。
 -engine id :指定硬件引擎。
然后用rsautl子命令-sign生成签名,-verify验证,示例如下:
/*提取PCKS8格式的私钥*/
openssl pkcs8 -topk8 -in RSA.pem -passin pass:123456 -out pri.pem –nocrypt
/*使用私钥生成签名*/
openssl rsautl -sign -in plain.txt -inkey pri.pem  -out sign.txt
/*使用公钥对签名进行验证*/
openssl rsautl -verify -in sign.txt -inkey pub.pem -pubin -out replain.txt
/*用默认的rsa生成的pkcs1格式私钥生成签名*/
openssl rsautl -sign -in plain.txt -inkey RSA.pem -passin pass:123456  -out sign1.txt
/*用公钥验证签名*/
openssl rsautl -verify -in sign1.txt -inkey pub.pem  -pubin -out replain1.txt
/*查看解密内容*/
xxd replain1.txt
3. crypto++

Crypto++ is written in C++, and is mostly public domain files, although there are a few restrictions on the use of the collection. Crypto++ includes a set of ECC functions.

基于c++的cryptlib,这个库在网上的评价还算不错的,支持多种加密算法。

这里暂时选择Cryto++这个库,选择这个库是因为关于它的资料文档最多,容易快速上手。

地址:http://www.cryptopp.com/

cryptopp8.2.0就开始支持国密算法了,安装如下:

代码语言:javascript
复制
# 安装默认的6版本(不支持国密)
sudo apt install libcrypto++-dev
# 下载
https://github.com/weidai11/cryptopp/releases/tag/CRYPTOPP_8_2_0
# ubuntu编译
make
sudo make install
# windows编译
VS2017 cmake编译指定版本即可
AES算法:
代码语言:javascript
复制
#include <iostream>
#include <aes.h>
 
#pragma comment( lib, "cryptlib.lib" )
using namespace std; 
using namespace CryptoPP;
int main()
{
//AES中的固定参数是以类AES中定义的enum数据类型,而不是成员函数或变量
cout << "AES Parameters: " << endl;
cout << "Algorithm name : " << AES::StaticAlgorithmName() << endl; 

//Crypto++库中一般用字节数来表示长度
cout << "Block size : " << AES::BLOCKSIZE * 8 << endl;
cout << "Min key length : " << AES::MIN_KEYLENGTH * 8 << endl;
cout << "Max key length : " << AES::MAX_KEYLENGTH * 8 << endl;

//AES中只包含一些固定的数据,而加密解密的功能由AESEncryption和AESDecryption来完成
//加密过程
AESEncryption aesEncryptor; //加密器 

unsigned char aesKey[AES::DEFAULT_KEYLENGTH]; //密钥
unsigned char inBlock[AES::BLOCKSIZE] = "123456789"; //要加密的数据块
unsigned char outBlock[AES::BLOCKSIZE]; //加密后的密文块
unsigned char xorBlock[AES::BLOCKSIZE]; //必须设定为全零

memset( xorBlock, 0, AES::BLOCKSIZE ); //置零

aesEncryptor.SetKey( aesKey, AES::DEFAULT_KEYLENGTH ); //设定加密密钥
aesEncryptor.ProcessAndXorBlock( inBlock, xorBlock, outBlock ); //加密

//以16进制显示加密后的数据
for( int i=0; i<16; i++ ) {
cout << hex << (int)outBlock[i] << " ";
}
cout << endl;

//解密
AESDecryption aesDecryptor;
unsigned char plainText[AES::BLOCKSIZE];

aesDecryptor.SetKey( aesKey, AES::DEFAULT_KEYLENGTH );
aesDecryptor.ProcessAndXorBlock( outBlock, xorBlock, plainText );

for( int i=0; i<16; i++ ) 
{ 
cout << plainText[i]; 
}
cout << endl;

return 0;

}
DES算法:
代码语言:javascript
复制
#include <iostream>
#include <des.h>

#pragma comment( lib, "cryptlib.lib" )

using namespace std;
using namespace CryptoPP;

int main( void )
{
   cout << "DES Parameters: " << endl;
   cout << "Algorithm name : " << DES::StaticAlgorithmName() << endl;

   unsigned char key[ DES::DEFAULT_KEYLENGTH ];
   unsigned char input[ DES::BLOCKSIZE ] = "12345";
   unsigned char output[ DES::BLOCKSIZE ];
   unsigned char txt[ DES::BLOCKSIZE ];

   cout << "input is: " << input << endl;

   //首先构造一个加密器
   DESEncryption encryption_DES;
   //设置密匙。
   encryption_DES.SetKey( key, DES::KEYLENGTH );
   //进行加密
   encryption_DES.ProcessBlock( input, output );

   //显示结果
   //for和for之后的cout可有可无,主要为了运行的时候看加密结果
   //把字符串的长度写成一个常量其实并不被推荐。
   //这里主要是把output也就是加密后的内容,以十六进制的整数形式输出。
   for( int i = 0; i < 5; i++ )
   {
       cout << hex << (int)output[ i ] << ends;
   }
   cout << endl;

   //构造一个解密器
   DESDecryption decryption_DES;

   //加密和解密都是同一个密匙,
   decryption_DES.SetKey( key, DES::KEYLENGTH );
   //进行解密,把结果写到txt中
   //decryption_DES.ProcessAndXorBlock( output, xorBlock, txt );
   decryption_DES.ProcessBlock( output, txt );

   //验证加密前的明文和解密后的译文是否相等。
   if ( memcmp( input, txt, 5 ) != 0 )
   {
       cerr << "DES Encryption/decryption failed.\n";
       abort();
   }
   cout << "DES Encryption/decryption succeeded.\n";

   return 0;
}
RSA算法:
代码语言:javascript
复制
#include "randpool.h"
#include "rsa.h"
#include "hex.h"
#include "files.h"
#include <iostream>

using namespace std;
using namespace CryptoPP;

#pragma comment(lib, "cryptlib.lib")

void GenerateRSAKey( unsigned int keyLength, const char *privFilename, const char *pubFilename, const char *seed  );
string RSAEncryptString( const char *pubFilename, const char *seed, const char *message );
string RSADecryptString( const char *privFilename, const char *ciphertext );
RandomPool & GlobalRNG();

void main( void )

{
   char priKey[ 128 ] = { 0 };
   char pubKey[ 128 ] = { 0 };
   char seed[ 1024 ]  = { 0 };

   // 生成 RSA 密钥对
   strcpy( priKey, "pri" );  // 生成的私钥文件
   strcpy( pubKey, "pub" );  // 生成的公钥文件
   strcpy( seed, "seed" );
   GenerateRSAKey( 1024, priKey, pubKey, seed );

   // RSA 加解密
   char message[ 1024 ] = { 0 };
   cout<< "Origin Text:\t" << "Hello World!" << endl << endl;
   strcpy( message, "Hello World!" );
   string encryptedText = RSAEncryptString( pubKey, seed, message );  // RSA 公匙加密
   cout<<"Encrypted Text:\t"<< encryptedText << endl << endl;
   string decryptedText = RSADecryptString( priKey, encryptedText.c_str() );  // RSA 私匙解密
}

// 生成密钥对
void GenerateRSAKey(unsigned int keyLength, const char *privFilename, const char *pubFilename, const char *seed)
{
   RandomPool randPool;
   randPool.Put((byte *)seed, strlen(seed));

   RSAES_OAEP_SHA_Decryptor priv(randPool, keyLength);
   HexEncoder privFile(new FileSink(privFilename));

   priv.DEREncode(privFile);
   privFile.MessageEnd();

   RSAES_OAEP_SHA_Encryptor pub(priv);
   HexEncoder pubFile(new FileSink(pubFilename));
   pub.DEREncode(pubFile);

   pubFile.MessageEnd();

   return ;
}

// RSA加密
string RSAEncryptString( const char *pubFilename, const char *seed, const char *message )
{
   FileSource pubFile( pubFilename, true, new HexDecoder );
   RSAES_OAEP_SHA_Encryptor pub( pubFile );

   RandomPool randPool;
   randPool.Put( (byte *)seed, strlen(seed) );

   string result;
   StringSource( message, true, new PK_EncryptorFilter(randPool, pub, new HexEncoder(new StringSink(result))) );

   return result;
}

// RSA解密
string RSADecryptString( const char *privFilename, const char *ciphertext )
{
   FileSource privFile( privFilename, true, new HexDecoder );
   RSAES_OAEP_SHA_Decryptor priv(privFile);

   string result;
   StringSource( ciphertext, true, new HexDecoder(new PK_DecryptorFilter(GlobalRNG(), priv, new StringSink(result))) );

   return result;
}

// 定义全局的随机数池
RandomPool & GlobalRNG()
{
   static RandomPool randomPool;
   return randomPool;
}
SHA-256算法:
代码语言:javascript
复制
//参考 http://hi.baidu.com/magic475/blog/item/19b37a8c1fa15a14b21bbaeb.html
#include <iostream>
#include <string.h>

#include "sha.h"
#include "secblock.h"
#include "modes.h"
#include "hex.h"

#pragma comment( lib, "cryptlib.lib")

using namespace std;
using namespace CryptoPP;

void CalculateDigest(string &Digest, const string &Message);
bool VerifyDigest(const string &Digest, const string &Message);

int main( void )
{
   string strMessage( "Hello world" );
   string strDigest;	// 摘要
   //string strMessage2( "hello world" ); //只是第一个字母不同
   //string strDigest2;

   CalculateDigest( strDigest, strMessage );  //计算Hash值并打印一些debug信息
   cout << "the size of Digest is: " << strDigest.size() << endl;
   cout << "Digest is: " << strDigest << endl;

   bool bIsSuccess = false;
   bIsSuccess = VerifyDigest( strDigest, strMessage );
   //通过校验,看看strDigest是否对应原来的message
   if( bIsSuccess )
   {
       cout << "sussessive verify" << endl;
       cout << "origin string is: " << strMessage << endl << endl;
   }
   else
   {
       cout << "fail!" << endl;
   }

   //通过strDigest2与strMessage进行校验,要是相等,
   //就证明strDigest2是对应的strMessage2跟strMessage1相等。
   /*CalculateDigest( strDigest2, strMessage2 );
   bIsSuccess = VerifyDigest( strDigest2, strMessage );
   if( !bIsSuccess )
   {
       cout << "success! the tiny modification is discovered~" << endl;
       cout << "the origin message is: \n" << strMessage << endl;
       cout << "after modify is: \n" << strMessage2 << endl;
   }*/
   return 0;
}

void CalculateDigest(string &Digest, const string &Message)
{
   SHA256 sha256;
   int DigestSize = sha256.DigestSize();
   char* byDigest;
   char* strDigest;

   byDigest = new char[ DigestSize ];
   strDigest = new char[ DigestSize * 2 + 1 ];

   sha256.CalculateDigest((byte*)byDigest, (const byte *)Message.c_str(), Message.size());
   memset(strDigest, 0, sizeof(strDigest));
   //uCharToHex(strDigest, byDigest, DigestSize);
   Digest = byDigest;

   delete []byDigest;
   byDigest = NULL;
   delete []strDigest;
   strDigest = NULL;

   return;
}

bool VerifyDigest(const string &Digest, const string &Message)
{
   bool Result;
   SHA256 sha256;
   char* byDigest;

   byDigest = new char[ sha256.DigestSize() ];
   strcpy( byDigest, Digest.c_str() );

   //HexTouChar(byDigest, Digest.c_str(), Digest.size());
   Result = sha256.VerifyDigest( (byte*)byDigest, (const byte *)Message.c_str(), Message.size() );

   delete []byDigest;
   byDigest = NULL;
   return Result;
}

获取设备mac物理地址程序:

代码语言:javascript
复制
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif // !_CRT_SECURE_NO_WARNINGS

//#include "stdafx.h"
#include <windows.h>
#include <wincon.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <Nb30.h>

#include <fstream>
#include <iostream>
#include <string>
#pragma comment(lib,"netapi32.lib")

int GetMac(char * mac)
{
	NCB ncb;
	typedef struct _ASTAT_
	{
		ADAPTER_STATUS   adapt;
		NAME_BUFFER   NameBuff[30];
	}ASTAT, *PASTAT;

	ASTAT Adapter;

	typedef struct _LANA_ENUM
	{
		UCHAR   length;
		UCHAR   lana[MAX_LANA];
	}LANA_ENUM;

	LANA_ENUM lana_enum;
	UCHAR uRetCode;
	memset(&ncb, 0, sizeof(ncb));
	memset(&lana_enum, 0, sizeof(lana_enum));
	ncb.ncb_command = NCBENUM;
	ncb.ncb_buffer = (unsigned char *)&lana_enum;
	ncb.ncb_length = sizeof(LANA_ENUM);
	uRetCode = Netbios(&ncb);

	if (uRetCode != NRC_GOODRET)
		return uRetCode;

	for (int lana = 0; lana < lana_enum.length; lana++)
	{
		ncb.ncb_command = NCBRESET;
		ncb.ncb_lana_num = lana_enum.lana[lana];
		uRetCode = Netbios(&ncb);
		if (uRetCode == NRC_GOODRET)
			break;
	}

	if (uRetCode != NRC_GOODRET)
		return uRetCode;

	memset(&ncb, 0, sizeof(ncb));
	ncb.ncb_command = NCBASTAT;
	ncb.ncb_lana_num = lana_enum.lana[0];
	strcpy((char*)ncb.ncb_callname, "*");
	ncb.ncb_buffer = (unsigned char *)&Adapter;
	ncb.ncb_length = sizeof(Adapter);
	uRetCode = Netbios(&ncb);

	if (uRetCode != NRC_GOODRET)
		return uRetCode;

	sprintf(mac, "%02X-%02X-%02X-%02X-%02X-%02X",
		Adapter.adapt.adapter_address[0],
		Adapter.adapt.adapter_address[1],
		Adapter.adapt.adapter_address[2],
		Adapter.adapt.adapter_address[3],
		Adapter.adapt.adapter_address[4],
		Adapter.adapt.adapter_address[5]);

	return 0;
}

int main(int argc, char* argv[])
{
	//1.未授权的钥匙
	int is_authorized = 0;
	char   mac_empty[200] = "asghjkkklll";//电脑mac地

	std::string key_file_name = "key.txt";

	std::ofstream outfile(key_file_name);
	std::ifstream infile(key_file_name);

	//写入文件夹
	std::fstream file(key_file_name, std::ios::out);//文件夹清空
	outfile << std::to_string(is_authorized) << std::endl;
	outfile << mac_empty << std::endl;

	char   mac_empty1[200] = "000000000";//电脑mac地
	int is_authorized1 = 0;
	if (!infile.is_open())
	{
		std::cout << "文件夹已经打开!" << std::endl;
		return -1;
	}

	//2.读取密钥信息
	std::string mac_name;
	std::string is_authorized2_str;
	if (!infile.is_open())
	{
		std::cout << "文件夹已经打开!" << std::endl;
		return -1;
	}
	std::getline(infile, is_authorized2_str);
	std::getline(infile, mac_name);
	const std::string  is_authorized11 = is_authorized2_str;
	int  is_authorized3 = std::stoi(is_authorized11);

	//3.判定如果软件还未授权,则授权
	if (is_authorized3 == 0)
	{
		GetMac(mac_empty);
		is_authorized = 1;
		
		std::fstream file(key_file_name, std::ios::out);//文件夹清空
		outfile << is_authorized << std::endl;
		outfile << mac_empty << std::endl;

		printf("授权成功!/n");
	}

	//4.再次读取授权状态
	if (!infile.is_open())
	{
		std::cout << "文件夹已经打开!" << std::endl;
		return -1;
	}
	std::getline(infile, is_authorized2_str);
	std::getline(infile, mac_name);
	const std::string  is_authorized12 = is_authorized2_str;
    is_authorized3 = std::stoi(is_authorized12);

	if (is_authorized3 == 1)
	{	
		char   mac_real[200] = "1111111";//电脑mac地
		GetMac(mac_real);
		if (strcmp(mac_real, mac_name.c_str()) == 0)
		{
			std::cout << "mac 地址正确!" << std::endl;
		}
		else
		{
			std::cout << "mac 地址错误,软件未授权!" << std::endl;
		}
	}

	printf("The Mac Address is : %s   \n", mac_empty);
	system("pause");
	return 0;
}
一个示例:
代码语言:javascript
复制
#include <iostream>
#include <Windows.h>
#include <string>
#include <cryptlib.h>
#include <sha.h>
#include <md5.h>
#include <crc.h>
#include <files.h>
#include <hex.h>
#include <sm3.h>
#include <aes.h>
#include <des.h>
#include <filters.h>
#include <modes.h>
#include <randpool.h>
#include <rsa.h>
#include <hex.h>
#include <files.h>

using namespace std;
using namespace CryptoPP;

byte key[CryptoPP::AES::DEFAULT_KEYLENGTH], iv[CryptoPP::AES::BLOCKSIZE];

// RSA函数声明
void GenerateRSAKey(unsigned int keyLength, const char *privFilename, const char *pubFilename, const char *seed);
string RSAEncryptString(const char *pubFilename, const char *seed, const char *message);
string RSADecryptString(const char *privFilename, const char *ciphertext);
RandomPool & GlobalRNG();

void initKV()
{
	memset(key, 0x00, CryptoPP::AES::DEFAULT_KEYLENGTH);
	memset(iv, 0x00, CryptoPP::AES::BLOCKSIZE);

	// 或者也可以
	/*
	char tmpK[] = "1234567890123456";
	char tmpIV[] = "1234567890123456";
	for (int j = 0; j < CryptoPP::AES::DEFAULT_KEYLENGTH; ++j)
	{
		key[j] = tmpK[j];
	}
	for (int i = 0; i < CryptoPP::AES::BLOCKSIZE; ++i)
	{
		iv[i] = tmpIV[i];
	}
	*/
}

/*
string encrypt(const char * plainText, size_t len_plainText)
{
	string cipherText;

	CryptoPP::AES::Encryption aesEncryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH);
	CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, iv);
	CryptoPP::StreamTransformationFilter stfEncryptor(cbcEncryption, new CryptoPP::StringSink(cipherText));
	stfEncryptor.Put(reinterpret_cast<const unsigned char*>(plainText), len_plainText + 1);
	stfEncryptor.MessageEnd();

	/*
	string cipherTextHex;
	for (int i = 0; i < cipherText.size(); i++)
	{
		char ch[3] = { 0 };
		sprintf(ch, "%02x", static_cast<byte>(cipherText[i]));
		cipherTextHex += ch;
	}

	return cipherTextHex;
	

return cipherText;
}

string decrypt(const char * cipherText, size_t cipherTextLen) {
	string decryptedText;

	CryptoPP::AES::Decryption aesDecryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH);
	CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, iv);
	CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, new CryptoPP::StringSink(decryptedText));
	stfDecryptor.Put(reinterpret_cast<const unsigned char*>(cipherText), cipherTextLen);

	stfDecryptor.MessageEnd();

	return decryptedText;
}

*/

// 计算文件的 SHA256 值
string CalSHA256_ByFile(char *pszFileName)
{
	string value;
	SHA256 sha256;
	FileSource(pszFileName, true, new HashFilter(sha256, new HexEncoder(new StringSink(value))));
	return value;
}

// 计算数据的 SHA256 值
string CalSHA256_ByMem(PBYTE pData, DWORD dwDataSize)
{
	string value;
	SHA256 sha256;
	StringSource(pData, dwDataSize, true, new HashFilter(sha256, new HexEncoder(new StringSink(value))));
	return value;
}

int main(int argc, char * argv[])
{
	string src = "hello cryptopp";
	string dst;

	// 单独计算MD5值的使用
	MD5 md5;
	StringSource(src, true, new HashFilter(md5, new HexEncoder(new StringSink(dst))));
	std::cout << "计算字符串MD5: " << dst << std::endl;

	// 单独计算CRC32值
	CRC32 crc32;
	StringSource(src, true, new HashFilter(crc32, new HexEncoder(new StringSink(dst))));
	std::cout << "计算字符串CRC32: " << dst << std::endl;

	// 计算一个数组
	BYTE pArrayData[] = { 10, 20, 30, 40, 50 };
	DWORD dwArraySize = sizeof(pArrayData);

	dst.clear();
	StringSource(pArrayData, dwArraySize, true, new HashFilter(md5, new HexEncoder(new StringSink(dst))));
	std::cout << "计算数组的MD5: " << dst << std::endl;

	// 直接对文件计算Sha256散列值
	string sha = CalSHA256_ByFile("D:\\AAA_Work\\log0207.txt");
	std::cout << "文件散列值: " << sha << std::endl;

	// 读入文件到内存后计算
	HANDLE hFile = CreateFile(L"D:\\AAA_Work\\log0207.txt", GENERIC_READ, FILE_SHARE_READ, NULL,
		OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);
	DWORD dwFileSize = GetFileSize(hFile, NULL);
	BYTE *pData = new BYTE[dwFileSize];
	ReadFile(hFile, pData, dwFileSize, NULL, NULL);

	string sha2 = CalSHA256_ByMem(pData, dwFileSize);
	std::cout << "内存中文件散列值: " << sha2.c_str() << std::endl;

	// SM3
	HexEncoder encoder(new FileSink(std::cout));
	std::string msg = "Yoda said, Do or do not. There is not try.";
	std::string digest;
	SM3 hash;
	hash.Update((const byte*)&msg[0], msg.size());
	digest.resize(hash.DigestSize());
	hash.Final((byte*)&digest[0]);
	//std::cout << "Message: " << msg << std::endl;
	std::cout << "SM3加密后的Digest: ";
	StringSource(digest, true, new Redirector(encoder));
	std::cout << std::endl;

	/*
	// AES
	string text = "hello cryptopp";
	std::cout << "text : " << text << std::endl;

	initKV();
	string cipherHex = encrypt(text.c_str(), text.length());
	std::cout << "cipher : " << cipherHex << std::endl;
	string decryptedText = decrypt(cipherHex.c_str(), cipherHex.length());
	std::cout << "decrypted text = " << decryptedText << std::endl;
	*/


	// DES
	/*
	数据准备;
	构造加密器;
	设置加密密匙;
	加密数据;
	显示(非必要);
	设置解密密匙(跟加密密匙是同一个key);
	解密数据;
	验证与显示(非必要);
	*/
	cout << "算法名称: " << DES::StaticAlgorithmName() << endl;
	unsigned char key[DES::DEFAULT_KEYLENGTH];
	unsigned char input[DES::BLOCKSIZE] = "12345";
	unsigned char output[DES::BLOCKSIZE];
	unsigned char txt[DES::BLOCKSIZE];

	//cout << "输入信息: " << input << endl;
	//首先构造一个加密器
	DESEncryption encryption_DES;

	//设置密匙
	encryption_DES.SetKey(key, DES::KEYLENGTH);
	//进行加密
	encryption_DES.ProcessBlock(input, output);

	//显示结果
	//for和for之后的cout可有可无,主要为了运行的时候看加密结果
	//把字符串的长度写成一个常量其实并不被推荐。
	//不过笔者事先知道字符串长,为了方便调试,就直接写下。
	//这里主要是把output也就是加密后的内容,以十六进制的整数形式输出。
	cout << "加密结果:";
	for (int i = 0; i < 5; i++)
	{
		cout << hex << (int)output[i];
	}
	cout << endl;

	//构造一个解密器
	DESDecryption decryption_DES;

	//由于对称加密算法的加密和解密都是同一个密匙,
	//因此解密的时候设置的密匙也是刚才在加密时设置好的key
	decryption_DES.SetKey(key, DES::KEYLENGTH);
	//进行解密,把结果写到txt中
	//decryption_DES.ProcessAndXorBlock( output, xorBlock, txt );
	decryption_DES.ProcessBlock(output, txt);

	//以上,加密,解密还原过程已经结束了。以下是为了验证:
	//加密前的明文和解密后的译文是否相等。
	if (memcmp(input, txt, 5) != 0)
	{
		cerr << "DES Encryption/decryption failed.\n";
		abort();
	}
	cout << "DES Encryption/decryption succeeded.\n";

	// AES
	//AES中使用的固定参数是以类AES中定义的enum数据类型出现的,而不是成员函数或变量
	//因此需要用::符号来索引
	cout << "算法名称: " << AES::StaticAlgorithmName() << endl;

	//Crypto++库中一般用字节数来表示长度,而不是常用的字节数
	//cout << "Block size : " << AES::BLOCKSIZE * 8 << endl;
	//cout << "Min key length : " << AES::MIN_KEYLENGTH * 8 << endl;
	//cout << "Max key length : " << AES::MAX_KEYLENGTH * 8 << endl;

	//AES中只包含一些固定的数据,而加密解密的功能由AESEncryption和AESDecryption来完成
	//加密过程
	AESEncryption aesEncryptor; //加密器 

	unsigned char aesKey[AES::DEFAULT_KEYLENGTH]; //密钥
	unsigned char inBlock[AES::BLOCKSIZE] = "123456789"; //要加密的数据块
	unsigned char outBlock[AES::BLOCKSIZE]; //加密后的密文块
	unsigned char xorBlock[AES::BLOCKSIZE]; //必须设定为全零

	memset(xorBlock, 0, AES::BLOCKSIZE); //置零

	aesEncryptor.SetKey(aesKey, AES::DEFAULT_KEYLENGTH); //设定加密密钥
	aesEncryptor.ProcessAndXorBlock(inBlock, xorBlock, outBlock); //加密

	cout << "加密结果:";
	//以16进制显示加密后的数据
	for (int i = 0; i < 16; i++) {
		cout << hex << (int)outBlock[i];
	}
	cout << endl;

	//解密
	AESDecryption aesDecryptor;
	unsigned char plainText[AES::BLOCKSIZE];

	aesDecryptor.SetKey(aesKey, AES::DEFAULT_KEYLENGTH);
	//细心的朋友注意到这里的函数不是之前在DES中出现过的:ProcessBlock,
	//而是多了一个Xor。其实,ProcessAndXorBlock也有DES版本。用法跟AES版本差不多。
	//笔者分别在两份代码中列出这两个函数,有兴趣的朋友可以自己研究一下有何差异。
	aesDecryptor.ProcessAndXorBlock(outBlock, xorBlock, plainText);
	cout << "解密结果:";
	for (int i = 0; i < 16; i++)
	{
		cout << plainText[i];
	}
	cout << endl;

	system("pause");
	return 0;
}
4. botan

这个库没有太多的了解,也仅仅是刚知道,也是支持多种加密算法的。

地址:http://botan.randombit.net/

5. cryptlib

基于c的加密库。

地址:http://www.cryptlib.com/

如果应用涉及到 SSL 等 PKI 协议,应该使用 openssl,如果只是应用一些加密算法,则建议使用 Cryptlib。

三、性能与方案选择

加密技术包括两个元素:算法和密钥。密钥是用来对数据进行编码和解码的一种方法;而算法是将明文与密钥结合,产生不可理解的密文的步骤。

单向散列算法诞生较早,技术成熟,运行速度最快,但只能单向加密;对称加密算法是指客户端和服务端共用一套密钥,用相同的密钥去加密和解密,运行速度较快,安全性也较高;非对称加密算法有公钥和私钥,较难破解,安全等级最高,但运行速度较慢。

常用加密算法性能和安全性对比:

在这里插入图片描述
在这里插入图片描述

通过了解各种算法的实现方式、优缺点及相关应用,得出以下结论:校验时,可以使用单向散列算法(如MD5、CRC32),一般加密需求时,可以使用对称加密算法(如AES、DES);但加密要求较高时,建议选用混合加密策略,即用非对称加密算法(如RSA)来解决对称加密的密钥传输,密钥传输完成后,A和B之间采用对称加密算法(如AES)进行通信。这样做既可以保证密钥的安全性,也能保证数据正常传输过程中的运行效率。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-02-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、介绍
    • 1. 单向散列/哈希算法
      • 2. 对称加密算法
        • 3. 非对称加密算法
        • 二、加密算法实现
          • 1. 造轮子
            • AES算法
            • SM4算法
            • RSA算法
            • MD5
            • SHA-1
            • sm3
          • 2. openssl
            • 3. crypto++
              • AES算法:
              • DES算法:
              • RSA算法:
              • SHA-256算法:
              • 一个示例:
            • 4. botan
              • 5. cryptlib
              • 三、性能与方案选择
              相关产品与服务
              命令行工具
              腾讯云命令行工具 TCCLI 是管理腾讯云资源的统一工具。使用腾讯云命令行工具,您可以快速调用腾讯云 API 来管理您的腾讯云资源。此外,您还可以基于腾讯云的命令行工具来做自动化和脚本处理,以更多样的方式进行组合和重用。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档