嵌入式系统中的加解密签名

笔者来了解一下嵌入式系统中的加解密

1、背景与名词解释

笔者最近在做安全升级相关的模块,碰到了一些相关的概念和一些应用场景,特来学习记录一下。

1.1 名词解释

  1. 对称加密:对称加密是一种加密方法,使用相同的密钥(称为密钥)进行加密和解密。发送方和接收方必须共享同一个密钥。对称加密算法的优点是速度快,适合大量数据的加密,但密钥分发和管理可能会面临安全挑战。
  2. 非对称加密:非对称加密使用成对的密钥:公钥和私钥。公钥用于加密数据,只有持有配对的私钥才能解密。私钥用于对数据进行签名,公钥用于验证签名。非对称加密算法通常较慢适合于相对较小的数据量,但在密钥分发身份验证方面具有很大的优势。
  3. 签名:数字签名是非对称加密的一种应用,用于验证数据的完整性和来源。发送方使用其私钥对数据进行签名,接收方使用发送方的公钥来验证签名。如果验证成功,则可以确信数据未被篡改过。有点像这个数据被签名了一样,无法抵赖,就是从某个人发出的。
  4. 证书:证书是用于验证公钥拥有者身份的数字文件。证书包含公钥及其拥有者的信息,并由权威认证机构(CA)签名,用于证明该公钥确实属于指定的实体。证书常用于HTTPS连接中,以及公钥基础设施(PKI)中确保通信安全。

1.1.1 对称加密算法

  1. AES(Advanced Encryption Standard)
    AES是一种对称加密算法,广泛应用于保护敏感数据。它支持不同的密钥长度(如AES-128、AES-192、AES-256),速度快且安全性高,被广泛认可为目前最安全的对称加密算法之一。
  2. DES(Data Encryption Standard)
    DES是一种早期的对称加密算法,已不再被推荐使用,因为其56位密钥长度对现代计算能力来说安全性较低。
  3. 3DES(Triple DES)
    3DES是对DES的改进,通过多次对数据块应用DES算法来增强安全性。虽然安全性较高,但由于效率低下和对现代计算能力来说密钥长度较短的缺点,现在也逐渐被AES所取代。

1.1.2 非对称加密算法

  1. RSA(Rivest-Shamir-Adleman)
    RSA是最广为人知的非对称加密算法之一,用于加密、数字签名和密钥交换。它基于大素数的难解性,用于生成公钥和私钥对。RSA在加密和签名方面都有应用。
  2. DSA(Digital Signature Algorithm)
    DSA是一种用于生成和验证数字签名的非对称加密算法。它通常与SHA-1或SHA-2等哈希算法结合使用,用于确保数据的完整性和来源。
  3. ECC(Elliptic Curve Cryptography)
    ECC是一种基于椭圆曲线数学的非对称加密算法。它提供了与RSA相当的安全性,但使用更短的密钥长度,因此在资源受限的环境下更为适用。
  4. DH(Diffie-Hellman)
    Diffie-Hellman是一种密钥交换协议,虽然本身不是加密算法,但用于安全地交换对称加密算法中使用的密钥。它的安全性基于离散对数问题的难解性。
    这些算法都在不同的应用场景中发挥着重要作用,选择合适的加密算法取决于安全需求、性能要求以及特定的应用环境。

1.2 为什么对称加密有风险

在这里插入图片描述

如上图所示,甲和乙通过对称加密算法进行通信,

  1. 首先甲向乙发送对称秘钥,约定使用该秘钥进行加密和通讯
  2. 然后发送加密的消息通信,
  3. 由于网上有窃听者会监听数据,所以一旦秘钥在网上传输,就有泄露的风险,泄露之后,数据就和明文在网上传输没什么区别,有风险。
  4. 有些人会问,我不在网络上传输秘钥不就行了吗? 那不在网上传输,乙如何知道甲使用什么方式加密,又合入通信呢?所以该方式一定有风险。

1.3 为什么非对称加密安全性更高

基于以上的想法,一个秘钥肯定不行,那就有两个秘钥,一个永远保存在本地,另外一个可以再网上传输,一个加密,另外一个解密,不就可以保证秘钥不泄露吗?

在这里插入图片描述
如上图所示,私钥永远在本地,公钥在网上传输,公钥加密的数据,私钥才可以解密,所以即使网上知道了公钥,也无用,因为没有私钥,没法解密查看到数据。

在这里插入图片描述

1.4 为什么需要签名

因为上面公钥是公开的,所以任何人都可以用公钥加密给甲或者乙发数据,就导致甲或者乙无法验证对方的身份是否正确,假如窃听者模仿甲或者乙向对方发错误的指令,那可能也有严重的后果。

在这里插入图片描述

所以首先是验证身份,然后才是通信数据,这时候就有了签名的说法,类似你在通信数据上面做了签名,无法抵赖。
签名的验证是通过私钥加密,公钥验证签名的过程来实现,只要公钥可以验证过了,那说明对方一定是甲或者乙发出的,不会是第三方窃听者发出来。
在这里插入图片描述

1.5 为什么还需要证书

假如窃听者 在身份验证时,就截获了数据,然后甲和乙拿到的不是对端的公钥,而且窃听者的公钥,然后在窃听者那边去转换数据。
在这里插入图片描述
如上图所示,这样即使有了身份验证也无效,关键原因就是拿到的公钥不对,如果公钥有一定的公信力,那么就可以认可对方的身份,因此就有了证书的这个概念。

公钥被放到有公信力的证书里面,保证拿到的一定是对的公钥。证书一般有证书颁发机构来发放,即CA(certificate Authority)。证书包括了公钥,同时证书也被签名了,保证证书没有被篡改。

通常被内置在操作系统或浏览器中,这些受信任的根证书(Root Certificate)构成了信任链的基础,用于验证由该CA签发的所有证书的有效性。

2、嵌入式系统中的安全升级

2.1 嵌入式系统升级

嵌入式系统中的升级讲解相对较多,可以参考笔者之前的文章,BootLoader的理解与实现,Bootloader学习理解----跳转优化异常。Bootloader学习理解学习–加强版。

2.2 嵌入式系统安全升级

像上面嵌入式设备升级,如果设备相同,完全可以模拟同样的嵌入式设备下发升级文件,必须进行签名认证信息,才可以。

在这里插入图片描述

2.3 Python代码中的RSA加密与签名验证

在python里面,我们用到了Cryptography以及OpenSSL这个库。

  1. 生成一对秘钥
    生成私钥时,public_exponent=65537指的是公钥指数,通常是一个小的奇数,用于加密或者验证签名,
    Key的size为2048位,也就是256Byte
    最后转化为明文显示
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization

# 生成私钥
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
    backend=default_backend()
)

# 生成公钥
public_key = private_key.public_key()

# 将私钥和公钥序列化为PEM格式
private_pem = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.TraditionalOpenSSL,
    encryption_algorithm=serialization.NoEncryption()
)

public_pem = public_key.public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.SubjectPublicKeyInfo
)

# 打印私钥和公钥
print(private_pem.decode('utf-8'))
print(public_pem.decode('utf-8'))

在这里插入图片描述

  1. 私钥对文件进行签名
    签名的时候,指明的padding是v1.5版本的PKCS,一个应用相对广泛标准化的填充方案,适用于RSA签名和加密操作。还有其他版本的填充,比如1.2版本的,以及PSS算法填充等等。
    填充是保证数据块大小符合算法要求的一种方案,加密算法要求数据大小符合256Byte,比如长度是2048位的计算。
    在这里插入图片描述
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding

# 已经有了private_key和要签名的文件1.txt

# 读取文件内容
with open('1.txt', 'rb') as f:
    data = f.read()

# 使用私钥对文件进行签名
signature = private_key.sign(
    data,
    padding.PKCS1v15()
    hashes.SHA256()
)

# 将签名写入文件
with open('signature', 'wb') as f:
    f.write(signature)
  1. 公钥对文件进行验签
    验证签名时,一定要和加密时候的填充方案一致,选择PKCSv15,不然会验签失败。
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization


# 使用公钥验证签名
try:
    public_key.verify(
        signature,
        message,
        padding.PKCS1v15(),
        hashes.SHA256()
    )
    print("签名验证成功,消息未被篡改。")
except Exception as e:
    print(f"签名验证失败: {e}")

当然也可以用openssl库来 进行签名和验签工作。

# 生成2048位的RSA私钥
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048

# 从私钥中提取公钥
openssl rsa -pubout -in private_key.pem -out public_key.pem

# 使用私钥签名文件,生成签名文件
openssl dgst -sha256 -sign private_key.pem -out signature.bin example.txt

# 验证签名
openssl dgst -sha256 -verify public_key.pem -signature signature.bin example.txt

在这里插入图片描述

2.4 嵌入式系统中的加解密

笔者最近了解到一个mbedtls,适合于嵌入式的一个安全加密库,C语言编写,OpenSSL是互联网上面应用的,相对较大,不适合嵌入式。

仓库地址:https://github.com/Mbed-TLS/mbedtls。
mbedtls编译完成后,会生成三个库文件,之后就可以根据头文件,链接这三个库进行加解密。
在这里插入图片描述

例如下面的代码,可以编译生成来进行测试,生成签名和验签。

#include "mbedtls/pk.h"
#include "mbedtls/md.h"
#include "mbedtls/base64.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "string.h"
#include <stdlib.h>

#define SIGNATURE_MAX_SIZE  256

int rsa_pkcs1v15_sha256_sign(const unsigned char *msg, size_t msg_len,
                               const char *priavte_key_pem, char *sign_base64, int sign_len)
{
    mbedtls_pk_context pk;
    mbedtls_entropy_context entropy;
    mbedtls_ctr_drbg_context ctr_drbg;

    uint8_t sig_buff[SIGNATURE_MAX_SIZE];
    unsigned char hash[32] = {0};
    size_t sig_len = 0;
    int ret = 0;
    char *b64_out = NULL;
    int b64_len = 0;
    const char *pers = "mbedtls_pk_sign";       // Personalization data,
    // that is device-specific identifiers. Can be NULL.
    
    // 初始化随机数生成器
    mbedtls_entropy_init( &entropy );
    mbedtls_ctr_drbg_init( &ctr_drbg );
 
    //初始化上下文
    mbedtls_pk_init( &pk );

    mbedtls_ctr_drbg_seed( &ctr_drbg,
                           mbedtls_entropy_func,
                           &entropy,
                           (const unsigned char *) pers,
                           strlen( pers ) );

 //导入私钥
    ret = mbedtls_pk_parse_key(&pk, (const unsigned char *)priavte_key_pem,\
                               strlen(priavte_key_pem)+1,\
                               NULL, 0, NULL, 0);
    if(ret != 0)
    {
        ret = -1;
        goto exit;
    }

    // 计算 sha256 消息摘要
    ret = mbedtls_md(mbedtls_md_info_from_type( MBEDTLS_MD_SHA256 ),
                     (const unsigned char *)msg, msg_len, hash);
    if(ret != 0)
    {
        ret = -1;
        goto exit;
    }
 
    // 签名
    ret = mbedtls_pk_sign(&pk, MBEDTLS_MD_SHA256, hash, sizeof (hash), sig_buff, sizeof(sig_buff), &sig_len, mbedtls_ctr_drbg_random, &ctr_drbg);

    if(ret != 0)
    {
        ret = -1;
        goto exit;
    }

    b64_out = malloc(sig_len*2);
    if(b64_out == NULL)
    {
        ret = -1;
        goto exit;
    }
 
    // 对签名数据进行 base64 编码
    ret = mbedtls_base64_encode((unsigned char *)b64_out, sig_len*2,
                                (size_t *)&b64_len, (unsigned char *)sig_buff, (size_t)sig_len);

    if(ret != 0)
    {
        ret = -1;
        goto exit;
    }

    if(sign_len<b64_len)
    {
        ret = -1;
        goto exit;
    }

    strncpy(sign_base64, b64_out, sign_len);

exit:

    if(b64_out)
    {
        free(b64_out);
    }

    mbedtls_pk_free( &pk );
    mbedtls_ctr_drbg_free( &ctr_drbg );
    mbedtls_entropy_free( &entropy );

    return ret;

}
/**
 * @brief rsa_pkcs1v15_sha256_verify
 * 
 * @param [in] msg
 * @param [in] msg_len
 * @param [in] public_key_pem
 * @param [in] sign_base64
 * @return int 
 *  -- 0  verify pass
 *  -- -1 verify faild
 */
int rsa_pkcs1v15_sha256_verify(const unsigned char *msg, size_t msg_len,
                               const char *public_key_pem, const char *sign_base64)
{
    mbedtls_pk_context pk = {0};
    unsigned char hash[32] = {0};
    int ret = 0;
    size_t sign_len = 0;
    size_t b64out_len = 0;
    unsigned char *b64out_data = NULL;

    // 初始化上下文
    mbedtls_pk_init( &pk);

    // 导入公钥
    ret = mbedtls_pk_parse_public_key(&pk, (const unsigned char *)public_key_pem, strlen(public_key_pem)+1);
    if(ret != 0)
    {
        ret = -1;
        goto exit;
    }

    // 对需要验签的数据进行 sha256 计算,生成消息摘要数据
    ret = mbedtls_md(mbedtls_md_info_from_type( MBEDTLS_MD_SHA256 ),
                     (const unsigned char *)msg, msg_len, hash);
    if(ret != 0)
    {
        ret = -1;
        goto exit;
    }

    // 对原始签名数据进行 base64 解码
    sign_len = strlen(sign_base64);
    b64out_data = malloc(sign_len*2);
    memset(b64out_data, 0, sign_len*2);
    ret = mbedtls_base64_decode(b64out_data, sign_len*2, &b64out_len, (const unsigned char *)sign_base64, sign_len);
    if(ret != 0)
    {
        ret = -1;
        goto exit;
    }

    // 验证签名

    ret = mbedtls_pk_verify(&pk, MBEDTLS_MD_SHA256, hash, sizeof (hash), b64out_data, b64out_len);


exit:
    if(b64out_data)
    {
        free(b64out_data);
    }
    mbedtls_pk_free( &pk );

    return ret;

}

static void test_rsa_pkcs1_sign(void)
{

    int ret = 0;

    char *private_key = "-----BEGIN RSA PRIVATE KEY-----\n"
                        "MIICXQIBAAKBgQDTt8tp4xNp29CMxy6QS0NzpR6t8bAcv7ei3NkVM/Nzg3K5wWZR\n"
                        "aBTMovbzKCXdXYdC6GutVkG+CEetO3XHM4LhDqW0vwISTO65/XrvR3zqXD5ZjrJF\n"
                        "mtCAvkCwtMAPjqXZ/RJnd8yrXuoz5cRqVgKmq5TZlGIIiTPIklxGIGof8QIDAQAB\n"
                        "AoGAFf1BJoiD5+sBdFmsq6ZxhUWZU+ImEzpTUZpD/riEWNNGe2YLoTlg7acgZH1f\n"
                        "P2hbJ9cZdemfTuQvw52JHE0sktCUM6R0wq5rlbDj740+5yZYzs9FlUntm6UtoU9w\n"
                        "tpd62/iPxovFkguunJB2KBbtP8q0dYQntATEce1TZuS3trUCQQDl7VRYygSb3/HY\n"
                        "ij2ya1592WpgNWgmPvbpmUjGGBvjmnO8Ye1lEy6x69RmGjRrLvFfhWYwcF2HpmYQ\n"
                        "9wXKEwT1AkEA67nc/CdeT4j9jRE/QFXlhVrW8Gq8IfjXFGbGK5BqlTRbty3OpW+L\n"
                        "M9GPqiMC2XxN60peEiANlQ8aUnvbHZexjQJAcz4RGK+ov7fvL+maIuNN6SYf+zjJ\n"
                        "iuHkQBFkOGW9FMdFWxZ6Nj73GJZrTwGzZEWTFZ13KrAnMOZmIfquHCqMQQJBAL+u\n"
                        "x9ATg1FRqDyKBdEfCCDEmXuuj4VggCUK3aKXMNRbWyk9iohkh+F/Sz+icLLBreri\n"
                        "8lPy1JidS14/cRJDRBECQQCT4oNvmV5CYzqkqbgwtLPi/FIjc6Zi26DGxBzL01V+\n"
                        "yTO1ZlOOUOtY4dPBnU4COkdq6hWqum/Q6kiVj91qAUHN\n"
                        "-----END RSA PRIVATE KEY-----";
    char *msg = "A message for signing";

    char sign[1024] = {0};

    ret = rsa_pkcs1v15_sha256_sign((const unsigned char *)msg, strlen(msg), private_key, sign, sizeof (sign));


    printf("rsa_pkcs1v15_sha256_sign ret=%d\r\n", ret);

    if(ret == 0)
    {
        printf("sign:%s\r\n", sign);
    }

}

 int main(void)
{

    int ret = 0;
 // 公钥
    char *pub_key = "-----BEGIN PUBLIC KEY-----\n"
                    "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDTt8tp4xNp29CMxy6QS0NzpR6t\n"
                    "8bAcv7ei3NkVM/Nzg3K5wWZRaBTMovbzKCXdXYdC6GutVkG+CEetO3XHM4LhDqW0\n"
                    "vwISTO65/XrvR3zqXD5ZjrJFmtCAvkCwtMAPjqXZ/RJnd8yrXuoz5cRqVgKmq5TZ\n"
                    "lGIIiTPIklxGIGof8QIDAQAB\n"
                    "-----END PUBLIC KEY-----";
    // 原始消息
    char *msg = "A message for signing";
 
    // base64 编码之后的签名数据
    char * sign = "KYiZF/C18O3wgCZvDptfM8Vh/OPMrcAf6ne9eszSuxgGMK57cKCQuWc33JF8iQmKWrSo"
                  "+ezzkPJIfXGTj3z3Js9vv1DC2tX3oBh9CdZF+yc5MqZAT5LEEqmwNKWiT4iNwwnbXiJt"
                  "NSy8/T2PRRN0PBy/TZn3HKc1AMKMYMLUjf8=";

    ret = rsa_pkcs1v15_sha256_verify((const unsigned char *)msg, strlen(msg), pub_key, sign);


    printf("rsa_pkcs1v15_sha256_verify ret=%d\r\n", ret);
	

    test_rsa_pkcs1_sign();
	return 0;

}


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

力扣刷题 杨辉三角(使用c++ vector解法)

杨辉三角 题目描述示例1示例2提示:代码 题目描述 给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例1 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]] 示例2 …

4、SpringMVC 实战小项目【加法计算器、用户登录、留言板、图书管理系统】

SpringMVC 实战小项目 3.1 加法计算器3.1.1 准备⼯作前端 3.1.2 约定前后端交互接⼝需求分析接⼝定义请求参数:响应数据: 3.1.3 服务器代码 3.2 ⽤⼾登录3.2.1 准备⼯作3.2.2 约定前后端交互接⼝3.2.3 实现服务器端代码 3.3 留⾔板实现服务器端代码 3.4 图书管理系统准备后端 3…

【内存管理】页面分配机制

前言 Linux内核中是如何分配出页面的&#xff0c;如果我们站在CPU的角度去看这个问题&#xff0c;CPU能分配出来的页面是以物理页面为单位的。也就是我们计算机中常讲的分页机制。本文就看下Linux内核是如何管理&#xff0c;释放和分配这些物理页面的。 伙伴算法 伙伴系统的…

Visual Studio开发环境搭建

原文&#xff1a;https://blog.c12th.cn/archives/25.html Visual Studio开发环境搭建 测试&#xff1a;笔记本原装操作系统&#xff1a;Windows 10 家庭中文版 资源分享链接&#xff1a;提取码&#xff1a;qbt2 注意事项&#xff1a;注意查看本地硬盘是否够用&#xff0c;建议…

在阿里云使用Docker部署MySQL服务,并且通过IDEA进行连接

阿里云使用Docker部署MySQL服务&#xff0c;并且通过IDEA进行连接 这里演示如何使用阿里云来进行MySQL的部署&#xff0c;系统使用的是Linux系统 (Ubuntu)。 为什么使用Docker? 首先是因为它的可移植性可以在任何有Docker环境的系统上运行应用&#xff0c;避免了在不通操作系…

SpringBoot+ENC实现密钥加密及使用原理

&#x1f60a; 作者&#xff1a; 一恍过去 &#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390 &#x1f38a; 社区&#xff1a; Java技术栈交流 &#x1f389; 主题&#xff1a; SpringBootENC实现密钥加密及使用原理 ⏱️ 创作时间&#xff1a; 202…

AtCoder Beginner Contest 359 A~C(D~F更新中...)

A.Count Takahashi 题意 给出 N N N个字符串&#xff0c;每个字符串为以下两种字符串之一&#xff1a; "Takahashi" "Aoki" 请你统计"Takahashi"出现了多少次。 分析 输入并统计即可。 代码 #include <bits/stdc.h>using namespa…

web集群-nginx(nginx三种文件模块,nginx用户请求流程,域名访问网站,虚拟主机,搭建大型直播购物平台)

nginx文件模块 lineinfile未来修改配置文件使用&#xff0c;类似于sed -i ‘sg’ 和sed ‘cai’ 掌握file模块&#xff1a;创建文件&#xff0c;目录&#xff0c;创建软链接&#xff0c;修改权限和所有者&#xff0c;删除文件目录 服务管理systemd systemctl相当于linux sys…

[stm32]温湿度采集与OLED显示

一、I2C总线协议 I2C&#xff08;Inter-integrated circuit &#xff09;是一种允许从不同的芯片或电路与不同的主芯片通信的协议。它仅用于短距离通信&#xff0c;是一种用于两个或多个设备之间进行数据传输的串行总线技术&#xff0c;它可以让你在微处理器、传感器、存储器、…

UE5开发游戏Tutorial

文章目录 PlayerStart 初始化设置默认 LevelBP_Character 初始化BP_Character 添加动画BP_Character 攻击BP_Enemy 初始化 以及 AI 运动Camera Collision 相机碰撞BP_Character 生命以及伤害Wave Spawner 波生成UI 初始化以及 Damage Screen指定位置随机生成添加声音环境 Envir…

使用SpringCache实现Redis缓存

目录 一 什么是Spring Cache 二 Spring Cache 各注解作用 ①EnableCaching ②Cacheable ③CachePut ④CacheEvict 三实现步骤 ①导入spring cache依赖和Redis依赖 ②配置Redis连接信息 ③在启动类上加上开启spring cache的注解 ④ 在对应的方法上加上需要的注解 一 什么…

PINN解偏微分方程实例4

PINN解偏微分方程实例4 一、正问题1. Diffusion equation2. Burgers’ equation3. Allen–Cahn equation4. Wave equation 二、反问题1. Burgers’ equation3. 部分代码示例 本文使用 PINN解偏微分方程实例1中展示的代码求解了以四个具体的偏微分方程&#xff0c;包括Diffusio…

长亭谛听教程部署和详细教程

PPT 图片先挂着 挺概念的 谛听的能力 hw的时候可能会问你用过的安全产品能力能加分挺重要 溯源反制 反制很重要感觉很厉害 取证分析 诱捕牵制 其实就是蜜罐 有模板直接爬取某些网页模板进行伪装 部署要求 挺低的 对linux内核版本有要求 需要root 还有系统配置也要修改 …

论文阅读--Efficient Hybrid Zoom using Camera Fusion on Mobile Phones

这是谷歌影像团队 2023 年发表在 Siggraph Asia 上的一篇文章&#xff0c;主要介绍的是利用多摄融合的思路进行变焦。 单反相机因为卓越的硬件性能&#xff0c;可以非常方便的实现光学变焦。不过目前的智能手机&#xff0c;受制于物理空间的限制&#xff0c;还不能做到像单反一…

long long ago

一、long 众所周知&#xff0c;英文单词 long&#xff0c;表示长,长的。 但是&#xff0c;还有很多你不知道到的东西&#xff0c;根据英文单词首字母象形原则&#xff0c;我们可以做一下单词long结构分析&#xff1a; long l长 ong长 什么意思&#xff1f;就是说首字线 l…

Maven的依赖传递、依赖管理、依赖作用域

在Maven项目中通常会引入大量依赖&#xff0c;但依赖管理不当&#xff0c;会造成版本混乱冲突或者目标包臃肿。因此&#xff0c;我们以SpringBoot为例&#xff0c;从三方面探索依赖的使用规则。 1、 依赖传递 依赖是会传递的&#xff0c;依赖的依赖也会连带引入。例如在项目中…

AI大模型企业应用实战(14)-langchain的Embedding

1 安装依赖 ! pip install --upgrade langchain ! pip install --upgrade openai0.27.8 ! pip install -U langchain-openai ! pip show openai ! pip show langchain ! pip show langchain-openai 2 Embed_documents # 1. 导入所需的库 from langchain_openai import Open…

poi生成的excel,输入数字后变成1.11111111111111E+23

poi版本4.1.2 生成excel后&#xff0c;单元格输入数字&#xff0c;过长的话变成这样 解决&#xff1a;生成的时候设置单元格格式为文本格式 import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.FileOutputStream; imp…

解析PDF文件中的图片为文本

解析PDF文件中的图片为文本 1 介绍 解析PDF文件中的图片&#xff0c;由两种思路&#xff0c;一种是自己读取PDF文件中的图片&#xff0c;然后用OCR解析&#xff0c;例如&#xff1a;使用PyMuPDF读取pdf文件&#xff0c;再用PaddleOCR或者Tesseract-OCR识别文字。另一种使用第…

使用matlab的大坑,复数向量转置!!!!!变量区“转置变量“功能(共轭转置)、矩阵转置(默认也是共轭转置)、点转置

近期用verilog去做FFT相关的项目&#xff0c;需要用到matlab进行仿真然后和verilog出来的结果来做对比&#xff0c;然后计算误差。近期使用matlab犯了一个错误&#xff0c;极大的拖慢了项目进展&#xff0c;给我人都整emo了&#xff0c;因为怎么做仿真结果都不对&#xff0c;还…