【C++】crypto++加密库简单使用

2023-11-15

crypto++加密库简单使用

目录

crypto++密码学库简单使用

一、简介

二、配置

三、使用示例

1.CRC32校验

2.Base64编码

3.Blake2b

4.AES

5.RSA


一、简介

        crypto++是一个免费开源(公共领域)的C++密码学库,首作者叫Wei Dai(美籍华裔姓Dai)。

        它包含主流的密码学方案,比如对称加密AES,非对称RSA,哈希函数SHA2等。还包含更常见但不够安全的功能,比如SHA1MD5。还有没有安全需求的常见功能,比如CRC32Base64。在官网还列出一大堆功能,读者可以自行查阅。

二、配置

        官网地址:Crypto++ Library 8.6 | Free C++ Class Library of Cryptographic Schemes

        wiki地址:Crypto++ Wiki

        作者主页:Wei Dai's Home Page

        github地址:GitHub - weidai11/cryptopp: free C++ class library of cryptographic schemes

        通过官网可以下载库,官网有API参考,不过看wiki的教程会比较轻松。

        库自带vc工程文件,在windows平台很方便使用。

三、使用示例

        为了使代码简单,我们使用两个类型声明(包含<cryptlib.h>头文件,引用CryptoPP命名空间):

#include <cryptlib.h>
using namespace CryptoPP;

using p = CryptoPP::byte*;
using cp = const CryptoPP::byte*;

1.CRC32校验

        CRC本意为循环冗余校验(Cyclic Redundancy Check),CRC32一般用来校验数据的完整性,它输出一个32位长度的值。由于算法简单,输出值不够长,没有安全考虑,只是单纯的验证数据的完整性。比如硬盘出错、网络传输、比特翻转等造成的数据错误可以很快的检测出来,而理论上一些不法分子可以篡改文件内容,但使CRC32校验值依旧相同,从而让别人使用被篡改的文件。

#include <crc.h>
dnd::n32 HashCrc32(const Buffer& buf)
{
	n32 ret;
	CRC32 hash;
	hash.Update((cp)buf._p, buf._size);
	hash.Final((p)&ret);
	return ret;
}

bool HashCrc32Check(const Buffer& buf, n32 digest)
{
	CRC32 hash;
	hash.Update((cp)buf._p, buf._size);
	return hash.Verify((cp)&digest);
}

        其中Buffer为一段内存,p为首地址,size为长度。n32为32位无符号整型

        将代码中的CRC32类型,替换为CRC32C即可使用CRC32-C版本。CRC32-C在TCP/IP中使用,所以也叫Internet校验和,它比CRC32更新一些,效率也高一点,应该优先使用它。

2.Base64编码

        所谓Base64编码,意译即是使用64个符号来编码。首先它编码的目标是字节流,即基本单位为字节(byte),当然在一般的C/C++代码里面,处理数据的基本单位也是字节,而不是位。所以大家常说的二进制流,比特流与字节流差异不大(但还是有区别)。

        众所周知1个字节为8位,即表示范围为[0, 255],但是ascii码表并非都是可见字符。所以任意的一段内存数据并不能直接使用可见字符的形式表示出来(比如数据中的0,在ascii码中表示结束符)。

        所以Base64编码将256个值用64个值表示。64个值如下:

A B C D E F G H
I J K L M N O P
Q R S T U V W X
Y Z a b c d e f
g h i j k l m n
o p q r s t u v
w x y z 0 1 2 3
4 5 6 7 8 9 + /

        由于64=2^6,那么一个Base64码,需要6位二进制。那么3个比特(byte)是24位,则可以用4个Base64码表示。

        所以任意数据都可以通过Base64编码为可见字符,它还有两个规则:每76个字符(密文)换行;原文不是3字节的倍数补0,每补一个字节末尾添加一个=。当然3的非0余数只有1和2,所以只会出现最多两个=。

#include <base64.h>
dnd::Buffer EncodeBase64(const Buffer& buf)
{
	Buffer ret;

	string str_sink;
	StringSource ss((cp)buf._p, buf._size, true,
		new Base64Encoder(
			new StringSink(str_sink)
		) // Base64Encoder
	); // StringSource
	ret.Copy(str_sink);
	return ret;
}

dnd::Buffer DecodeBase64(const Buffer& buf)
{
	Buffer ret;

	string str_sink;
	StringSource ss((cp)buf._p, buf._size, true,
		new Base64Decoder(
			new StringSink(str_sink)
		) // Base64Encoder
	); // StringSource
	ret.Copy(str_sink);
	return ret;
}

        上面的代码中,我们使用StringSink接收编码(或解码)后的数据。然后我们调用Buffer::Copy从string_sink复制了一份内存。此处的内存复制讲道理可以优化,不过暂未研究出来。

3.Blake2b

        此算法用于替代不安全的MD5,更快更简单更安全。与CRC32等哈希函数一样,它的结果值是固定长度的,所以我们使用固定大小的类型来返回结果。

using Blake2b = array<byte, 64>;//长度为64字节
#include <blake2.h>
Blake2b HashBlake2b(const Buffer& buf)
{
	assert(!buf.Empty());

	Blake2b ret;
	BLAKE2b hash;
	hash.Update((cp)buf._p, buf._size);
	hash.Final((p)&ret);
	return ret;
}

bool HashBlake2bCheck(const Buffer& buf, Blake2b digest)
{
	assert(!buf.Empty());

	BLAKE2b hash;
	hash.Update((cp)buf._p, buf._size);
	return hash.Verify((cp)&digest);
}

4.AES

        AES全称高级加密标准(Advanced Encryption Standard),属于对称加密算法,即加密与解密均使用同一密钥。它比DES算法先进,DES加密算法已经不安全。

        使用AES首先要生成密钥,它还会生成一个IV来重复使用同一个密钥(可以简单视为密码的一部分)。它的长度我们使用默认的,所以如下定义类型:

using AesKey = array<byte, 16>;
using AesIV = array<byte, 16>;

        而库本身使用SecByteBlock类来保存密码,因为它释放后会清空内存,防止关键信息残留内存。这一点可具体看文档说明,这里简单起见,直接复制了出来。

#include <osrng.h>
#include <rijndael.h>
tuple<AesKey, AesIV> GenerateAES()
{
	AutoSeededRandomPool prng;

	SecByteBlock key(AES::DEFAULT_KEYLENGTH);
	SecByteBlock iv(AES::BLOCKSIZE);

	prng.GenerateBlock(key, key.size());
	prng.GenerateBlock(iv, iv.size());

	tuple<AesKey, AesIV> ret;
	AesKey& ret_key = get<0>(ret);
	AesIV& ret_iv = get<1>(ret);

	memcpy(ret_key.data(), key.data(), key.size());
	memcpy(ret_iv.data(), iv.data(), iv.size());

	return ret;
}

        通过上面的函数可以生成密钥(key、iv对),如下所示,使用了std::tuple,然后我将其打印了出来。

auto [key, iv] = Crypto::GenerateAES();
debug_test(format("key:{}", toString(key)));
debug_test(format("iv :{}", toString(iv)));
key:2d9e6c261b2625eb728121e77a68fe6c
iv :f9f9178a3ca880c21895ae4b3047a68a

        接下来是加密与解密,它俩代码基本一致,只是CBC_Mode<AES>::Encryption换为了CBC_Mode<AES>::Decryption

Buffer EncryptAES(const Buffer& buf, const AesKey& in_key, const AesIV& in_iv)
{
	SecByteBlock key((cp)in_key.data(), in_key.size());
	SecByteBlock iv((cp)in_iv.data(), in_iv.size());

	CBC_Mode<AES>::Encryption e;
	e.SetKeyWithIV(key, key.size(), iv);

	Buffer ret;
	//ArraySink是固定长度的接收缓冲区
	string str_sink;
	StringSource s((cp)buf._p, buf._size, true,
		new StreamTransformationFilter(e,
			new StringSink(str_sink)
		) // StreamTransformationFilter 
	); // StringSource 

	ret.Copy(str_sink);
	return ret;
}


Buffer DecryptAES(const Buffer& buf, const AesKey& in_key, const AesIV& in_iv)
{
	SecByteBlock key((cp)in_key.data(), in_key.size());
	SecByteBlock iv((cp)in_iv.data(), in_iv.size());

	CBC_Mode<AES>::Decryption e;
	e.SetKeyWithIV(key, key.size(), iv);

	Buffer ret;
	//ArraySink是固定长度的接收缓冲区
	string str_sink;
	StringSource s((cp)buf._p, buf._size, true,
		new StreamTransformationFilter(e,
			new StringSink(str_sink)
		) // StreamTransformationFilter 
	); // StringSource 

	ret.Copy(str_sink);
	return ret;
}

5.RSA

        RSA由三位作者完成,所以RSA为三位作者名字首字母的合称。RSA为非对称加密算法,它的密钥分为公钥私钥。一般公钥用于加密数据,私钥用于解密数据。但是也可以私钥加密,而公钥来解密。

        私钥由自己保存,不会通过网络传输,也就不存在窃听的可能(除非主机被入侵直接获取到私钥,或者你把私钥发给别人)。

        公钥是公开的密码,别人使用公钥对数据进行加密,然后其余人没有私钥是无法进行解密的。所以这保证了:别人发送给你的信息不会泄漏

        而你私钥加密的信息,任何人有公钥都能解密,这只能证明你拥有私钥,即验证身份。但不能对信息保密。

        RSA算法的根基是基于大质数难以分解的数学问题,原理可以参考我这篇博客:

RSA加密原理_略游的博客-CSDN博客

        假设你要与朋友交流一些不可告人的事情,实际上使用AES对称加密算法也足够了。首先生成一个AES密钥,然后两人记录下来(不能通过网络发送,存在被窃听可能)。然后将加密后的数据通过网络传输,这样别人即使窃取到数据,而不知道密钥,是无法解密的。
        服务器与客户端的交互,可以生成一个临时的AES密钥,用于双方交流信息。但是它们无法提前约定密钥,所以必须使用非对称加密的魔法来传递密钥信息。

        但是使用RSA发送AES密钥时,需要注意对方给予的公钥是真的。比如甲给乙要发送AES密钥,所以乙给了甲自己的公钥,等到乙接受到密文后使用乙的私钥即可解密获得AES密钥。但是这个过程,丙可以伪装自己是乙,将丙的公钥发送给甲,待甲用丙的公钥加密后,这样甲的AES密钥便会泄漏给丙。

        所以现在的CA证书,便是通过CA机构认证后颁发,来证明某公钥是某人的拥有者。

        代码如下,我没有直接生成密钥,而是通过参数保存,私钥是(n,d)对,而公钥是(n,e)对,pq是两个大质数,不过此处不需要使用pq:

#include <modes.h>
#include <rsa.h>
#include <integer.h>

//BigInteger就是Integer,可以自行替换
struct RsaKeyPrivate
{
	BigInteger _n;
	BigInteger _e;
	BigInteger _d;
};
struct RsaKeyPublic
{
	BigInteger _n;
	BigInteger _e;
};

tuple<RsaKeyPrivate, RsaKeyPublic> GenerateRSA()
{
	AutoSeededRandomPool rng;

	InvertibleRSAFunction params;
	params.GenerateRandomWithKeySize(rng, 3072);

	/*
	///
	// Generated Parameters
	const Integer& n = params.GetModulus();
	const Integer& p = params.GetPrime1();
	const Integer& q = params.GetPrime2();
	const Integer& d = params.GetPrivateExponent();
	const Integer& e = params.GetPublicExponent();

	///
	// Dump
	cout << "RSA Parameters:" << endl;
	cout << " n: " << n << endl;
	cout << " p: " << p << endl;
	cout << " q: " << q << endl;
	cout << " d: " << d << endl;
	cout << " e: " << e << endl;
	cout << endl;
	*/
	///
	//生成密钥
	/*RSA::PrivateKey priKey(params);
	RSA::PublicKey	pubKey(params);*/

	//返回
	tuple<RsaKeyPrivate, RsaKeyPublic> ret;
	RsaKeyPrivate& key_pri = get<0>(ret);
	RsaKeyPublic& key_pub = get<1>(ret);

	const Integer& n = params.GetModulus();
	const Integer& d = params.GetPrivateExponent();
	const Integer& e = params.GetPublicExponent();
	*((Integer*)(key_pri._n.GetImp())) = n;
	*((Integer*)(key_pri._d.GetImp())) = d;
	*((Integer*)(key_pri._e.GetImp())) = e;

	*((Integer*)(key_pub._n.GetImp())) = n;
	*((Integer*)(key_pub._e.GetImp())) = e;

	return ret;
}

        在加密和解密时,再生成密钥,代码如下:

dnd::Buffer EncryptRSA(const Buffer& buf, const RsaKeyPublic& key_pub)
{
	AutoSeededRandomPool rng;

	RSA::PublicKey pubKey;

	RsaKeyPublic& key = const_cast<RsaKeyPublic&>(key_pub);
	const Integer n = *((Integer*)key._n.GetImp());
	const Integer e = *((Integer*)key._e.GetImp());
	pubKey.Initialize(n, e);

	RSAES_OAEP_SHA_Encryptor encryptor(pubKey);

	Buffer ret;
	string str_sink;
	StringSource ss((cp)buf._p, buf._size, true,
		new PK_EncryptorFilter(rng, encryptor,
			new StringSink(str_sink)
		) // PK_EncryptorFilter 
	); // StringSource 

	ret.Copy(str_sink);
	return ret;
}


dnd::Buffer DecryptRSA(const Buffer& buf, const RsaKeyPrivate& key_pri)
{
	AutoSeededRandomPool rng;
	RSA::PrivateKey priKey;

	RsaKeyPrivate& key = const_cast<RsaKeyPrivate&>(key_pri);
	const Integer& n = *((Integer*)key._n.GetImp());
	const Integer& e = *((Integer*)key._e.GetImp());
	const Integer& d = *((Integer*)key._d.GetImp());
	priKey.Initialize(n, e, d);

	RSAES_OAEP_SHA_Decryptor decryptor(priKey);

	Buffer ret;
	string str_sink;
	StringSource ss((cp)buf._p, buf._size, true,
		new PK_DecryptorFilter(rng, decryptor,
			new StringSink(str_sink)
		) // PK_DecryptorFilter 
	); // StringSource 

	ret.Copy(str_sink);
	return ret;
}

        不过上面的代码会产生内存泄漏(反复使用不会产生额外的泄漏),原因参见:

Memory leak in Singleton::Ref()? · Issue #550 · weidai11/cryptopp · GitHub

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【C++】crypto++加密库简单使用 的相关文章

  • 如何在 Windows 窗体上将 CausesValidation 设置为 false 以关闭[Xbox?

    我已经将取消按钮上的 CausesValidation 设置为 false 并且它可以工作 bool IsCancelBtnClicked false private void EmployeeIDtextBox Validating ob
  • 如何在 C# 中导航任何 JSON 树?

    我需要像导航 XML 一样导航 Json 结构XmlDocument 结构未知 我需要迭代节点来解析一些数据 这可能吗 我知道我可以使用JavaScriptSerializer将其反序列化为已知类型 但事实并非如此 因为我可以接收任何有效的
  • 如何在编译时检查该类是否是抽象的?

    我所说的抽象类是指至少具有一个纯虚方法的类 如果检查显示该类是 我希望编译失败not抽象的 有可能吗 Use std is abstract http en cppreference com w cpp types is abstract
  • 如何在类的 Dispose 方法中取消订阅匿名函数?

    我有一个 A 类 在它的构造函数中 我正在为 Object B 的 eventHandler 分配一个匿名函数 如何从 A 类的 Dispose 方法中删除 取消订阅 它 任何帮助 将不胜感激 谢谢 Public Class A publi
  • 易失性限定符是否会取消该内存的缓存?

    在本文中 http www drdobbs com parallel 易失性 vs 易失性 212701484 pgno 2 http www drdobbs com parallel volatile vs volatile 212701
  • 使用 C# 的服务 SACL ||使用 C# 获取具有 ACCESS_SYSTEM_SECURITY 权限的服务的句柄

    有人知道如何使用 C 获取远程服务上的 SACL 吗 我尝试了很多不同的方法 但基本上没有什么效果 我可以在本地计算机上获取 DACL 和 SACL 但在远程计算机上获取其中任何一个似乎都不可能 我所做的是创建一个名为ServiceSecu
  • 如何在 Windows 中获取和设置系统音量

    我想使用 unity 和 c 将键盘点击时的操作系统音量设置为一定水平 例如我想将 Windows 音量 不是 unity 设置为 70 我该怎么做 void Update if Input GetKeyDown KeyCode A Set
  • 如何对多重映射中的键和值进行排序?

    建议使用任何方法对多重映射的键及其值进行排序 例如 输入 5 1 1 9 1 1 5 2 1 2 输出必须是 1 1 1 2 1 9 5 1 5 2 答案是emplace hint 伪代码如下所示 insert with hint M mm
  • 了解带有位移位的右移运算符

    我很难理解右移运算符 我理解左移 假设我们没有 int n 11 which is 1011 现在如果我们左移它n lt lt 1结果是 int a n lt lt 1 so a 10110 simply add a 0 to the en
  • 高效快速的线程参数方法

    创建带参数的线程最有效的方法是什么 参数是一个结构体 如果该结构体不能保留在父线程堆栈上 有两种解决方案 具有动态内存分配 struct Arg int x int y void my thread void v arg Arg arg A
  • bet365 网站上 Selenium 的 Chrome 驱动程序陷入灰屏

    当我尝试使用 Chrome 驱动程序和 Selenium 打开 bet365 网站时出现灰屏 var driver new ChromeDriver driver Navigate GoToUrl https www bet365 it 我
  • 无需使用abs函数或if语句即可获取绝对值

    我在想如何在不使用的情况下获得整数的绝对值if声明也不abs 起初我使用的是左移位 lt lt 试图将负号移出范围 然后将位右移回原来的位置 但不幸的是它对我不起作用 请让我知道为什么它不起作用以及其他替代方法 From 位摆弄黑客 htt
  • 自定义 web.config 部分处理程序

    我之前设计过一个自定义部分处理程序 但我遇到了一个我似乎无法想到的问题 我有一个像这样的配置部分
  • strstr() 函数类似,忽略大小写

    我有两根弦 可以说 str1 One Two Three and str2 two 我想知道是否有任何函数可以检查第一个字符串中第二个字符串的匹配 并返回指向第一个字符串的指针 例如strstr 但它不会将相同的字母 大写或小写 视为两个不
  • 如何将 Ctrl+,(control 加逗号)指定为 WPF 菜单项的键盘快捷键?

    Question I would like to assign the keyboard shortcut Ctrl control plus comma to the Preferences menu item How do I do t
  • ‘+= new EventHandler’和‘-= new EventHandler(anEvent)’之间的区别

    我看到一些代码使用 新的事件处理程序 anEvent 你能告诉我有什么不同吗 新的事件处理程序 Thanks 一个将委托添加到订阅者集合中 另一个将其删除 例如 如果您之前订阅了某个事件 但您希望在关闭表单时删除引用 则可以使用 版本 您将
  • 无限循环消耗 100% CPU

    我陷入了需要生成某个 Hz 的定义频率的情况 我尝试过多媒体计时器和互联网上提供的所有其他东西 但到目前为止 带有一些 if else 条件的无限循环给了我最好的结果 但这种方法的问题是它消耗了几乎所有的CPU 没有空间让其他应用程序正常工
  • 收到不明确符号的错误,需要帮助来删除它

    我收到此错误 无法在 Visual Studio 2010 中删除 我正在使用一个第三方库 该库使用自己的 字符串 定义 另外 Visual Studio 的 xstring 文件位于其安装的文件夹中 现在 当我尝试编译代码时 出现以下错误
  • 非通用接口是通用接口的同义词

    我在 C 中有一个通用接口 并且几乎总是将它与其中一种类型一起使用 我想为该类型创建一个非通用接口并使用它 假设我有以下代码 public interface IMyGenericList
  • 列出所有会话信息

    我想在页面中显示我的asp net页面 aspx 的所有会话信息 我怎样才能做到这一点 编程语言是C 这两种方法对我有用 稍微改进和纠正了大卫的答案 第一种方法 for int i 0 i lt Session Count i var cr

随机推荐

  • 队列数据类型及Python实现

    1 队列的实现 队列是一种有次序的数据集合 其特征是 新数据项的添加总发生在一端 通常称为尾端 rear 而现存数据类型的移除总发生在另一端 通常称为首段 front 当数据项加入队列 首先出现在队尾 随着队首数据项的移除 它逐渐接近队首
  • NEZHA知识点

    1 华为NEZHA 主要是将bert之后预训练模型的长处拼接在一起 1 相对位置编码 Bert的位置编码是直接初始化一个embedding 然后通过预训练去学的 是固定位置编码 NEZHA使用函数式相对位置编码 在qk时 加在k上 表示q和
  • Trie树(字典树,单词查找树)

    例题引入 题目传送门于是他错误的点名开始了 题目大意 给出n个单词 有m个询问 每次给出一个单词 如果这个单词出现过且是第一次出现 输出 OK 如果这个单词没有出现过 输出 WRONG 如果这个单词出现过但不是第一次出现 输出 REPEAT
  • how to

    打开一个virtualfile VirtualFile file Project project OpenFileDescriptior descriptor new OpenFileDescriptior file navigate Fi
  • PROFIBUS DP从站开发 VPC3源程序分析---vpc3_get_dinbufptr (void)函数

    UBYTE PTR ATTR vpc3 get dinbufptr void 以下程序注释由成都地质学院霸王猫添加 引用时请尊重作者劳动成果 标明引用者来自成都地质学院霸王猫 UBYTE PTR ATTR vpc3 get dinbufpt
  • hadoop habse集群增加磁盘空间

    最近项目上采集的数据量超出预期 hbase集群中的datanode节点磁盘使用过快 基本都超过50 按现有的使用频率来看 预计只能继续支撑3个月左右 考虑着要加新的硬盘 但是却被这么一个小问题折腾了好一会才解决 特地在此记录一下 cento
  • Webstorm的介绍

    WebStorm 是什么 WebStorm 是jetbrains公司旗下一款JavaScript 开发工具 被广大中国JS开发者誉为 Web前端开发神器 最强大的HTML5编辑器 最智能的JavaScript IDE 等 与IntelliJ
  • 蜗牛君漫聊动态布局框架(三):适配器与创建者

    大家好 欢迎来到蜗牛君漫聊动态布局框架专题 上篇文章中我们介绍完了框架的核心功能实现 本篇继续介绍剩下的所有功能 不同类型ViewHolder的动态创建已经完成 接下来我们要实现数据与布局的中间件 适配器 Adapter 代码实现 中间件
  • anaconda所有版本大全

    今天在整理的时候把自己的anaconda搞废了 所以用了万能的方法 重装 但是面临装了官网的最新版 发现出现很多的小问题 不是缺少这就是缺少那 python的版本也是最新的3 8 用起来很不习惯 于是想到了降低python版本 但是在实际的
  • keil C51 常见错误和警告

    C51编译器识别错类型有三种 1 致命错误 伪指令控制行有错 访问不存在的原文件或头文件等 2 语法及语义错误 语法和语义错误都发生在原文件中 有这类错误时 给出 提示但不产生目标文件 错误超过一定数量才终止编译 3 警告 警告出现并不影响
  • 最强大脑记忆法

    3 14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 上面这个是故事摄影的方法 10个记忆宫殿来记20个数字 汽车的记忆宫殿 1前轮 2车灯 3车
  • transformer位置编码最详细的解析

    位置编码positional encoding 1 位置编码是什么 为什么需要它 2 transformer提出的位置编码方法 3 直觉 4 其他细节 5 相对位置 6 常见问题解答 1 位置编码是什么 为什么需要它 位置和词语的顺序是任何
  • STL——set容器、map容器

    初识STL set容器 multiset容器 set容器 构造和赋值 set容器 大小和交换 set容器 插入和删除 set容器的查找和统计 set和multiset的区别 set的相关操作源码 multiset的相关操作源码 pair使用
  • Acwing算法基础课知识点

    知识点基础算法 代码模板链接 常用代码模板1 基础算法 排序 二分 高精度 前缀和与差分 双指针算法 位运算 离散化 区间合并 数据结构 代码模板链接 常用代码模板2 数据结构 链表与邻接表 树与图的存储 栈与队列 单调队列 单调栈 kmp
  • Android插件化的探索

    简介 对于App而言 所谓的插件化 个人的理解就是把一个完整的App拆分成宿主和插件两大部分 我们在宿主app运行时可以动态的载入或者替换插件的部分 插件不仅是对宿主功能的扩展而且还能减小宿主的负担 所谓的宿主就是运行的app 插件即宿主运
  • 几种本地存储方式

    浏览器本地存储的容器 1 cookie 2 sessionStorage 3 localStorage cookie cookie 浏览器早期存储数据容器 主要用于 存放用户名和密码 特点 容量小 4kb 操作繁琐 name zs pass
  • android studio安装automotive模拟器

    添加源 打开android studio的SDK Manager 选择SDK Update Sites选项卡 点击Add 弹出地址设置界面 添加polestar2 sys img Name填写 Polestar 2 System Image
  • RabbitMQ如何保证消息的顺序性【重点】

    1 1 保证顺序性的意义 消息队列中的若干消息如果是对同一个数据进行操作 这些操作具有前后的关系 必须要按前后的顺序执行 否则就会造成数据异常 举例 比如通过mysql binlog进行两个数据库的数据同步 由于对数据库的数据操作是具有顺序
  • SQLi LABS Less-12 联合注入+报错注入

    第十二关是双引号 括号的字符型注入 推荐使用联合注入 报错注入 方式一 联合注入 参考文章 联合注入使用详解 原理 步骤 实战教程 第一步 判断注入类型 用户名输入 a or 1 a 密码随便输入 1 页面正常显示 用户名输入 a or 0
  • 【C++】crypto++加密库简单使用

    crypto 加密库简单使用 目录 crypto 密码学库简单使用 一 简介 二 配置 三 使用示例 1 CRC32校验 2 Base64编码 3 Blake2b 4 AES 5 RSA 一 简介 crypto 是一个免费开源 公共领域 的