当前位置: 首页 > news >正文

【密码学代码分享】突破ECDSA算法封装--JS无三方包纯手写ECDSA

ECDSA(Elliptic Curve Digital Signature Algorithm)是一种基于椭圆曲线密码学的数字签名算法。它用于确保数字数据的完整性和身份验证,通常在信息安全和加密通信中使用。在日常使用中,通常会使用一些函数库来实现完成这个算法的功能,但是有部分情况是需要自高度自定义ECDSA相关逻辑的,这里分享JavaScript语言在不借助第三方库的前提下纯手写的ECDSA算法代码,并对其实现原理进行解释。


文章目录

  • 1.ECDSA算法原理
      • 1.密钥生成算法
      • 2.签名算法
      • 3.验证算法
      • 4.正确性证明
  • 2.纯手写ECDSA 应用场景说明
  • 3.JS实现的几大主要问题
    • 1.大数运算
    • 2.椭圆曲线群上的加法和乘法
  • 4.完整代码(以secp256k1为例)


1.ECDSA算法原理

这里我直接copy之前自己文章中对其的详细描述。

ECDSA(Elliptic Curve Digital Signature Algorithm) 是使用椭圆曲线密码(ECC)对数字签名算法(DSA)的模拟。

ECDSA安全性依赖于基于椭圆曲线的有限群上的离散对数难题。与基于RSA的数字签名和基于有限域离散对数的数字签名相比,在相同的安全强度条件下,ECDSA方案具有如下特点:

  • 签名长度短
  • 密钥存储空间小
  • 特别适用于存储空间有限、带宽受限、要求高速实现的场合(如在智能卡中应用)。

1.密钥生成算法

G F ( p ) GF(p) GF(p)为有限域, E E E是有限域上 G F ( p ) GF(p) GF(p)上的椭圆曲线。选择 E E E上一点 G ∈ E G\in E GE G G G的阶为满足安全要求的素数 n n n,即 n G = O nG=O nG=O O O O为无穷远点)。选择一个随机数 d d d d ∈ [ 1 , n − 1 ] d \in [1, n-1] d[1,n1],计算 Q Q Q,使得 Q = d G Q=dG Q=dG,那么公钥为 ( n , Q ) (n, Q) (n,Q),私钥为 ( d ) (d) (d)

2.签名算法

签名者 A l i c e Alice Alice对消息 m m m签名的过程如下:

  • (1)随机选取整数 k k k k ∈ [ 1 , n − 1 ] k \in [1,n-1] k[1,n1],计算 k G = ( x , y ) kG=(x,y) kG=(x,y) r ≡ x ( m o d n ) r \equiv x(mod \ n) rx(mod n)
  • (2)计算 e = h ( m ) e=h(m) e=h(m) h h h为安全的散列函数;
  • (3)计算 s ≡ ( e + r d ) k − 1 ( m o d n ) s \equiv (e+rd)k^{-1}(mod \ n) s(e+rd)k1(mod n)。如果 r = 0 r=0 r=0 s = 0 s=0 s=0,则令选取随机数 k k k,重新执行上面的过程。消息 m m m的签名为 ( r , s ) (r,s) (r,s)

3.验证算法

签名接收者 B o b Bob Bob对消息 m m m签名 ( r , s ) (r,s) (r,s)的验证过程如下:

  • (1)计算 e = h ( m ) e=h(m) e=h(m)
  • (2)计算 u ≡ s − 1 e ( m o d n ) u \equiv s^{-1}e(mod \ n) us1e(mod n) v ≡ s − 1 r ( m o d n ) v \equiv s^{-1}r(mod \ n) vs1r(mod n) ( x 1 , y 1 ) = u G + v Q (x_{1}, y_{1})=uG+vQ (x1,y1)=uG+vQ r 1 ≡ x 1 ( m o d n ) r_{1} \equiv x_{1}(mod \ n) r1x1(mod n)
  • (3)若 r = r 1 r=r_{1} r=r1,则签名有效;否则,签名无效。

4.正确性证明

由于
Q = d G s ≡ ( e + r d ) k − 1 ( m o d n ) k G = ( x , y ) u ≡ s − 1 e ( m o d n ) v ≡ s − 1 r ( m o d n ) ( x 1 , y 1 ) = u G + v Q Q=dG\\ s \equiv (e+rd)k^{-1}(mod \ n)\\ kG=(x,y)\\ u \equiv s^{-1}e(mod \ n)\\ v \equiv s^{-1}r(mod \ n)\\ (x_{1},y_{1})=uG+vQ Q=dGs(e+rd)k1(mod n)kG=(x,y)us1e(mod n)vs1r(mod n)(x1,y1)=uG+vQ
则有:
k ≡ ( e + r d ) s − 1 ≡ s − 1 e + s − 1 ≡ u + v d ( m o d n ) ( x , y ) = k G = u G + v d G = u G + v Q = ( x 1 , y 1 ) r 1 = x 1 m o d n = x m o d n = r k \equiv (e+rd)s^{-1} \equiv s^{-1}e+s^{-1} \equiv u+vd (\ mod \ n)\\ (x,y)=kG=uG+vdG=uG+vQ=(x_{1},y_{1})\\ r_{1}=x_{1} \ mod \ n = x \ mod \ n=r k(e+rd)s1s1e+s1u+vd( mod n)(x,y)=kG=uG+vdG=uG+vQ=(x1,y1)r1=x1 mod n=x mod n=r

2.纯手写ECDSA 应用场景说明

从头开始手写的ECDSA算法毫无疑问在算法的效率上以及拓展性上要比封装好的函数库低不少,那么为什么还需要自己纯手写实现ECDSA的逻辑呢?如果存在以下需求,从头手写是一个好的选择。

  • 1.如果需要对算法的逻辑进行高度的定制,常见的函数库提供的拓展功能都无法满足需求。例如需要高度定义其随机数的产生(部分函数库提供对随机数函数的拓展,但是拓展能力有限)、需要取到中间变量做一些其他的操作。
  • 2.想在客户端隐蔽的使用ECDSA算法,不想直接使用三方包(这样特征太明显)。从攻击的角度来看,对着一个经过混淆后纯手写的ECDSA算法代码,如果不是对算法特别熟悉,分析难度还是不小的。

当你想使用自己手写的ECDSA的时候,就必须接受不能将其作为一个广泛应用、高频调用的算法包,因为算法的效率会差不少。

3.JS实现的几大主要问题

我们回顾下上面算法的细节,发现在JavaScript中实现,主要有两个方面的问题需要解决:

  • 1.大数运算如何实现。
  • 2.如何做椭圆曲线群上的加法和乘法操作。

1.大数运算

对于第一个问题,自从ECMAScript 2020(ES11)引入BigInt类型以来,JavaScript原生支持大整数运算。BigInt类型用于表示任意精度的整数,可以执行标准的算术操作。不需要额外的库,但在一些老版本的浏览器中可能不受支持。

2.椭圆曲线群上的加法和乘法

对于第二个问题,我们尝试推导下。

首先明确,椭圆曲线上加法的定义,假设有一根椭圆曲线 E : y 2 = x 3 + a x + b E: y^2=x^3+ax+b E:y2=x3+ax+b,其中, a a a b b b 是曲线的参数, x x x y y y 是曲线上的点坐标。在椭圆曲线上的加法操作定义如下:

  • 点加法:给定曲线上的两个点 P P P Q Q Q,点加法操作 P + Q P+Q P+Q的结果是曲线上另一个点 R R R
  • 加法规则:
    • 如果 P P P Q Q Q是相同的点,那么点加法操作是对 P P P的倍乘,即 P + P P+P P+P
    • 如果 P P P Q Q Q是不同的点,并且它们不是垂直于 x 轴的,那么点加法操作的步骤是:连接 P P P Q Q Q,计算直线 L L L,找到直线与曲线 E E E的交点 R R R R R R关于x轴的对称点 − R -R R就是 P + Q P+Q P+Q的结果。
    • 特殊情况:在某些情况下,点加法操作可能会遇到特殊情况,例如 P P P Q Q Q 垂直于 x 轴,或 P P P Q Q Q 是互为相反元素。这些情况需要根据具体的椭圆曲线参数和实现进行处理。

从上可以看出,我们主要需要处理的情况就是找到两点所确定的直线和曲线的交点,对于这个交点,我们尝试建立方程进行解出。

P P P的坐标为 ( x 1 , y 1 ) (x_{1},y_{1}) (x1,y1) Q Q Q的坐标为 ( x 2 , y 2 ) (x_{2},y_{2}) (x2,y2),两点所确立的直线为: y − y 1 = k ( x − x 1 ) + b y-y_{1}=k(x-x_{1})+b yy1=k(xx1)+b k = x 1 − x 2 y 1 − y 2 k= \frac{x_{1}-x{2}}{y_{1}-y_{2}} k=y1y2x1x2

通过求导的方式计算出 E E E上某点的斜率:

F ( x ) = x 3 + a x + b − y 2 F x ′ = 3 x 2 + a F y ′ = − 2 y k = − F y ′ F x ′ = 3 x 2 + a 2 y F(x)=x^3+ax+b-y^2\\F_{x}'=3x^2+a\\F_{y}'=-2y\\k=-\frac{F_{y}'}{F_{x}'}=\frac{3x^2+a}{2y} F(x)=x3+ax+by2Fx=3x2+aFy=2yk=FxFy=2y3x2+a

联立上式子,可以解得直线 L L L与曲线 E E E的交点:
x 3 = k 2 − x 1 − x 2 y 3 = y 1 + k ( x 2 − x 1 ) x_{3}=k^2-x_{1}-x_{2}\\y_{3}=y_{1}+k(x_{2}-x_{1}) x3=k2x1x2y3=y1+k(x2x1)

椭圆曲线上的乘法,只需采用快速幂的方式进行加法运算即可。

至此,用JS纯手写的两大核心问题都解决了,具体细节处理见代码。

4.完整代码(以secp256k1为例)

  • 下面代码已经过完整测试并以用于具体生产项目中。
  • 其中签名产生的v值为以太坊中相关约定,不需要可以去除。
  • 签名值保证了s一定小于n的一半(以太坊相关约定),所以提取随机数的时候是会有两种可能。不需要该处逻辑可以去除。

// 椭圆曲线点定义
class Point{constructor(x,y){this.x = BigInt(x);this.y = BigInt(y);}
}
// secp256k1曲线
const secp_p = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F'); // 2n ** 256n - 2n ** 32n - 977n;
const secp_n = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141'); // 2n ** 256n - 432420386565659656852420866394968145599n;
const secp_a = BigInt(0);
const Gx = BigInt('0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798');
const Gy = BigInt('0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8');
const secp_G = new Point(Gx, Gy);function get_inv(x, y, p){return x === 1n ? 1n : get_inv(y % x, y, p) * (y - y / x) % p;
}// 求模逆元
function modInverse(b,p){return get_inv(b%p,p,p);
}// 最大公约数
function get_gcd(x,y){return y ? get_gcd(y,x%y):x;
}// 在有限域下计算的求模
function mod(a, b) {const result = a % b;return result >= 0n ? result : b + result;
}// 椭圆曲线点加法
function point_add(pa, pb, p){// 零点相加if(pa.x === 0n && pa.y === 0n || pb.x === 0n && pb.y === 0n){return new Point(pa.x+pb.x, pa.y+pb.y);}// 对称点相加if(pa.x === pb.x && pa.y !== pb.y){return new Point(0, 0);}// 定义斜率k的分母和分子let k, k_num, k_den;// k的分子分母计算if(pa.x === pb.x && pa.y === pb.y){// 自己和自己相加 斜率为该点切线k_num = 3n * pa.x * pa.x + secp_a;k_den = 2n * pa.y;} else {// 两点确定斜率k_num = pa.y - pb.y;k_den = pa.x - pb.x;}// k符号记载let neg = 0;if(k_num * k_den < 0n){neg = 1;k_num = k_num > 0n ? k_num : -k_num;k_den = k_den > 0n ? k_den : -k_den;}// k化简分子分母let gcd = get_gcd(k_num, k_den);k_num /= gcd;k_den /= gcd;// 分母不为1,计算逆元if(k_den !== 1n){k_den = modInverse(k_den, p);}k = k_num * k_den % p;if(neg === 1) {k = -k;}// 计算最终结果let x3 = mod(k * k - pa.x - pb.x, p);let y3 = mod(k * (pa.x - x3) - pa.y, p);return new Point(x3, y3);
}// 椭圆曲线点快速乘法
function point_mul(n, g, p){n = BigInt(n)let ans = new Point(0, 0);while(n > 0n){if(n & 1n){ans = point_add(ans, g, p);}g = point_add(g, g, p);n >>= 1n;}return ans;
}export default {// 由私钥生成公钥generatePublicKey(privateKey) {// 私钥类型校验if(typeof privateKey != 'bigint') {throw new Error("私钥不是BigInt类型");}const publicKey = point_mul(privateKey, secp_G, secp_p);return publicKey;},ecSign(privateKey, msgHash, k) {// k类型校验if(typeof k != 'bigint') {throw new Error("随机数k不是BigInt类型");}// k大小校验if(k < 1n || k > secp_n - 1n) {throw new Error("随机数k不符合要求");}// 私钥类型校验if(typeof privateKey != 'bigint') {throw new Error("私钥不是BigInt类型");}// msg校验// if(typeof msg != 'string') {// 	throw new Error("msg不是string类型");// }const d = privateKey;//const e = keccak256Int(msg);const e = BigInt("0x" + msgHash);const kG = point_mul(k, secp_G, secp_p);const r = kG.x;var s = modInverse(k, secp_n) * (e + r * d) % secp_n;if(r === 0n || s === 0n) {throw new Error("随机数k不符合要求");}const v = (kG.y % 2n) ^ (s * 2n < secp_n ? 0n : 1n);if(s * 2n >= secp_n) {s = secp_n - s;}return [r, s, v];},ecVerify(publicKey, msgHash, sign) {// msg校验// if(typeof msg != 'string') {// 	throw new Error("msg不是string类型");// }const r = sign[0];const s = sign[1];const Q = publicKey;//const e = keccak256Int(msg);const e = BigInt("0x" + msgHash);const s_inv = modInverse(s, secp_n);const u = s_inv * e % secp_n;const v = s_inv * r % secp_n;const uG = point_mul(u, secp_G, secp_p);const vQ = point_mul(v, Q, secp_p);const uG_add_vQ = point_add(uG, vQ, secp_p);const r1 = uG_add_vQ.x;return r === r1;},// 提取ECDSA随机数 s有两个可能,会产生两个可能的k// privateKey msgHash 都是bigint  sign = [r, s]extractRandomK(privateKey, msgHash, sign) {// 私钥类型校验if(typeof privateKey != 'bigint') {throw new Error("私钥不是BigInt类型");}// msg校验// if(typeof msg != 'string') {// 	throw new Error("msg不是string类型");// }function recoverK(s) {const d = privateKey;const r = sign[0];//const s = secp_n - sign[1];//const e = keccak256Int(msg);const e = BigInt("0x" + msgHash);const s_inv = modInverse(s, secp_n);const k = s_inv * (e + r * d) % secp_n;return k;}return [recoverK(sign[1]), recoverK(secp_n - sign[1])];},
}

ATFWUS 2023-09-02


相关文章:

【密码学代码分享】突破ECDSA算法封装--JS无三方包纯手写ECDSA

ECDSA&#xff08;Elliptic Curve Digital Signature Algorithm&#xff09;是一种基于椭圆曲线密码学的数字签名算法。它用于确保数字数据的完整性和身份验证&#xff0c;通常在信息安全和加密通信中使用。在日常使用中&#xff0c;通常会使用一些函数库来实现完成这个算法的功…...

stable diffusion实践操作-文生图

本文专门开一节写文生图相关的内容&#xff0c;在看之前&#xff0c;可以同步关注&#xff1a; stable diffusion实践操作 正文 1 liblib SD1.5底模 lora(baihuaniang_1.0) 详细信息&#xff1a; 底模&#xff1a;SD 1.5 Lora:baihuaniang_1.0 正向提示词&#xff1a; Best …...

Spring容器及实例化

一、前言 Spring 容器是 Spring 框架的核心部分&#xff0c;它负责管理和组织应用程序中的对象&#xff08;Bean&#xff09;。Spring 容器负责创建、配置和组装这些对象&#xff0c;并且可以在需要时将它们提供给应用程序的其他部分。 Spring 容器提供了两种主要类型的容器&…...

# Go学习-Day9

文章目录 Channel声明存入取出一个简单的死锁分析 个人博客&#xff1a;CSDN博客 Channel Channel本质是一个队列多goroutine访问时不需要加锁&#xff0c;Channel天然线程安全channel有类型&#xff0c;只能写入相同类型channel是引用类型channel必须初始化才能写入数据&…...

chatGPT如何在Java中使用

ChatGPT是一个基于GPT模型的聊天机器人平台&#xff0c;它提供了API接口&#xff0c;可以通过HTTP请求与之交互。您可以使用Java编写程序&#xff0c;通过HTTP请求与ChatGPT进行交互&#xff0c;实现聊天机器人的功能。 具体步骤如下&#xff1a; 1、注册ChatGPT账号并创建应…...

vue+axios——创建多个实例共用请求拦截器和响应拦截器(403错误信息不提示bug解决)——基础积累

创建多个实例共用请求拦截器和响应拦截器&#xff1a;使用的是函数的继承&#xff0c;也就是call()方法&#xff0c;这个方法第一个参数就是this,后面的参数可以是一个也可以是多个。最后一定要记得要return出去&#xff0c;否则接口是拿不到数据的。 import axios from axios…...

全球免费编程教育网站:Code.org

全球免费编程教育网站&#xff1a;Code.org 官网地址注册使用 你还在为小朋友的编程教育而发愁吗&#xff1f; 你还在为小朋友放假无聊而头疼吗&#xff1f; 他来了他来了&#xff0c;全球免费编程教育网站来了。 2013年成立的Code.org是一个非营利组织。 它致力于为年轻女子、…...

构造函数与成员变量初始化

C自学精简教程 目录(必读) 1 为什么需要定义构造函数&#xff1f; 构造函数主要用来给成员变量初始化。 让类对象有一个良好的开始状态。 2 构造函数初始化成员变量 下面我们来完善上一篇文章中的几个构造函数。 让这些构造函数完成给成员变量初始化的职责。 为此&#…...

使用Go env命令设置Go的环境

文章目录 前言Linux的设置Windlows设置Go version > 1.13 当你的GO的版本大于1.13的时候 Set environment variable allow bypassing the proxy for selected modules 前言 在进行Go开发的时候&#xff0c;设置Go的环境变量信息是必须的。下面介绍windows和Linux&#xff0…...

目标检测YOLO实战应用案例100讲-道路场景下目标检测与分割模型的压缩研究与实现(续)

目录 道路场景下目标检测与语义分割模型的改进研究 3.1 道路场景数据集分析 3.1.1 Cityscapes数据集...

b站手机缓存文件转MP4

b站缓存的文件 音频、视频、弹幕是分开的 这里我只用到了音频和视频所以只介绍这一部分 b站的缓存视频文件和路径结构如下 默认缓存路径 内部存储\Android\data\tv.danmaku.bilil\download\89720189 文件夹结构 文件夹 c_738583 这是单个视频的缓存文件夹 进入c_738583文件夹…...

一个集成的BurpSuite漏洞探测插件1.2

4、DNSLog查询漏报 注&#xff1a;扫描结束后才会在BurpSuite的Target、Dashboard模块显示高危漏洞&#xff0c;进程扫描中无法进行同步&#xff0c;但可以在插件中查看&#xff08;涉及到DoPassive方法问题&#xff09;。...

[FMMPEG] parse与 demuxer

FFmpeg源码分析&#xff1a;av_parser_parse2()解析数据包 ffmpeg 4.3添加自定义demuxer ffmpeg API基础...

【Bug】Ubuntu 有线设置打不开无反应

前言&#xff1a; 突然有线设置就没法启用了&#xff0c;但是能联网&#xff0c;能查看ip 解决&#xff1a; 最后安装了一个新的依赖包&#xff1a; sudo apt install gnome-control-center 然后就可以了 还有一个方法&#xff0c;没试过&#xff0c;但感觉有点道理的&#…...

迈向无限可能, ATEN宏正领跑设备切换行业革命!

随着互联网在各个领域的广泛应用,线上办公这一不受时间和地点制约、不受发展空间限制的办公模式开始广受追捧,预示着经济的发展正朝着新潮与活跃的方向不断跃进。当然,在互联网时代的背景下,多线程、多设备的线上办公模式也催生了许多问题:多设备间无法进行高速传输、切换;为保…...

Ubuntu18.04:ORB-SLAM3使用数据集构建地图和保存点云地图

文章目录 保存地图方法一&#xff1a;使用ORB-SLAM3自带的保存方法&#xff08;oea后缀文件&#xff09;保存地图方法二&#xff1a;使用PCL库保存为PCD类型地图文件安装PCL库&#xff1a;取巧方法&#xff1a;CMakeLists.txt 文件修改内容&#xff1a;&#xff08;向该文件内添…...

找到自制电子杂志的方法了,快来看看?

终于找到自制电子杂志的方法了&#xff0c;这真是令人兴奋啊&#xff01;现在&#xff0c;我们可以利用这个方法来创造属于自己的电子杂志&#xff0c;将我们的想法和创意以独特的方式展现给世界。 1.需要一个电子杂志制作工具 市面上有许多专门用于制作电子杂志的工具&#x…...

Django请求的生命周期

Django请求的生命周期是指: 当用户在浏览器上输入URL到用户看到网页的这个时间段内&#xff0c;Django后台所发生的事情。 直白的来说就是当请求来的时候和请求走的阶段中&#xff0c;Django的执行轨迹。 一个完整的Django生命周期: 用户从客户端发出一条请求以后&#xff…...

Kotlin 中 OkHttp 使用及解析

build.gradle dependencies {//OkHttpimplementation com.squareup.okhttp3:okhttp:4.9.0 } 简单使用例子 val okHttpClient OkHttpClient.Builder().connectTimeout(Duration.ofSeconds(10)).readTimeout(Duration.ofSeconds(10)).writeTimeout(Duration.ofSeconds(10)).re…...

【C++代码】用栈实现队列,用队列实现栈--代码随想录

队列是先进先出&#xff0c;栈是先进后出。卡哥给了关于C方向关于栈和队列的4个问题&#xff1a; C中stack 是容器么&#xff1f; 使用的stack是属于哪个版本的STL&#xff1f; 使用的STL中stack是如何实现的&#xff1f; stack 提供迭代器来遍历stack空间么&#xff1f; …...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频

使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

《基于Apache Flink的流处理》笔记

思维导图 1-3 章 4-7章 8-11 章 参考资料 源码&#xff1a; https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...