程序员必须知道的加密、解密和签名算法

news2024/10/7 17:29:54

1. 对称加密

对称加密,加密和解密使用相同的秘钥,加密速度快、效率高。常见的有 DES(淘汰)、3DES(淘汰)、AES(用于替代 DES,是目前常用的)等。

 加密解密

1.1. DES(Data Encryption Standard)

DES 现在认为是一种不安全的加密算法,已经有用穷举法攻破 DES 密码的报道了。3DES 是 DES 的加强版本(也被淘汰),是 DES 向 AES 过渡的加密算法。

1.2. AES(Advanced Encryption Standard)

AES 把明文按每组16个字节分成一组一组的、长度相等的数据,每次加密一组,直到加密完整个明文。在 AES 标准中,分组长度只能是128位,但是密钥的长度可以使用128位、192位或256位。

下面先来了解“分组加密机制、填充模式、初始向量、加密模式”等基本概念,最后给出 Java 代码示例。

1.2.1. 分组密码体制

分组密码体制就是指将明文切成一段一段的来加密,而且每段数据的长度要求必须是128位16个字节,如果最后一段不够16个字节了,就需要用 Padding 来把这段数据填满16个字节,然后再把一段一段的密文拼起来形成最终密文的加密方式。

1.2.2. 填充模式 Padding

Padding 就是用来把不满16个字节的分组数据填满16个字节用的,它有三种模式 PKCS5、PKCS7 和 NOPADDING。

  • PKCS5 是指分组数据缺少几个字节,就在数据的末尾填充几个字节的几,比如缺少5个字节,就在末尾填充5个字节的5。
  • PKCS7 是指分组数据缺少几个字节,就在数据的末尾填充几个字节的0,比如缺少7个字节,就在末尾填充7个字节的0。
  • NoPadding 是指不需要填充,也就是说数据的发送方肯定会保证最后一段数据也正好是16个字节。

在 PKCS5 模式下,有这样一种特殊情况,假设最后一段数据的内容刚好就是16个16,这时解密端怎么区分是填充还是数据呢?

对于这种情况,PKCS5 模式会自动帮我们在最后一段数据后再添加16个字节的数据,而且填充数据也是16个16,这样解密端就能知道谁是有效数据谁是填充数据了。同样的道理,PKCS7 最后一段数据的内容是16个0。

解密端需要使用和加密端同样的 Padding 模式,才能准确的识别有效数据和填充数据。开发通常采用 PKCS7 Padding 模式。

1.2.3. 初始向量 IV

初始向量 IV 的作用是使加密更加安全可靠。使用 AES 加密时要主动提供初始向量,而且只需提供一个初始向量就够了,后面每段数据的加密向量都是前面一段的密文。初始向量 IV 的长度规定为128位16个字节,初始向量通常采用随机生成。

1.2.4. 密钥

AES 要求密钥的长度可以是128位、192位或者256位,位数越高,加密强度越大,但是加密的效率自然会低一些,因此要做好衡量。

1.2.5. 分组加密模式

分组加密算法只能对固定长度的分组进行加密,面对超过分组长度的明文,就需要对分组密码算法进行迭代,以便将很长的明文全部加密。而迭代的方法就称为“分组加密的模式”。分组密码的模式有很多,常见的有:

  • ECB(电子密码本模式 Electronic Codebook Book),相对的不安全,很少使用
  • CBC(密码分组链接模式 Cipher Block Chaining),不支持并行计算,比 ECB 模式多了一个初始向量 IV,是这些模式中最安全的,也是最常用的模式
  • CFB(密码反馈模式 Cipher FeedBack),可被施以”重放攻击“
  • OFB(输出反馈模式 Output FeedBack),可被主动攻击者反转密文而引起解密后明文中的相应比特也发生变化
  • CTR(计数器模式 Counter mode),与 OFB 一样可被主动攻击者反转密文,但比 OFB多了支持并发计算的特性

AES 和 RSA 都属于分组加密算法。

1.2.6. AES Java 示例

AES-128-CBC 加解密

public static String cbcEncrypt(String plain, String key, String ivSeed) {
    Assert.notNull(plain, "plain must not be null");
    Assert.notNull(key, "key must not be null");
    Assert.notNull(ivSeed, "ivSeed must not be null");
    Assert.isTrue(ivSeed.length() == 16, "ivSeed must be 16 bytes");
    String base64 = null;
        
    try {
        // 生成秘钥
        SecretKeySpec keySpec = createKey(key);
        // 设置算法/模式/填充方式
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        // 设置偏移
        IvParameterSpec iv = new IvParameterSpec(ivSeed.getBytes(StandardCharsets.UTF_8));
        // 加密模式
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
        // 加密
        byte[] encrypted = cipher.doFinal(plain.getBytes(StandardCharsets.UTF_8));
        // 转 base64
        base64 = Base64.getEncoder().encodeToString(encrypted);
    } catch (Exception ex) {
        log.error("exception: {}", ex.getMessage());
    }

    return base64;
}

public static String cbcDecrypt(String base64, String key, String ivSeed) {
    Assert.notNull(base64, "base64 must not be null");
    Assert.notNull(key, "key must not be null");
    Assert.notNull(ivSeed, "ivSeed must not be null");
    Assert.isTrue(ivSeed.length() == 16, "ivSeed must be 16 bytes");

    String plain = null;
    try {
        // base64 解码
        byte[] decodedBase64 = Base64.getDecoder().decode(base64);
        // 生成秘钥
        SecretKeySpec keySpec = createKey(key, isBase64Key);
        if(null != keySpec) {
            // 设置偏移
            IvParameterSpec iv = new IvParameterSpec(ivSeed.getBytes(StandardCharsets.UTF_8));
            // 设置算法/模式/填充方式
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            // 解密模式
            cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
            // 解密
            byte[] decrypted = cipher.doFinal(data);
            // 转 base64
            plain = new String(decrypted, StandardCharsets.UTF_8);
        }
    } catch (Exception ex) {
        log.error("exception: {}", ex.getMessage());
    }

    return plain;
}

private static SecretKeySpec createKey(String key) {
    Assert.notNull(key, "key must not be null");

    byte[] bytesKey = key.getBytes(StandardCharsets.UTF_8);
    Assert.isTrue(bytesKey.length == 16, "key must be 16 bytes");

    // 生成秘钥
    return new SecretKeySpec(bytesKey, "AES");
}

2. 非对称加密

非对称加密算法,需要两个密钥, 一个是公钥 (public key),公开,任何人都可以获取;另一个是 私钥 (private key),不公开,由个人保存在安全的地方。公钥用于加密,私钥用于解密。

 

RSA (三位数学家名字的缩写)算法是第一个能同时用于 加密 和 数字签名 的非对称加密算法,它能够 抵抗 到目前为止已知的 所有密码攻击,已被 ISO 推荐为公钥数据加密标准。

2.1. 使用场景

假设 A 和 B 之间要进行加密通信,那么:

(1)B向A发送加密数据

  • A 生成一对密钥,私钥由 A 自己保留不公开;而公钥传给 B,公开,任何人可以获取
  • B 用该公钥对消息进行加密,并发送给 A
  • A 接收到加密消息后,用私钥对消息进行解密

在这个过程中,只有2次传递过程,第一次是 A 传递公钥给 B,第二次是 B 传递加密消息给A,即使都被截获,也没有危险性,因为只有 A 的私钥才能对消息进行解密,防止了消息内容的泄露。

(2)A向B发送“已收到”回复

  • A 用私钥对消息加签形成签名,并将加签的消息和消息本身一起传递给 B
  • B 收到消息后,用公钥进行验签,如果验签出来的内容与消息本身一致,证明消息是 A 回复的。

在这个过程中,算上前面的传递公钥,也只有2次传递过程,一次是传递公钥,第二次就是 A 传递加签的消息和消息本身给 B,即使都被敌方截获,也同样没有危险性,因为只有 A 的私钥才能对消息进行签名,即使知道了消息内容,也无法伪造带签名的回复给 B,防止了消息内容的篡改。

但是,综合上面两个场景会发现:

  • 第一个场景虽然被截获的消息没有泄露,但是可以利用截获的公钥,将假指令进行加密,然后传递给 A。
  • 第二个场景虽然截获的消息不能被篡改,但是消息的内容可以利用公钥验签来获得,并不能防止泄露。

所以在实际应用中,要根据情况使用,可以双方同时使用加密和签名,比如 A 和 B 都有一套自己的公钥和私钥,当 A 要给 B 发送消息时,先用 B 的公钥对消息加密,再对加密的消息使用 A 的私钥加签名,达到既不泄露也不被篡改,更能保证消息的安全性。

2.2. RAS 加密算法

2.2.1. 填充模式 Padding

Padding 常见模式如下表:

 Padding 模式

RSA 常用的加密填充模式

  • RSA/None/PKCS1Padding(Java 默认的 RSA 实现)
  • RSA/ECB/PKCS1Padding

2.2.2. RSA Java 示例

/**
 * 公钥加密
 */
public static String ecbEncrypt(String data, String publicKeyBase64) throws NoSuchPaddingException, NoSuchAlgorithmException,
InvalidKeySpecException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
    Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    
    cipher.init(Cipher.ENCRYPT_MODE, toPublicKey(publicKeyBase64));
    byte[] bytes cipher.doFinal(data.getBytes());
    
    return Base64.getEncoder().encodeToString(bytes);
}

/**
 * 私钥解密
 */
public static String ecbDecrypt(String base64, String privateKeyBase64) throws IllegalBlockSizeException, InvalidKeyException,
InvalidKeySpecException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
    byte[] bytes = Base64.getDecoder().decode(base64.getBytes());

    Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    cipher.init(Cipher.DECRYPT_MODE, toPrivateKey(privateKeyBase64));

    return new String(cipher.doFinal(data));
}

/**
 * 生成随机密钥对
 */
public static HashMap<String, String> randomKeyPair() throws NoSuchAlgorithmException {
    KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
    generator.initialize(2048);
    KeyPair pair = generator.generateKeyPair();
    if (null == pair) {
        return null;
    }

    PrivateKey pvt = pair.getPrivate();
    PublicKey pub = pair.getPublic();

    Base64.Encoder encoder = Base64.getEncoder();
    String pvtVal = encoder.encodeToString(pvt.getEncoded());
    String pubVal = encoder.encodeToString(pub.getEncoded());

    HashMap<String, String> rsaKeyMap = new HashMap<>(2);
    rsaKeyMap.put("privateKeyBase64", pvtVal);
    rsaKeyMap.put("publicKeyBase64", pubVal);

    return rsaKeyMap;
}

/**
 * privateKeyBase64 私钥转为 PrivateKey 对象
 */
private static PrivateKey toPrivateKey(String privateKeyBase64) throws NoSuchAlgorithmException, InvalidKeySpecException {
    byte[] bytes = Base64.getDecoder().decode(privateKeyBase64.getBytes());

    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
    KeyFactory kf = KeyFactory.getInstance("RSA");

    return kf.generatePrivate(keySpec);
}

/**
 * publicKeyBase64 公钥转为 PublicKey 对象
 */
private static PublicKey toPublicKey(String publicKeyBase64) throws NoSuchAlgorithmException, InvalidKeySpecException {
    byte[] bytes = Base64.getDecoder().decode(publicKeyBase64.getBytes());

    X509EncodedKeySpec ks = new X509EncodedKeySpec(bytes);
    KeyFactory kf = KeyFactory.getInstance("RSA");

    return kf.generatePublic(ks);
}

3. 签名

加密是为了防止信息被泄露,而签名是为了防止信息被篡改和伪造。

 

签名 & 验签

3.1. 摘要算法

哈希函数(Hash function),又称散列函数、散列算法,也叫摘要算法,它是一种不可逆的信息摘要算法。

好的散列算法具备如下特性:

  • 单向性(one-way)即不可逆
  • 抗冲突性(collision-resistant)即产生两个相同散列值的概率很低(但输入相同,则输出的结果一定相同)
  • 雪崩效应(avalanche effect)即原始数据的微小改动,会导致散列值的巨大差异

常见的用途:

  • 密码保护:把用户密码通过散列函数加密保存(保存散列值),只有用户自己知道密码的明文
  • 签名 & 验签:比如对接口调用、对消息进行签名,接收方进行验签
  • 数据完整性/一致性校验:比如网上提供的文件下载通常都提供散列值和算法,便于用户校验
  • 数据秒传:上传几个G的大文件只用几秒,就是通过对比文件的散列值实现的,散列值(信息的指纹)相同就认为是同一个文件

常见的散列算法有”报文摘要算法 MD“、”安全散列算法 SHA“,以及”消息认证码算法 MAC“。

 

摘要算法

3.1.1. 报文摘要算法(MD系列)

信息摘要算法(Message-Digest Algorithm)。最常用的是 MD5 (Message-Digest Algorithm 5),是一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),常用于确保信息传输完整一致。

3.1.2. 安全散列算法(SHA系列)

安全散列算法(Secure Hash Algorithm)是一种不可逆的信息安全算法,经过量化运算和转换,可以把任意长度的数据生成不可逆的、固定长度的字符串,这个固定长度的字符串就是对相应的原始输入字符串的散列(也称为摘要),可以作为信息的指纹。

SHA-224,SHA-256,SHA-384,SHA-512 统称为 SHA-2,而 SHA-1 算法已经不够安全,不建议继续使用。

3.1.3. 消息认证码(MAC系列)

消息认证码算法(Message Authentication Code)是含有加密密钥的散列算法,它在 MD 和 SHA 算法特性的基础上加入了加密密钥,通过特别的计算方式来构造消息认证码(MAC)的方法。 因 MAC 算法融合了密钥散列函数,通常也称为 HMAC 算法(Hash-based Message Authentication Code,散列消息认证码)。

常见的有:HMAC-SHA224、HMAC-SHA256、HMAC-SHA384、HMAC-SHA512

3.2. 签名验签原理

3.2.1. 签名

对需要发送的报文 originData 计算摘要(相关摘要算法有 md5、sha256等)特征值 signBlock。 使用私钥 privateKey 对 signBlock 加密获得数字签名 signatureData。 将 signatureData 与 originData 打包发一起送给对方。

3.2.2. 验签

接收方接收到数据后,把消息拆分为 signatureData 与 originData 。 对 originData 计算特征值 signBlock,使用的算法必须要和发送方一致。 使用公钥 publicKey 对 signatureData 解密,获得 signBlock1。 比较 signBlock 和 signBlock1,若匹配则验证成功,报文未被篡改。

3.3. RSA 签名示例

/**
* 用私钥对数据进行签名并返回签名后的base64
* @param data 代签名的字符串
* @param base64PrivateKey 私钥
*/
public static String sign(String data, String base64PrivateKey) 
throws InvalidKeySpecException, InvalidKeyException, NoSuchAlgorithmException, SignatureException {
    PrivateKey key = toPrivateKey(base64PrivateKey);
    Signature signature = Signature.getInstance("SHA256withRSA");
    signature.initSign(key);
    signature.update(data.getBytes());
    return new String(Base64.getEncoder().encode(signature.sign()));
}

/**
* 验签
* @param data 原始数据
* @param base64PublicKey 公钥
* @param sign 私钥签名后的数据
*/
public static boolean verify(String data, String base64PublicKey, String sign) 
throws InvalidKeySpecException, InvalidKeyException, NoSuchAlgorithmException, SignatureException {
    PublicKey key = toPublicKey(base64PublicKey);
    Signature signature = Signature.getInstance("SHA256withRSA");
    signature.initVerify(key);
    signature.update(data.getBytes());
    return signature.verify(Base64.getDecoder().decode(sign.getBytes()));
}

/**
* base64PrivateKey 私钥转为 PrivateKey 对象
*/
private static PrivateKey toPrivateKey(String base64PrivateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
    byte[] bytes = Base64.getDecoder().decode(base64PrivateKey.getBytes());

    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
    KeyFactory kf = KeyFactory.getInstance("RSA");

    return kf.generatePrivate(keySpec);
}
/**
* base64PublicKey 公钥转为 PublicKey 对象
*/
private static PublicKey toPublicKey(String base64PublicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
    byte[] bytes = Base64.getDecoder().decode(base64PublicKey.getBytes());
    X509EncodedKeySpec ks = new X509EncodedKeySpec(bytes);
    KeyFactory kf = KeyFactory.getInstance("RSA");
    return kf.generatePublic(ks);
}

小结

本文主要介绍了常用的对称加密算法、非对称加密算法;常用的摘要算法、签名算法。以及使用算法需要了解的基本概念(比如填充模式、IV等),算法的使用场景,并且分别给出了 Java 示例代码。

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

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

相关文章

建造者模式:详解构造函数、set方法、建造者模式三种对象创建方式

思考&#xff1a; 为什么需要建造者模式 与工厂模式有何区别&#xff1f; 为什么需要建造者模式&#xff1f; 平时常常使用new关键字来创建对象&#xff0c;什么时候new对象时候不适用了呢&#xff1f;可能是创建对象时候可能是构造函数中传入太多的内容吧。 下面通过一个例子…

基于Java电脑硬件库存管理系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

Linux下从CPU/内存/IO三个方面来分析系统性能

在实际生产环境中是否遇到如下问题&#xff1f; 系统平均负载过高。 CPU使用率过高。 硬盘利用率已经饱和&#xff0c;IO存在瓶颈。 首先明确一下进程的常见6种状态 R运行状态&#xff08;running&#xff09;&#xff1a;并不意味着进程一定在运行中&#xff0c;它表明进程要…

钉钉聊天对话框和截图经常发生白屏

环境&#xff1a; 7.0.30-rel6019102 Win10专业版 L盾加密环境 问题描述&#xff1a; 钉钉聊天对话框和截图经常发生白屏 解决方案&#xff1a; 1.【电脑端钉钉】- 左上角【头像】-【设置】-【高级】- 下拉【网络检测】- 点击【开始检测】 如果变红说明网络有问题&#x…

redis安装后启动报redis-server.exe redis.windows.conf

文章目录 1. 报错的内容2. 解决方法&#xff1a;&#xff08;亲测有效&#xff09; 1. 报错的内容 redis安装后启动报redis-server.exe redis.windows.conf 完整报错如下&#xff1a; 2. 解决方法&#xff1a;&#xff08;亲测有效&#xff09; 先使用命令切换到redis安装目…

Redis常用命令操作

#linux是redis-cli #普通环境 redis-cli.exe -h host -p port -a password #集群环境&#xff0c;否则报&#xff1a;(error) MOVED 6918 127.0.0.1:6381 redis-cli.exe -c -h host -p port -a password#参数说明 #host&#xff1a;远程redis服务器host #port&#xff1a;远程r…

开发人员必备:9个令人惊叹的CSS网格生成器推荐!

微信搜索 【大迁世界】, 我会第一时间和你分享前端行业趋势&#xff0c;学习途径等等。 本文 GitHub https://github.com/qq449245884/xiaozhi 已收录&#xff0c;有一线大厂面试完整考点、资料以及我的系列文章。 快来免费体验ChatGpt plus版本的&#xff0c;我们出的钱 体验地…

【分布式应用】zabbix 二:自定义监控、自动发现与自动注册

目录 一、添加zabbix客户端主机1.1环境设置1.2配置zabbix-angent1.3在 Web 页面中添加 agent 主机 二 、自定义监控内容2.1客户端自定义key2.2在Web页面创建自定义监控模板 三、zabbix自动发现四、zabbix自动注册 一、添加zabbix客户端主机 1.1环境设置 systemctl disable --…

2490. 回环句

句子 是由单个空格分隔的一组单词&#xff0c;且不含前导或尾随空格。 例如&#xff0c;"Hello World"、"HELLO"、"hello world hello world" 都是符合要求的句子。 单词 仅 由大写和小写英文字母组成。且大写和小写字母会视作不同字符。 如果…

查询例题(三道)

一、 写法一&#xff1a; 写法二&#xff1a; 二、 1、内连接&#xff1a; 一个部门下有哪些人&#xff0c;找的相关联的数据 2、左外连接&#xff1a; 以部门表为基准&#xff0c;部门下面没有人&#xff0c;但是也会查询出来 3、右外连接&#xff1a; 以员工表为基准&#…

【Spring 丨数据绑定】

数据绑定 概述Databinder核心属性绑定参数绑定元数据绑定验证 概述 Spring 数据绑定(Data Binding)的作用是将用户的输入动态绑定到应用程序的领域模型JavaBean(或用于处理用户输入的任何对象)。 也就是说&#xff0c;Spring数据绑定机制是将属性值设置到目标对象中。如下图所示…

Bug小能手系列(python)_9: 使用sklearn库报错 module ‘numpy‘ has no attribute ‘int‘

AttributeError: module numpy has no attribute int. 0. 错误介绍1. 环境介绍2. 问题分析3. 解决方法3.1 调用解决3.2 库包中存在报错 4. 总结 首先&#xff0c;对于自己使用代码dtypenp.int报错的情况&#xff0c;建议直接修改为np.int_即可解决&#xff0c;也不用向下看了&a…

Python3安装教程在Unix/Linux操作系统

在Linux操作系统上安装Python3教程&#xff0c;先下载Python3安装包&#xff1a; Python3下载&#xff1a;https://www.python.org/downloads/source/ 选择适用于 Unix/Linux 的源码压缩包。下载及解压压缩包 Python-3.x.x.tgz&#xff0c;3.x.x 为你下载的对应版本号。如果你…

triton客户端使用

model_analyzer 简介&#xff1a; Triton Model Analyzer is a CLI tool which can help you find a more optimal configuration, on a given piece of hardware, for single, multiple, ensemble, or BLS models running on a Triton Inference Server. Model Analyzer wil…

SSM框架训练 实现各个功能时遇到的常见问题

快速复制当前代码到下一行&#xff1a;ctrlD 格式化代码&#xff08;快速整理代码&#xff09;&#xff1a;ctrilaltL 一步一步来&#xff0c;后续会不停添加功能。 先创建项目结构&#xff1a;搭建框架 (36条消息) SSM框架模板&#xff08;高配&#xff1a;一次性配完所有…

指针进阶1

目录 本章将学习 1字符指针 2数组指针与指针数组 3数组传参与指针传参 复习指针初阶基本知识点 1指针是个地址&#xff08;编号&#xff09;&#xff0c;指针变量是存放指针的变量&#xff0c;但是我们平常所说的指针就是指的指针变量&#xff0c;指针变量的大小有4(32位平…

解决Quixel Bridge导出到Blender3.1失败port 28888

文章目录 前言一、错误情景二、解决办法总结 前言 解决Quixel Bridge导出到Blender3.1报错无法经由端口28888导出. 一、错误情景 导出插件显示已安装完成: 但是点击右下角导出报错无法从端口28888执行该操作. 我尝试过把MSPlugin插件手动安装到Blender3.1但这并不奏效. 二、解…

聚观早报|Threads上线7小时注册破千万;兰博基将终结燃油车生产

今日要闻&#xff1a;Threads上线7小时注册破千万&#xff1b;兰博基尼宣布将终结燃油车生产&#xff1b;腾讯旗下企鹅FM9月6日正式停止运营&#xff1b;ChatGPT暂停接入必应搜索功能&#xff1b;首个国产GLP-1“减肥药”获批 Threads上线7小时注册破千万 7 月 6 日消息&#…

Windows 基本概念和术语

Windows 基本概念和术语 Windows APIWindows API 的风格Windows 运行时.NET Framework 服务、函数和例程进程使用任务管理器查看进程信息父进程 线程纤程用户模式调度线程 作业虚拟内存内核模式和用户模式虚拟机监控程序固件终端服务和多会话对象和句柄安全性注册表Unicode总结…

google软件测试之道

目录 前言&#xff1a; 一、Google软件测试介绍 1&#xff09;质量不等于测试 2&#xff09;角色、职责 3&#xff09;组织结构 4&#xff09;测试版本 5&#xff09;测试类型&#xff1a; 二、软件测试开发工程师&#xff0c;SET 1&#xff09;SET的工作 2&#xff…