Seal库官方示例(一):bfv_basics.cpp解析

news2024/11/24 21:04:02

尽量理论来理解代码。
完整代码或者\native\examples里面

说到前面的话

两段官方的话
image.png
image.png
大致意思就是,这个库有门槛,需要先学会同态的概念,提供的例子必须要看要理解。必看的例子如下,
image.png

代码解析

基础加密

参数设置

三个核心参数
首先要知道这里的BFV代码是基于RLWE的,也就是涉及到多项式运算,同时它只能加密整数。
回忆一下RLWE的实例 ( b = a s + e , a ) (b=as+e,a) (b=as+e,a)中的部分是取自于多项式环 R q = Z q [ x ] / f ( x ) R_q = \mathbb{Z}_q[x]/f(x) Rq=Zq[x]/f(x)(其中 f ( x ) = x N + 1 f(x)=x^N+1 f(x)=xN+1),明文空间是 R t R_t Rt
那么由此将得到三个十分重要的加密参数,即多项式阶数模 N N N、多项式系数模数 q q q、明文模数 t t t
第三个参数仅有bfv需要(BGV的明文空间是 R 2 R_2 R2,CKKS是浮点数和复数)。

  • poly_modulus_degree (degree of polynomial modulus);
  • coeff_modulus ([ciphertext] coefficient modulus);
  • plain_modulus (plaintext modulus; only for the BFV scheme);

同态方案设置
设置当前使用的方案是bfv

EncryptionParameters parms(scheme_type::bfv);

多项式阶数模设置
多项式的阶数模必须是2的正整数次幂,设置得越大,那么密文将越大,相应的计算就会更慢,但是能使用更复杂的计算。另外,官方推荐的至少大于等于1024。这个例子用的4096。

size_t poly_modulus_degree = 4096;
parms.set_poly_modulus_degree(poly_modulus_degree);

多项式系数模设置
接下来设置多项式的系数模,这是多个大素数相乘得到的大整数,每个素数最大为60bits(计算机目前64位,没法直接表示这个大整数,所以用多个相乘),每个素数是由一个类的实例表示的向量,系数的最大位长为它的素数因子位长之和。系数模越大,那么计算能力更强,但它的位长受限于多项式的阶数模,如下
image.png
上面的数字可通过查看native/src/seal/util/hestdparms.h 或者调用函数

  • CoeffModulus::MaxBitCount(poly_modulus_degree);

也可以直接调用默认的

  • CoeffModulus::BFVDefault(poly_modulus_degree);

所以设置多项式系数模就可以像下面一样设置

parms.set_coeff_modulus(CoeffModulus::BFVDefault(poly_modulus_degree));

明文模数设置
明文模数在bfv中不受限制,可以是任意的正整数,比如这里就取2的幂次可以使得运算简化。
明文的模数决定明文数据类型的size和乘法中的噪声预算的消耗,回想一下bfv中的密文是使用了类似模交换操作的,也就是乘以了 t / q t/q t/q来进行噪声缩放,从而达到控制噪声的目的,所以容易知道明文模数 t t t越大,将导致每一次乘法的噪声越大,也就是噪声预算消耗得越大,所以需要它尽可能的小来获取更好的性能。
这里估算的噪声预算大致是log2(coeff_modulus/plain_modulus) (bits)( log ⁡ q / t \log q/t logq/t
每一次乘法消耗的噪声预算大致是log2(plain_modulus) + (other terms)()

parms.set_plain_modulus(1024);

参数可用性校验
参数设置完成后,需要校验一下已设置的参数是否可用,本代码的例子计算的是 4 ( x 2 + 1 ) ( x + 1 ) 2 = 4 x 4 + 8 x 3 + 8 x 2 + 8 x + 4 4(x^2+1)(x+1)^2=4x^4+8x^3+8x^2+8x+4 4(x2+1)(x+1)2=4x4+8x3+8x2+8x+4

SEALContext context(parms);
/*Print the parameters that we have chosen.*/
print_line(__LINE__);
cout << "Set encryption parameters and print" << endl;
print_parameters(context);
cout << "Parameter validation (success): " << context.parameter_error_message() << endl;
cout << endl;
cout << "~~~~~~ A naive way to calculate 4(x^2+1)(x+1)^2. ~~~~~~" << endl;

校验结果如图
image.png

代码的最后(本来在代码的最后位置,这里我提到这里来了),展示了如果设置的参数有问题,seal库会输出为什么有问题。。。相当于一个不错的辅助作用,能帮助修改参数。

print_line(__LINE__);
cout << "An example of invalid parameters" << endl;
parms.set_poly_modulus_degree(2048);
context = SEALContext(parms);
print_parameters(context);
cout << "Parameter validation (failed): " << context.parameter_error_message() << endl << endl;

校验结果如图
image.png

密钥生成

主要位公钥 s k = s sk=s sk=s、私钥 p k = ( b = a s + e , a ) pk=(b=as+e,a) pk=(b=as+e,a)、评估密钥evk的生成,首先需要一个密钥生成类的实例来创建一个自动生成私钥的生成器,接着利用私钥来生成公钥。

公钥和私钥生成

KeyGenerator keygen(context);
SecretKey secret_key = keygen.secret_key();
PublicKey public_key;
keygen.create_public_key(public_key);

创建加密器
创建加密器,实例化一个加密器类

Encryptor encryptor(context, public_key);

同态计算生成
同态计算密文的时候需要用到这个类(实际运用中,它不会由私钥拥有方来构建)

Evaluator evaluator(context);

创建解密器
创建解密器,实例化一个解密器类

Decryptor decryptor(context, secret_key);

加密

再次说明这里加密的例子是 4 x 4 + 8 x 3 + 8 x 2 + 8 x + 4 4x^4+8x^3+8x^2+8x+4 4x4+8x3+8x2+8x+4,这里设置了x=6。在RLWE问题中我们可以把明文消息看作多项式的系数,这里的明文就是6,也就是只有一项常数项。另外,别忘了明文的模数是1024。
明文创建
下面的代码创建了一个包含常量6的明文多项式,接着将明文多项式变为一个字符串,其中多项式的系数为十六进制。

print_line(__LINE__);
uint64_t x = 6;
Plaintext x_plain(uint64_to_hex_string(x));
cout << "Express x = " + to_string(x) + " as a plaintext polynomial 0x" + x_plain.to_string() + "." << endl;

输出结果如下
image.png

加密操作
密文由两个或多个多项式形如( c t = ( c 0 , c 1 ) ) c_t=(c_0,c_1)) ct=(c0,c1)))组成的,系数要模多项式系数模数,也就是模( q q q

print_line(__LINE__);
Ciphertext x_encrypted;
cout << "Encrypt x_plain to x_encrypted." << endl;
encryptor.encrypt(x_plain, x_encrypted);

密文的大小由Ciphertext::size()给出,新加密的密文大小都是2

cout << "    + size of freshly encrypted x: " << x_encrypted.size() << endl;

查看噪声预算

cout << "    + noise budget in freshly encrypted x: " << decryptor.invariant_noise_budget(x_encrypted) << " bits"
         << endl;

解密

调用解密器

Plaintext x_decrypted;
cout << "    + decryption of x_encrypted: ";
decryptor.decrypt(x_encrypted, x_decrypted);
cout << "0x" << x_decrypted.to_string() << " ...... Correct." << endl;

输出结果如下
image.png
这里没有输出加密后的密文,我试了半天没输出成功(太菜了😇),找了一下原因是输出结果是二进制形式不是可阅读的,所以Ciphertexts类里面只提供了save(可以通过stringstream来保存为一个文件)和load(从文件里导出)方法,它没有提供一个类似Plaintext类里的to_string方法。
这是Ciphertexts里的save方法的说明。
image.png

同态计算

因为噪声预算会随着乘法深度(次数)的增加而增加,所以为了能够完成更多次的计算,一般选择最小化乘法次数
所以这里计算多项式 4 x 4 + 8 x 3 + 8 x 2 + 8 x + 4 4x^4+8x^3+8x^2+8x+4 4x4+8x3+8x2+8x+4的时候,选择使用 4 ( x 2 + 1 ) ( x + 1 ) 2 4(x^2+1)(x+1)^2 4(x2+1)(x+1)2,这样对比直接计算能将乘法的深度从4降到了2。
首先是计算 x 2 + 1 x^2+1 x2+1

print_line(__LINE__);
cout << "Compute x_sq_plus_one (x^2+1)." << endl;
Ciphertext x_sq_plus_one;
evaluator.square(x_encrypted, x_sq_plus_one);//Squares a ciphertext
Plaintext plain_one("1");
evaluator.add_plain_inplace(x_sq_plus_one, plain_one);//Add a ciphertext and a plaintext

同态乘法会导致密文扩张,精确的说,如果输出的两个密文大小为M和N,那么同态乘法后的密文大小将变为M+N-1,下面的代码输出了密文扩张大小和噪声消耗。

cout << "    + size of x_sq_plus_one: " << x_sq_plus_one.size() << endl;
cout << "    + noise budget in x_sq_plus_one: " << decryptor.invariant_noise_budget(x_sq_plus_one) << " bits"
      << endl;

输出一下解密的结果

Plaintext decrypted_result;
cout << "    + decryption of x_sq_plus_one: ";
decryptor.decrypt(x_sq_plus_one, decrypted_result);
cout << "0x" << decrypted_result.to_string() << " ...... Correct." << endl;

输出结果如下
image.png
可以看到这里乘法后,密文从2项变到了3项,而噪声消耗了55-33=22个比特,因为还有噪声预算能够消耗,也就是还没达到噪声界限,所以能够正确解密。

接着计算 ( x + 1 ) 2 (x+1)^2 (x+1)2

print_line(__LINE__);
cout << "Compute x_plus_one_sq ((x+1)^2)." << endl;
Ciphertext x_plus_one_sq;
evaluator.add_plain(x_encrypted, plain_one, x_plus_one_sq);
evaluator.square_inplace(x_plus_one_sq);
cout << "    + size of x_plus_one_sq: " << x_plus_one_sq.size() << endl;
cout << "    + noise budget in x_plus_one_sq: " << decryptor.invariant_noise_budget(x_plus_one_sq) << " bits"
     << endl;
cout << "    + decryption of x_plus_one_sq: ";
decryptor.decrypt(x_plus_one_sq, decrypted_result);
cout << "0x" << decrypted_result.to_string() << " ...... Correct." << endl;

这里需要提一下的是add_plain_inplace()、square_inplace(0与add_plain()、square()函数的区别,带有inplace的是需要指定一个参数用来接收结果的,不带的就是直接保存在第一个参数里的。
输出结果如下
image.png
这里的噪声消耗也是22比特

最后是计算两个的乘积

print_line(__LINE__);
cout << "Compute encrypted_result (4(x^2+1)(x+1)^2)." << endl;
Ciphertext encrypted_result;
Plaintext plain_four("4");
evaluator.multiply_plain_inplace(x_sq_plus_one, plain_four);
evaluator.multiply(x_sq_plus_one, x_plus_one_sq, encrypted_result);
cout << "    + size of encrypted_result: " << encrypted_result.size() << endl;
cout << "    + noise budget in encrypted_result: " << decryptor.invariant_noise_budget(encrypted_result) << " bits"
     << endl;
cout << "NOTE: Decryption can be incorrect if noise budget is zero." << endl;

cout << endl;
cout << "~~~~~~ A better way to calculate 4(x^2+1)(x+1)^2. ~~~~~~" << endl;

这里本来是没有输出结果的,看到噪声预算还有剩余,就加了点代码看看结果。输出结果如下
image.png
大概消耗了33-4=29比特的噪声预算,式子越复杂消耗的越多。两个三项的相乘会得到五项。
x = 6 x=6 x=6,表达式是 4 x 4 + 8 x 3 + 8 x 2 + 8 x + 4 = 4 ( x 2 + 1 ) ( x + 1 ) 2 4x^4+8x^3+8x^2+8x+4=4(x^2+1)(x+1)^2 4x4+8x3+8x2+8x+4=4(x2+1)(x+1)2,我们期待解密结果是 4 × 37 × 49 = 7252 m o d    1024 = 84 4\times37\times49=7252 \mod 1024 = 84 4×37×49=7252mod1024=84换算为十六进制就是0x54,所以解密正确。

重线性化

重线性化是为了减少密文的规模扩张,并将密文规模还原到近似乘法之前的状况。
重线性化需要重线性化密钥rlk(其他方案里也叫做evk),它是由一层一层计算出来的一连串的密钥链,每一层都需要对应每一层的密钥,主要是用来消除密钥乘积的二次项,但是会诞生一定的噪声。
假设一个二阶的密文为 [ c 0 , c 1 , c 2 ] [\mathbf c_0,\mathbf c_1,\mathbf c_2] [c0,c1,c2],线性化的目的是将它变成一阶的 [ c 0 ′ , c 1 ′ ] [\mathbf c'_0,\mathbf c'_1] [c0,c1] [ c 0 + c 1 ⋅ s + c 2 ⋅ s 2 ] q = [ c 0 ′ + c 1 ′ ⋅ s + r ] q \left[\mathbf{c}_{0}+\mathbf{c}_{1} \cdot \mathbf{s}+\mathbf{c}_{2} \cdot \mathbf{s}^{2}\right]_{q}=\left[\mathbf{c}_{0}^{\prime}+\mathbf{c}_{1}^{\prime} \cdot \mathbf{s}+\mathbf{r}\right]_{q} [c0+c1s+c2s2]q=[c0+c1s+r]q,其中 r \mathbf r r很小。
首先需要生成重线性化密钥rlk

print_line(__LINE__);
cout << "Generate relinearization keys." << endl;
RelinKeys relin_keys;
keygen.create_relin_keys(relin_keys);

接下来重新计算 4 ( x 2 + 1 ) ( x + 1 ) 2 4(x^2+1)(x+1)^2 4(x2+1)(x+1)2,并在每一次乘法后都调用重线性化,还原密文规模。

print_line(__LINE__);
cout << "Compute and relinearize x_squared (x^2)," << endl;
cout << string(13, ' ') << "then compute x_sq_plus_one (x^2+1)" << endl;
Ciphertext x_squared;
evaluator.square(x_encrypted, x_squared);
cout << "    + size of x_squared: " << x_squared.size() << endl;
evaluator.relinearize_inplace(x_squared, relin_keys);
cout << "    + size of x_squared (after relinearization): " << x_squared.size() << endl;
evaluator.add_plain(x_squared, plain_one, x_sq_plus_one);
cout << "    + noise budget in x_sq_plus_one: " << decryptor.invariant_noise_budget(x_sq_plus_one) << " bits"
     << endl;
cout << "    + decryption of x_sq_plus_one: ";
decryptor.decrypt(x_sq_plus_one, decrypted_result);
cout << "0x" << decrypted_result.to_string() << " ...... Correct." << endl;

print_line(__LINE__);
Ciphertext x_plus_one;
cout << "Compute x_plus_one (x+1)," << endl;
cout << string(13, ' ') << "then compute and relinearize x_plus_one_sq ((x+1)^2)." << endl;
evaluator.add_plain(x_encrypted, plain_one, x_plus_one);
evaluator.square(x_plus_one, x_plus_one_sq);
cout << "    + size of x_plus_one_sq: " << x_plus_one_sq.size() << endl;
evaluator.relinearize_inplace(x_plus_one_sq, relin_keys);
cout << "    + noise budget in x_plus_one_sq: " << decryptor.invariant_noise_budget(x_plus_one_sq) << " bits"
     << endl;
cout << "    + decryption of x_plus_one_sq: ";
decryptor.decrypt(x_plus_one_sq, decrypted_result);
cout << "0x" << decrypted_result.to_string() << " ...... Correct." << endl;

print_line(__LINE__);
cout << "Compute and relinearize encrypted_result (4(x^2+1)(x+1)^2)." << endl;
evaluator.multiply_plain_inplace(x_sq_plus_one, plain_four);
evaluator.multiply(x_sq_plus_one, x_plus_one_sq, encrypted_result);
cout << "    + size of encrypted_result: " << encrypted_result.size() << endl;
evaluator.relinearize_inplace(encrypted_result, relin_keys);
cout << "    + size of encrypted_result (after relinearization): " << encrypted_result.size() << endl;
cout << "    + noise budget in encrypted_result: " << decryptor.invariant_noise_budget(encrypted_result) << " bits"
     << endl;

cout << endl;
cout << "NOTE: Notice the increase in remaining noise budget." << endl;

接着是看看解密的结果

print_line(__LINE__);
cout << "Decrypt encrypted_result (4(x^2+1)(x+1)^2)." << endl;
decryptor.decrypt(encrypted_result, decrypted_result);
cout << "    + decryption of 4(x^2+1)(x+1)^2 = 0x" << decrypted_result.to_string() << " ...... Correct." << endl;
cout << endl;

来看看输出的结果
image.png
可以看到由于多了重线性化操作,密文的大小也有5减少到了2,而最终表达式的噪声预算还剩下10比特多于没有重线性化的4比特。(怎么两个因子消耗的预算还是没变化~~~😅,算了,whatever,最后的结果是优化了的)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/43158.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

flutter系列之:在flutter中使用流式布局

文章目录简介Flow和FlowDelegateFlow的应用总结简介 我们在开发web应用的时候&#xff0c;有时候为了适应浏览器大小的调整&#xff0c;需要动态对页面的组件进行位置的调整。这时候就会用到flow layout&#xff0c;也就是流式布局。 同样的&#xff0c;在flutter中也有流式布…

真题集P93---2017年计专真题

真题集P93---2017年计专真题六思路&#xff1a;模拟代码七思路一&#xff1a;哈希表法二&#xff1a;排序法 (利用排序去重)三&#xff1a;拓展代码&#xff08;仅思路一&#xff09;六 思路&#xff1a;模拟 1、接口介绍 int turnNum(int num[], int nums)&#xff1a;拿来一…

数据库约束

约束概述 为什么需要约束 数据完整性&#xff08;Data Integrity&#xff09;是指数据的精准性&#xff08;Accuracy&#xff09;和可靠性&#xff08;Reliability&#xff09;。它是防止数据库中存在不符合语义规定的数据和防止因错误信息的输入输出造成无效操作或错误信息而…

种草软文怎么写?分享一些超实用的种草软文写作技巧。

一提起“种草”这个词语&#xff0c;想必很多小伙伴都不陌生&#xff0c;我们都有“被种草”的经历&#xff0c;指的就是把一件商品推荐给大家&#xff0c;分享给需要的人&#xff0c;然后促成销售转化的过程。 在一些主流的内容平台&#xff0c;比如知乎、小红书、得物、公众…

Android 9.0 MediaPlayer播放流程分析

1.MediaPlayer初始化流程 EventHandler是后面处理数据回调的handler. 在AudioFlinger.cpp中获取nextUniqueId&#xff1a; audio_unique_id_t AudioFlinger::nextUniqueId(audio_unique_id_use_t use) {// This is the internal API, so it is OK to assert on bad parameter.…

18-1、k8s 对外服务之ingress

一、什么是ingress 原来的项目是部署在一台电脑上的&#xff0c;这样爬取速度虽然很快&#xff0c;但是我们还能提升&#xff0c;联想到分布式的思想&#xff0c;我们是否可以通过多台电脑进行配合爬取&#xff0c;这样我们的爬取速度就能大幅度提升。 …

【Mysql】数据库的基本操作和表的增删改查

本章内容是,用sql语言实现对数据库的基本操作和表的基本操作 文章目录前言1. 数据库的基本操作1.1 创建数据库1.2 查看数据库1.3 选中数据库1.4 删除数据库2. 数据库基本数据类型3. 表的基本操作3.1 创建表3.2 显示数据库中的表3.3 查看表的构造3.4 删表4. 表的增删改查4.1 增加…

基于PHP+MySQL汽车查询系统的设计与实现

随着时代的发展,汽车已经逐渐成为人们代步的主要工具之一,按时因为工业的发展,汽车的品牌和型号也层出不穷,如此多的汽车信息如何能够让爱车人士更好的 汽车查询系统的主要功能包含&#xff1a;汽车的类别管理、汽车的信息管理、留言管理、用户的管理等。网站分为管理员、会员用…

开放与融合趋势下,工业互联网安全破圈之道

作者 | 伍杏玲 出品 | CSDN 在工业 4.0 和数字经济的发展促进下&#xff0c;工业互联网作为连接工业经济的重要要素&#xff0c;成为推动数字经济发展的重要基础设施。据统计&#xff0c;到 2025 年&#xff0c;全球 IoT 连接设备数量达 519 亿。 然而在 OT 和 IT 融合趋势下…

1721. 交换链表中的节点-仅遍历一次链表-考研满分答案

1721. 交换链表中的节点-仅遍历一次链表 给你链表的头节点 head 和一个整数 k 。 交换 链表正数第 k 个节点和倒数第 k 个节点的值后&#xff0c;返回链表的头节点&#xff08;链表 从 1 开始索引&#xff09;。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], k…

第1关:Hbase数据库的安装

在安装HBase之前你需要先安装Hadoop和Zookeeper&#xff0c;如果你还没有安装可以通过这两个实训来学习&#xff1a;Hadoop安装与配置&#xff0c;Zookeeper安装与配置。 本次实训的环境已经默认安装好了Hadoop&#xff0c;接下来我们就开始安装配置HBase吧。 HBase安装 HBas…

浙大MPA常规批复试上岸经验分享

在经历笔试和面试的备考后&#xff0c;去年终于来到了复试环节&#xff0c;好在通过自己的不懈努力和不放松的精神&#xff0c;最终成功上岸&#xff0c;现在把个人的备考经验做整理为大家做个参考&#xff01; 一、复试前准备&#xff1a;在正式复试前建议一定要对浙大MPA项目…

【综合评价分析】topsis评价 原理+完整MATLAB代码+详细注释+操作实列

【综合评价分析】topsis评价 原理完整MATLAB代码详细注释操作实列 文章目录 1、TOPSIS法的原理 2、TOPSIS法案例分析 3.建立模型并求解 3.1数据预处理 3.2代码实现数据预处理 3.3 本案例中数据预处理的运用 4.计算距离和评价指标 4.1 代码 4.2 运行结果 5.总结 1、T…

基于Jsp的OA企业人事管理系统【论文、数据库设计、源码、开题报告】

数据库脚本下载地址&#xff1a; https://download.csdn.net/download/itrjxxs_com/86500769 主要使用技术 SpringspringMVCmybatisjspccsjsMysqlTomcat 功能介绍 部门管理&#xff1a;支持对部门信息&#xff08;部门名称、部门类型、电话、传真、描述、上级部门&#xff…

外卖项目(项目优化1)10---缓存优化

优化前面已经创建好的项目&#xff1a;将一些问题进行优化处理。本节主要解决是下面的问题。 前面的的项目&#xff0c;是将短信验证码等数据缓存到session中&#xff0c;该部分进行优化后&#xff0c;是将数据内容缓存到Redis中。 目录 一、使用git管理代码&#xff08;对ID…

Guava环境设置

Guava本地环境设置 这部分指导如何下载和设置Java在机器上。请按照以下步骤来设置环境。 Java SE免费提供链接&#xff1a;下载Java。所以&#xff0c;根据操作系统下载对应版本。 按照说明下载java和运行.exe 在机器上&#xff0c;并按说明安装Java。一旦机器上安装了Java&…

Leetcode原题电话号码的字母组合的两种解法【BFS-DFS】

来源&#xff1a;LeetCode 第17题【公众号&#xff1a;数据结构和算法】 给定一个仅包含数字2-9的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按任意顺序返回。给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意1不对应任何字母。 示例1&am…

浅析分布式数据库

前言 随着信息技术的迅猛发展&#xff0c;各行各业产生的数据量呈爆炸式增长&#xff0c;传统集中式数据库的局限性在面对大规模数据处理中逐渐显露&#xff0c;从而分布式数据库应运而生。分布式数据库是在集中式数据库的基础上发展起来的&#xff0c;是分布式系统与传统数据…

java刷题day 06

一. 单选题&#xff1a; 解析&#xff1a;最终类也叫密封类&#xff0c;是被final修饰的类&#xff0c;不能被继承 解析&#xff1a; A&#xff1a;6入&#xff0c;5 入&#xff0c;5出&#xff0c;4入&#xff0c;4出&#xff0c;3入&#xff0c;3出&#xff0c;6出&#xff0…

SpringBoot 封装 HBase 操作工具类

最近项目中用到了Hbase相关的操作并封装成工具类&#xff0c;我的Hbase服务器端版本是2.1.0&#xff0c;图示如下&#xff1a; 特此记录便于日后查阅。 一、pom.xml 依赖 <dependency><groupId>org.apache.hbase</groupId><artifactId>hbase-shaded-…