Java实现加密(七)国密SM2算法的签名和验签(附商用密码检测相关国家标准/国密标准下载)

news2025/4/26 13:16:47

目录

    • 一、国密标准中,关于SM2签名验签的定义
    • 二、SM2签名和验签的实现原理
      • 1. 前置知识
      • 2. 签名生成过程
      • 3. 验签过程
      • 4. 数学正确性证明
      • 5. 安全性与注意事项
    • 三、带userId、不带userId的区别
      • 1. 核心区别
      • 2.算法区别
        • (1) 哈希计算过程
        • (2) 签名验签流程
    • 四、Java代码实现
      • 1. Maven 依赖
      • 2. 代码实现
      • 3. 测试结果
      • 4. 签名结果解析R和S
      • 5. 在线验证
    • 五、签名的ASN.1结构解析
      • 1. ASN.1整体结构
      • 2. 字段解析
      • 3. 关键字段说明
      • 4. 为什么需要 `00` 前缀?
      • 5. 实际签名示例
      • 6. 代码验证(Java + Bouncy Castle)
    • 五、补充:商用密码检测相关标准下载
      • 1. GB/T 15843 国家标准(权限鉴别相关)
      • 2. GB/T 38540国家标准(电子 签章相关,参考)
      • 3. GM/T 0003 国密标准(SM2算法)
      • 4. GM/T 0031国密标准(电子签章相关,参考)
      • 5.其余国家标准下载
      • 6. 其余国密标准下载
      • 7. GB/T 38540 国家标准和 GM/T 0031 国密标准的应用场景差异:

一、国密标准中,关于SM2签名验签的定义

参考 《GM∕T 0003-2010 SM2椭圆曲线公钥密码算法.pdf》

其中第2部分对于数字签名的描述如下:

  • 数字签名算法 由一个 签名者 对数据产生数字签名,并由一个 验证者 验证签名的可靠性。每个签名都有一个公钥和私钥,其中私钥用于产生签名,验证者用签名者的公钥验证签名。

标准原始描述截图如下:

在这里插入图片描述

补充:《SM2椭圆曲线公钥密码算法》共分为四个部分:

  • 第1部分:总则
  • 第2部分:数字签名算法
  • 第3部分:密钥交换协议
  • 第4部分:公钥加密算法

注意:在 SM2 算法中涉及的公钥、私钥、签名的长度基本是固定的,以下为标准长度,可以比对参考长度是否争取:

标准公钥:
23BCB208E10056523D4F4090C0130D5B8898A858E8D5D9FF3B16572FA04E70E28A88459060FF5D88CC53D77407619F9B8B584317A30EDDFCA71DC4965F3ED143

标准私钥:
CBD981B9C2FC49D9E497A68EB4EA3AC2E33472CCECBA7EA803B1A1DDB3B0EBCE

标准签名数据:
R:D00C1DEFEAD263A0FEDDE0AEC26274DBB80719385BB3DDD9AB2A31FB11F378C3

R:00CE562BE2CEB0DDD0DD18E925FF00AB87BB67BB33F4234967F82EDC3798265CDF

S:F1BEDD87A2B17D7150E4ECDCFAEB0D3E34AFE5985CB4EFA39D4FDCE7B32CFBE4

在线网址:
SM2 密钥在线生成工具:https://const.net.cn/tool/sm2/genkey/
SM2 在线签名生成工具(带userId):https://const.net.cn/tool/sm2/sign/
SM2 在线验签工具(带userId):https://const.net.cn/tool/sm2/verify/


二、SM2签名和验签的实现原理

1. 前置知识

  • 椭圆曲线参数
    SM2 使用特定的椭圆曲线方程(如 y^2 = x^3 + ax + b )和公开参数:
    • 基点 G(生成元)。
    • n(基点的阶,一个大素数)。
  • 密钥对
    • 私钥 d:随机数,1 ≤ dn−1。
    • 公钥 P:椭圆曲线上的点,P = dG
  • 哈希函数
    SM3 算法(国密标准哈希函数),用于计算消息和用户ID的哈希值。

2. 签名生成过程

签名者对消息 M 生成签名 (r,s),步骤如下:

步骤 1:计算哈希值 ZAe

  • 用户ID哈希(ZA
    将用户ID(如身份证号、邮箱等)与公钥绑定,防止身份伪造:

    Z A = SM3 ( UserID ∥ 公钥坐标 ∥ 曲线参数 ) ZA = \text{SM3}(\text{UserID} \parallel \text{公钥坐标} \parallel \text{曲线参数}) ZA=SM3(UserID公钥坐标曲线参数)

  • 消息哈希(e
    结合 ZA​ 和原始消息 M

    e = SM3 ( Z A ∥ M ) e = \text{SM3}(ZA \parallel M) e=SM3(ZAM)

步骤 2:生成随机数 k

  • 随机选择 k∈[1,n−1],且每次签名必须不同(否则私钥会泄露)。

步骤 3:计算临时椭圆曲线点 (x1, y1)

( x 1 , y 1 ) = k ⋅ G (x₁, y₁) = k \cdot G (x1,y1)=kG

  • x1 的整数形式,计算 r

    r = ( e + x 1 ) m o d    n r = (e + x₁) \mod n r=(e+x1)modn

    r=0r+k=n,需重新选择 k

**步骤 4:计算签名值 s

s = ( 1 + d ) − 1 ⋅ ( k − r ⋅ d ) m o d    n s = (1 + d)^{-1} \cdot (k - r \cdot d) \mod n s=(1+d)1(krd)modn

  • (1+d)−1 是模 n 下的乘法逆元。
  • s=0,需重新签名。

最终签名

输出 (r,s) 作为数字签名(通常编码为 64 字节,rs 各 32 字节)。


3. 验签过程

验签者使用公钥 P 验证签名 (r, s) 的合法性:

步骤 1:检查 rs 范围

  • 确保 r, s∈[1, n−1],否则验签失败。

步骤 2:重新计算哈希值 e

  • 使用相同的 UserID 和公钥计算 ZAe(与签名过程一致)。

步骤 3:计算中间值 t

t = ( r + s ) m o d    n t = (r + s) \mod n t=(r+s)modn

  • t=0,验签失败。

步骤 4:恢复临时点 (x1, y1)

( x 1 , y 1 ) = s ⋅ G + t ⋅ P (x₁, y₁) = s \cdot G + t \cdot P (x1,y1)=sG+tP

  • 利用公钥 P=d⋅G,推导如下:

    s ⋅ G + t ⋅ P = s ⋅ G + t ⋅ d ⋅ P = ( s + t ⋅ d ) ⋅ G s \cdot G + t \cdot P = s \cdot G + t \cdot d \cdot P = (s + t \cdot d) \cdot G sG+tP=sG+tdP=(s+td)G

    • 签名时:

      s ≡ ( 1 + d ) − 1 ⋅ ( k − r d ) m o d    n s ≡ (1+d)^{−1} \cdot (k−rd) \mod n s(1+d)1(krd)modn

      ,代入可得:

      s + t ⋅ d ≡ k m o d    n s + t \cdot d ≡ k \mod n s+tdkmodn

    • 因此恢复的点应为 k ⋅ G,即签名时的 (x1, y1)

步骤 5:验证 r 的合法性

R = ( e + x 1 ) m o d    n R = (e + x₁) \mod n R=(e+x1)modn

  • 检查是否满足 R = r
    • 若成立,验签通过;否则失败。

4. 数学正确性证明

验签的关键在于通过公钥 P 和签名 (r, s) 重构出签名时的临时点 k⋅G

  1. 签名时:

    s ≡ ( 1 + d ) − 1 ( k − r d ) m o d    n s \equiv (1 + d)^{-1}(k - r d) \mod n s(1+d)1(krd)modn

  2. 两边乘 (1+d) 得:

    s ( 1 + d ) ≡ k − r d m o d    n s(1+d) \equiv k - rd \mod n s(1+d)krdmodn

  3. 整理后:

    k ≡ s + ( s + r ) d m o d    n k \equiv s + (s + r)d \mod n ks+(s+r)dmodn

    • 注意到 t = r + s,故 k ≡ s + td mod n
  4. 验签时计算的点:

    s ⋅ G + t ⋅ P = ( s + t d ) ⋅ G = k ⋅ G s \cdot G + t \cdot P = (s + td) \cdot G = k \cdot G sG+tP=(s+td)G=kG

    • 与签名时的 (x1, y1) 一致,确保 x₁ 匹配。

5. 安全性与注意事项

  1. 随机数 k 的安全性
    • k 必须不可预测且不重复,否则攻击者可通过两次签名反推私钥(类似 ECDSA 的漏洞)。
  2. UserID 的作用
    • 绑定用户身份与公钥,防止公钥替换攻击。
  3. 哈希函数
    • 必须使用 SM3 算法,确保与国密标准兼容。
  4. 抵抗攻击
    • 基于椭圆曲线离散对数问题(ECDLP)的困难性,无法从 P=d⋅G 推导出 d

三、带userId、不带userId的区别

SM2的签名验签分为带userId和不带userId两种,主要是根据签名验签时是否需要userId作为入参来进行区分。

  • 带 UserID
    多一步预处理(计算 ZA),将用户身份、公钥和曲线参数绑定到哈希中,形成身份感知的签名
  • 不带 UserID
    跳过预处理,仅哈希原始消息,签名仅依赖公钥和消息本身。

1. 核心区别

特性带 UserID 的 SM2不带 UserID 的 SM2
哈希输入计算 `ZA = SM3(UserID
身份绑定签名与特定用户身份(UserID)强绑定仅绑定公钥和消息,无用户身份信息
安全性防止公钥替换攻击(需伪造 UserID)仅依赖公钥,易受公钥替换攻击
国密标准合规性✅符合 GM/T 0003-2012 标准❌非标准用法,通常不推荐

2.算法区别

(1) 哈希计算过程
  • 带 UserID

    1. 先计算 ZA(用户身份哈希):

      Z A = SM3 ( UserID ∥ 公钥坐标 ∥ 曲线参数 ) ZA = \text{SM3}(\text{UserID} \parallel \text{公钥坐标} \parallel \text{曲线参数}) ZA=SM3(UserID公钥坐标曲线参数)

    2. 再计算消息哈希 e

      e = SM3 ( Z A ∥ M ) e = \text{SM3}(ZA \parallel M) e=SM3(ZAM)

    • 作用:将用户身份、公钥和消息绑定,确保签名无法被其他用户复用。
  • 不带 UserID
    直接计算消息哈希:

    e = SM3 ( Z A ∥ M ) e = \text{SM3}(ZA \parallel M) e=SM3(ZAM)

    • 风险:攻击者可替换公钥,伪造签名(缺乏身份绑定)。
(2) 签名验签流程
  • 带 UserID

    • 签名

      r = ( e + x 1 ) m o d    n       ( x 1 来自 k ⋅ G ) r = (e + x₁) \mod n \space\space\space\space\space (x₁来自 k\cdot G) r=(e+x1)modn     (x1来自kG)

      s = ( 1 + d ) − 1 ⋅ ( k − r ⋅ d ) m o d    n s = (1+d)^{-1} \cdot (k - r \cdot d) \mod n s=(1+d)1(krd)modn

    • 验签
      验签方需使用相同的 UserID 重新计算 ZAe,否则验签失败。

  • 不带 UserID
    跳过 ZA 计算,直接使用 e = SM3(M),其余步骤相同。


四、Java代码实现

1. Maven 依赖

<!-- BC库 -->
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.70</version>
</dependency>

2. 代码实现

SM2WithUserIdExample.java

import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithID;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.util.encoders.Hex;

import java.security.*;

public class SM2WithUserIdExample {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    // SM2曲线参数
    private static final ECNamedCurveParameterSpec SM2_SPEC = ECNamedCurveTable.getParameterSpec("sm2p256v1");

    public static void main(String[] args) throws Exception {
        // 生成SM2密钥对
        KeyPair keyPair = generateSM2KeyPair();
        System.out.println("Public Key: " + Hex.toHexString(keyPair.getPublic().getEncoded()));
        System.out.println("Private Key: " + Hex.toHexString(keyPair.getPrivate().getEncoded()));
        // 用户ID
        byte[] userId = "1234567812345678".getBytes();
        // 待签名的消息
        String message = "Hello, SM2!";

        // 使用用户ID进行签名
        byte[] signature = signWithUserId(keyPair.getPrivate(), userId, message.getBytes());
        System.out.println("Signature: " + Hex.toHexString(signature));

        // 使用用户ID进行验签
        boolean isValid = verifyWithUserId(keyPair.getPublic(), userId, message.getBytes(), signature);
        System.out.println("Signature valid: " + isValid);
    }

    // 生成SM2密钥对
    public static KeyPair generateSM2KeyPair() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "BC");
        keyPairGenerator.initialize(SM2_SPEC, new SecureRandom());
        return keyPairGenerator.generateKeyPair();
    }

    // 使用用户ID进行签名
    public static byte[] signWithUserId(PrivateKey privateKey, byte[] userId, byte[] message) throws Exception {
        ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(
                ((java.security.interfaces.ECPrivateKey) privateKey).getS(),
                new ECDomainParameters(SM2_SPEC.getCurve(), SM2_SPEC.getG(), SM2_SPEC.getN())
        );

        SM2Signer signer = new SM2Signer();
        signer.init(true, new ParametersWithID(privKey, userId));
        signer.update(message, 0, message.length);
        return signer.generateSignature();
    }

    // 使用用户ID进行验签
    public static boolean verifyWithUserId(PublicKey publicKey, byte[] userId, byte[] message, byte[] signature) throws Exception {
        java.security.spec.ECPoint publicPoint = ((java.security.interfaces.ECPublicKey) publicKey).getW();
        org.bouncycastle.math.ec.ECPoint bcPublicPoint = SM2_SPEC.getCurve().createPoint(
                publicPoint.getAffineX(),
                publicPoint.getAffineY()
        );

        ECPublicKeyParameters pubKey = new ECPublicKeyParameters(
                bcPublicPoint,
                new ECDomainParameters(SM2_SPEC.getCurve(), SM2_SPEC.getG(), SM2_SPEC.getN())
        );

        SM2Signer verifier = new SM2Signer();
        verifier.init(false, new ParametersWithID(pubKey, userId));
        verifier.update(message, 0, message.length);
        return verifier.verifySignature(signature);
    }
}

3. 测试结果

在这里插入图片描述

Public Key: 04d1a1065f36c116040a5aef12c2f9f34fd26a0af4e639f6602f9ad252fdaddcbe62bb4c7e065b6391822ec56e6822baded04bd98cf909a846e4a17b61cc9ae7de
Private Key: 308193020100301306072a8648ce3d020106082a811ccf5501822d047930770201010420c9e443b10c9f567cfa014f2982a307bf5473612540fc597a1bffa4be8f277b9ca00a06082a811ccf5501822da14403420004d1a1065f36c116040a5aef12c2f9f34fd26a0af4e639f6602f9ad252fdaddcbe62bb4c7e065b6391822ec56e6822baded04bd98cf909a846e4a17b61cc9ae7de
Message: 48656c6c6f2c20534d3221
Signature: 3046022100a3a8ed43fd20d85edfc72744eebf58a205b8c92e87dd7c286770f2e9f22aee68022100fcab418b2961cce41f514fe1851d85266cef66cf5178201778b570e8eebb8d9f
Signature valid: true

4. 签名结果解析R和S

签名内容实际是16进制ASN.1格式的字节流,我们可以使用工具网站进行在线解析。

  • 解析网站: https://the-x.cn/zh-cn/encodings/Asn1.aspx

解析结果如下:

在这里插入图片描述

  • R:A3A8ED43FD20D85EDFC72744EEBF58A205B8C92E87DD7C286770F2E9F22AEE68
  • S:FCAB418B2961CCE41F514FE1851D85266CEF66CF5178201778B570E8EEBB8D9F

5. 在线验证

  • 验证网址: https://const.net.cn/tool/sm2/verify/

我们将对应的公钥、原文、签名(R+S)按照十六进制格式输入之后就可以成功验证了。

注意:

1.公钥信息需要去除04前缀;
2.原文不要直接输入,需要转换为十六进制;
3.签名的R、S需要去除00前缀。

在这里插入图片描述

如果验证结果为空,页面会展示具体的报错原因,在如下图所示的位置:

(例如公钥没有去除 04 前缀时的报错)

在这里插入图片描述


五、签名的ASN.1结构解析

我们将上一步代码生成示例的原签名和解析后的 R、S 进行拆分比对:

在这里插入图片描述

可以发现签名的拼接规律如下:

完整签名=3046+022100+R+022100+S

看到这里,恭喜你!你已经发现了ASN.1结构的规律!

1. ASN.1整体结构

SM2 签名默认输出为 DER 编码的 ASN.1 格式,包含两个整数 rs。完整编码结构如下:

SEQUENCE (30) → 包含两个 INTEGER (02)
    │
    ├── INTEGER (02) → r
    └── INTEGER (02) → s

2. 字段解析

3046022100...022100... 为例:

字节位置值(Hex)含义
0-130SEQUENCE 标签,表示后续是一个结构体。
246SEQUENCE 长度,表示后续 70 字节(0x46 = 70)是序列内容。
3-5022100INTEGER 标签和长度,表示 r 是一个 32 字节(0x21 = 33,含前缀 00)的正整数。
6-37...r 的具体值(32 字节)。
38-40022100INTEGER 标签和长度,表示 s 是一个 32 字节的正整数。
41-72...s 的具体值(32 字节)。

3. 关键字段说明

1) 3046

  • 30:ASN.1 的 SEQUENCE 标签(表示复合结构)。
  • 46:序列的 总长度(70 字节),计算如下:
    • r 部分:02(标签) + 21(长度) + 00(前缀) + 32 字节 = 35 字节。
    • s 部分:同上,35 字节。
    • 总计:35 + 35 = 70 字节 → 0x46

2) 022100

  • 02:ASN.1 的 INTEGER 标签。
  • 21:整数的长度(33 字节,包含前缀 00)。
  • 00:前缀字节(因 r/s 的最高位为 1,需补 00 避免被当作负数)。

4. 为什么需要 00 前缀?

  • 规则:若整数的最高位(MSB)为 1,需补 00 避免被误认为是负数(ASN.1 的 INTEGER 是带符号的)。
  • 示例
    • r 的第一个字节是 0x8F(二进制 10001111),需补 00 变为 008F...
    • r 的第一个字节是 0x3F(二进制 00111111),无需补 00

5. 实际签名示例

假设签名值为:

3046022100A1B2C3...32字节)022100D4E5F6...32字节)
  • 解析
    • 30 46:SEQUENCE,长度 70 字节。
    • 02 21 00r 是 33 字节(含前缀 00),实际值 32 字节。
    • A1B2C3...r 的具体数据。
    • 02 21 00s 是 33 字节(含前缀 00),实际值 32 字节。
    • D4E5F6...s 的具体数据。

6. 代码验证(Java + Bouncy Castle)

import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.util.encoders.Hex;

public class SM2SignatureParser {
    public static void main(String[] args) {
        String derSignature = "3046022100A1B2C3...022100D4E5F6..."; // 示例签名
        byte[] signatureBytes = Hex.decode(derSignature);

        // 解析 DER 编码
        ASN1Sequence sequence = ASN1Sequence.getInstance(signatureBytes);
        BigInteger r = ((ASN1Integer) sequence.getObjectAt(0)).getValue();
        BigInteger s = ((ASN1Integer) sequence.getObjectAt(1)).getValue();

        System.out.println("r: " + r.toString(16));
        System.out.println("s: " + s.toString(16));
    }
}

五、补充:商用密码检测相关标准下载

1. GB/T 15843 国家标准(权限鉴别相关)

15843标准共分为6部分,第1部分为总则。

《GB∕T 15843.1-2017 信息技术 安全技术 实体鉴别 第1部分:总则.pdf》
《GB∕T 15843.2-2024 网络安全技术 实体鉴别 第2部分:采用鉴别式加密的机制.pdf》
《GB∕T 15843.3-2023 信息技术 安全技术 实体鉴别 第3部分:采用数字签名技术的机制.pdf》
《GB∕T 15843.4-2024 网络安全技术 实体鉴别 第4部分:采用密码校验函数的机制.pdf》
《GB∕T 15843.5-2005 信息技术 安全技术 实体鉴别 第5部分:使用零知识技术的机制.pdf》
《GB∕T 15843.6-2018 信息技术 安全技术 实体鉴别 第6部分:采用人工数据传递的机制.pdf》

下载地址: https://share.weiyun.com/bEAhW1Ec

如果只是为了检测时满足国家标准,最简单地可以参考第2部分的单次鉴别。

在这里插入图片描述

2. GB/T 38540国家标准(电子 签章相关,参考)

《GB∕T 38540-2020 信息安全技术 安全电子签章密码技术规范.pdf》

下载地址: https://share.weiyun.com/Mw2EwyZl

3. GM/T 0003 国密标准(SM2算法)

《GM∕T 0003-2010 SM2椭圆曲线公钥密码算法.pdf》

下载地址: https://share.weiyun.com/PvwgP2sM

4. GM/T 0031国密标准(电子签章相关,参考)

《GMT 0031-2014 安全电子签章密码技术规范.pdf》

下载地址: https://share.weiyun.com/VA5zlwVW

5.其余国家标准下载

全国网络安全标准化技术委员会:https://www.tc260.org.cn/front/bzcx/yfgbcx.html

(该地址可以下载国家标准文件,但是仅供参考,部分国家标准搜索不到。)

6. 其余国密标准下载

区别于国家标准有版权限制,国密标准可以直接在官方网站进行下载。

国家密码管理局-官网地址: https://www.oscca.gov.cn/sca/index.shtml

例如,我们下载SM3标准,可以先进行搜索,如下所示:

在这里插入图片描述

搜索之后可以看到下面有相关的文件,点击就可以直接下载了。

在这里插入图片描述

在这里插入图片描述

7. GB/T 38540 国家标准和 GM/T 0031 国密标准的应用场景差异:

场景GB/T 38540适用性GM/T 0031适用性
国际化业务(如跨境电子合同)✅ 优先采用❌ 不适用
政府/金融等关键领域可选✅ 强制符合
商用密码产品认证❌ 无法用于过密评✅ 必需符合

整理完毕,完结撒花~🌻





参考地址:

1.SM2 签名验签 注意事项,https://blog.csdn.net/softt/article/details/141570577

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

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

相关文章

【华为HCIP | 华为数通工程师】821—多选解析—第十七页

多选835、IS-IS协议所使用的NSAP地址主要由哪几个部分构成? A、AREA ID B、SEL C、DSCp D、SYSTEM ID 解析:NSAP地址:网络服务访问点(Network Service Access Point)是 OSI 协议中用于定位资源的地址。NSAP 的地址结构如图所示,它由 IDP(Initial Domain …

函数的定义与使用(python)

lst[:]是传入lst的拷贝。改变它对原始lst没有任何影响。 *list一个*的元素在函数体内会被当成一个元组。 以下是对图中 Python 代码的详细解释&#xff1a; 代码总体功能 这段代码定义了一个生成器函数 getItem &#xff0c;用于依次返回多个列表中的元素。然后通过循环遍历…

List findIntersection getUnion

List findIntersection & getUnion 求两个列表的交集和并集 package zwf;import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List;/*** 列表工具类* * author ZengWenFeng* date 2025.04.22* mobile 13805029595* email 117791303qq.com*/ p…

乒乓操作(Ping-Pong)

乒乓操作 “ 乒乓操作” 是一个常常应用于数据流控制的设计思想&#xff0c; 典型的乒乓操作方法如下图 所示&#xff1a; ​ T1周期&#xff0c;输入数据流1缓存到数据缓冲模块1中&#xff0c;如上图棕色&#xff1b;T2周期&#xff0c;输入数据流2缓存到数据缓冲模块2中&…

微信小程序文章管理系统开发实现

概述 在内容为王的互联网时代&#xff0c;高效的文章管理系统成为各类平台的刚需。幽络源平台今日分享一款基于SSM框架开发的微信小程序文章管理系统完整解决方案&#xff0c;该系统实现了多角色内容管理、智能分类、互动交流等功能。 主要内容 一、用户端功能模块 ​​多角…

GrassRouter 小草MULE多5G多链路聚合通信路由设备在应急场景的聚合效率测试报告及解决方案

在应急通信场景中&#xff0c;快速、稳定、高效的通信链路是保障救援工作顺利开展的关键。MULE&#xff08;Multi-Link Unified Link Enhancement&#xff09;多链路聚合路由通信设备作为一种新型的通信技术解决方案&#xff0c;通过聚合多条通信链路&#xff08;如4G/5G、卫星…

【MySQL】MySQL索引与事务

目录 前言 1. 索引 &#xff08;index&#xff09; 1.1 概念 1.2 作用 1.3 使用场景 1.4 索引的相关操作 查看索引 创建索引 删除索引 2. 索引背后的数据结构 2.1 B树 2.2 B&#xff0b;树的特点 2.3 B&#xff0b;树的优势 3. 事务 3.1 为什么使用事务 3.2 事…

华为网路设备学习-19 IGP路由专题-路由策略

一、 二、 注意&#xff1a; 当该节点匹配模式为permit下时&#xff0c;参考if else 当该节点匹配模式为deny下时&#xff1a; 1、该节点中的apply子语句不会执行。 2、如果满足所有判断&#xff08;if-match&#xff09;条件时&#xff0c;拒绝该节点并跳出&#xff08;即不…

基于DrissionPage的表情包爬虫实现与解析(含源码)

目录 ​编辑 一、环境配置与技术选型 1.1 环境要求 1.2 DrissionPage优势 二、爬虫实现代码 三、代码解析 3.1 类结构设计 3.2 目录创建方法 3.3 图片链接获取 3.4 图片下载方法 四、技术升级对比 4.1 代码复杂度对比 4.2 性能测试数据 五、扩展优化建议 5.1 并…

区间和数量统计 之 前缀和+哈希表

文章目录 1512.好数对的数目2845.统计趣味子数组的数目1371.每个元音包含偶数次的最长子字符串 区间和的数量统计是一类十分典型的问题&#xff1a;记录左边&#xff0c;枚举右边策略前置题目&#xff1a;统计nums[j]nums[i]的对数进阶版本&#xff1a;统计子数组和%modulo k的…

全能 Sui 技术栈,构建 Web3 的未来

本文翻译自&#xff1a;FourPillarsFP&#xff0c;文章仅代表作者观点。 2025 年&#xff0c;SuiNetwork正在以一套全栈区块链策略强势出击&#xff0c;彻底打破加密行业的传统范式。正如 Mysten Labs 联合创始人 Adeniyi Abiodun 所说&#xff1a;“Sui 不只是一条区块链&…

linux安装单节点Elasticsearch(es),安装可视化工具kibana

真的&#xff0c;我安装个es和kibana&#xff0c;找了好多帖子&#xff0c;问了好几遍ai才安装成功&#xff0c;在这里记录一下&#xff0c;我相信&#xff0c;跟着我的步骤走&#xff0c;99%会成功&#xff1b; 为了让大家直观的看到安装过程&#xff0c;我把我服务器的es和ki…

RK3xxx 部分无法连接虚拟机 无法进行adb连接

我发现部分rk板子可以连接到虚拟机上&#xff0c;部分连接不上。其中尝试了一块是安卓系统的rk板子是可以连接虚拟机。但是用了linux系统的rk板子连接不上虚拟机。尝试了很多办法还是无法连接虚拟机。 然后也看到一些相关资料&#xff0c;但是太少了&#xff0c;只有这个链接提…

26考研——存储系统(3)

408答疑 文章目录 一、存储器概述二、主存储器三、主存储器与 CPU 的连接四、外部存储器五、高速缓冲存储器六、虚拟存储器七、参考资料鲍鱼科技课件26王道考研书 八、总结复习提示思考题常见问题和易混淆知识点 一、存储器概述 文章链接: 点击跳转 二、主存储器 文章链接: …

C# 实战_RichTextBox选中某一行条目高亮,离开恢复

C# 中控件richtextbox中某一行的条目内容高亮&#xff0c;未选中保持不变。当鼠标点击某一行的条目高亮&#xff0c;离开该条目就恢复默认颜色。 运行效果&#xff1a; 核心代码实现功能&#xff1a; //高亮指定行的方法private void HighlightLine(RichTextBox rtb,int lineI…

Servlet小结

视频链接&#xff1a;黑马servlet视频全套视频教程&#xff0c;快速入门servlet原理servlet实战 什么是Servlet&#xff1f; 菜鸟教程&#xff1a;Java Servlet servlet&#xff1a; server applet Servlet是一个运行在Web服务器&#xff08;如Tomcat、Jetty&#xff09;或应用…

2025上海车展:光峰科技全球首发“灵境”智能车载光学系统

当AI为光赋予思想&#xff0c;汽车将会变成什么样&#xff1f;深圳光峰科技为您揭晓答案。 2025年4月23日&#xff0c;在刚刚开幕的“2025上海车展”上&#xff0c;全球领先的激光核心器件公司光峰科技举办了主题为“AI光影盛宴&#xff0c;智享未来出行”的媒体发布会&#x…

BiliNote:开源的AI视频笔记生成工具,让知识提取与分享更高效——跨平台自动生成结构化笔记,实现从视频到Markdown的智能转化

引言:视频学习的痛点与BiliNote的解决方案 随着知识视频化趋势的加速,B站、YouTube等平台成为学习与信息获取的重要渠道,但手动记录笔记耗时低效、信息碎片化等问题依然突出。BiliNote的出现,通过AI驱动的自动化流程,将视频内容转化为结构清晰的Markdown笔记,支持截图插…

图纸安全防护管理:构建企业核心竞争力的关键屏障

在当今高度竞争的商业环境中&#xff0c;图纸作为企业核心技术的重要载体&#xff0c;其安全防护管理已成为企业知识产权保护体系中的关键环节。无论是建筑行业的施工蓝图、制造业的产品设计图&#xff0c;还是高科技企业的研发图纸&#xff0c;都承载着企业的核心竞争力和商业…

借助内核逻辑锁pagecache到内存

一、背景 内存管理是一个永恒的主题&#xff0c;尤其在内存紧张触发内存回收的时候。系统在通过磁盘获取磁盘上的文件的内容时&#xff0c;若不开启O_DIRECT方式进行读写&#xff0c;磁盘上的任何东西都会被缓存到系统里&#xff0c;我们称之为page cache。可以想象&#xff0…