如何把肯德基订餐电话写到证书的签名值和公钥数据里

admin 2025年6月26日00:21:55评论0 views字数 6369阅读21分13秒阅读模式

上次一文精通数字证书的签名算法讨论了证书签名算法,公钥类型和证书链等。这时脑后有反骨的朋友可能会说,“既然知道结构,又知道字段的生成算法,可不可以把我的名字写到签名值上呢?或者嵌入我自己发明的公钥算法?”。本文对这些问题逐一进行解答

目录

  1. 修改证书的公钥
    1. 示例操作:生成x25519类型的数字证书
  2. 修改证书的签名

    1. 示例操作:把证书的签名值清零

一、修改证书的公钥

在上一篇文章中提到公钥可以有多种算法类型,例如常用的RSA,DSA和椭圆曲线,也说明了父证书公钥类型会决定子证书的“签名算法”。在openssl生成证书时,根据密钥类型产生逻辑分叉,解析对应算法的数学参数。例如如果是椭圆曲线类型的,则会进一步提取出曲线参数是p256还是512的。如果公钥数据字节乱填,openssl会在这个步骤因为解析不出来抛异常。
因此公钥字节数据无法自由修改,必须符合某种密钥算法的数学基础性质。比如一些openssl不支持的非对称密钥都可以做一番尝试。下面尝试生成x25519算法的公钥证书。
首先正常情况下x25519类型的密钥是不能生成证书的。原因在前一篇数字证书的文章中有说明。例如使用openssl命令时会拒绝生成x25519类型证书,同时验证x25519证书也会报unimplement异常。但可以使用自主编程实现。下面是生成的样子。
如何把肯德基订餐电话写到证书的签名值和公钥数据里
from cryptography.hazmat.primitives import serialization, hashesfrom cryptography.hazmat.primitives.asymmetric import ec, x25519from cryptography.x509 import ( Certificate, NameOID, Name, SubjectAlternativeName, BasicConstraints, KeyUsage, ExtendedKeyUsage, CertificateSigningRequestBuilder,random_serial_number,load_pem_x509_certificate,CertificateBuilder,ObjectIdentifier,NameAttribute)from cryptography.x509.oid import ExtensionOIDfrom datetime import datetime, timedeltafrom cryptography.hazmat.backends import default_backend# 生成临时 X25519 密钥对temp_server_x_private_key = x25519.X25519PrivateKey.generate()temp_server_x_public_key = temp_server_x_private_key.public_key()temp_server_x_public_bytes = temp_server_x_public_key.public_bytes_raw()# ========= 签发证书 =========# 加载中间 CA 私钥和证书with open("intermediate_2_ec_private.key""rb"as f:   ca_private_key = serialization.load_pem_private_key(       f.read(), password=None   )with open("intermediate_2_ec_cert.pem""rb"as f: ca_cert =load_pem_x509_certificate(f.read())# 构建 X.509 证书模板cert_builder = (   CertificateBuilder()   .subject_name(Name([       NameAttribute(NameOID.COMMON_NAME, u"My X25519 Server")  # 设置主题   ]))   .issuer_name(ca_cert.subject)  # 颁发者为 CA 的主题   .public_key(temp_server_x_public_key) # 自定义数据 TODO。现在是Public Key Algorithm: id-ecPublicKey   .serial_number(random_serial_number())   .not_valid_before(datetime.utcnow())   .not_valid_after(datetime.utcnow() + timedelta(days=90))   .add_extension(       BasicConstraints(ca=False, path_length=None),       critical=True   )   .add_extension(       KeyUsage(           digital_signature=False,           key_encipherment=False,           key_agreement=True,  # X25519 密钥协商           content_commitment=False,           data_encipherment=False,           key_cert_sign=False,           crl_sign=False,           encipher_only=False,           decipher_only=False       ),       critical=True   )   .add_extension(       ExtendedKeyUsage([ObjectIdentifier("1.3.101.110")]),       critical=False   ))# 使用中间 CA 签发证书certificate = cert_builder.sign(   private_key=ca_private_key,    algorithm=hashes.SHA256(), )certificate = cert_builder.sign(   private_key=ca_private_key,   algorithm=hashes.SHA3_512(),  # 可以使用 SHA224,SHA384,SHA3_224,SHA3_256,SHA3_384,SHA3_512   backend=default_backend())# 序列化证书cert_pem = certificate.public_bytes(serialization.Encoding.PEM)with open("x25519_SHA3_512_tmp_certificate.pem""wb"as pem_file:   pem_file.write(cert_pem)

该证书使用了x25519作为公钥,使用SHA3-512作为签名的hash算法,低版本的openssl可能不支持验签,需要提高openssl版本或编程验签,否则会报下面的算法不支持异常。

error 7 at 0 depth lookup: certificate signature failureerror transaction_4_SHA3_512_tmp_certificate.pem: verification failed139976144574272:error:0D0C50C7:asn1 encoding routines:ASN1_item_verify:unknown signature algorithm:crypto/asn1/a_verify.c:114

工程中可能的用处

首先这个证书的特点如下。

  1. x25519是专门为密钥交换设计的,即数据加密。

  2. x25519证书无法签发子证书,如果强制签发会因算法没有签名功能而报错。这也就意味着x25519证书一定位于证书链的终结点。

x25519证书比较适合一次性临时证书的分发。例如通信双方通信前先进行秘钥协商,客户端发送x25519临时公钥到服务端,客户端使用证书链对x25519公钥进行完整性保护,根CA证书在服务端预置。这个设计同时完成了x25519协商公钥传递、公钥完整性保护、客户端身份证明3步操作。另外由于x25519证书是程序即时生成的,还可以加入其他自定义信息例如一次性challenge token一起组成签名数据,提供防重放功能。更多应用场景欢迎讨论和自由发挥。

二、修改证书的签名

修改签名无法通过常规证书签发手段完成,只能通过C++调用openssl底层库,试过python和golang的证书库都无法调用低级openssl函数。这里演示一下如何将RSA类型证书的签名值全部清空。

下面是一个自签名证书。注意签名值的字节长度是没法修改的,受证书的密钥类型限制。
如何把肯德基订餐电话写到证书的签名值和公钥数据里
完整代码(兼容 OpenSSL 3.x / 1.1.x)
#include<openssl/x509.h>#include<openssl/pem.h>#include<openssl/rsa.h>#include<openssl/evp.h>#include<openssl/bio.h>#include<openssl/err.h>#include<openssl/x509v3.h>#include<iostream>#include<cstring>// for memsetvoidhandle_openssl_error(){   ERR_print_errors_fp(stderr);   abort();}intmain(){   //初始化 OpenSSL   OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);   ERR_load_crypto_strings();   //创建 RSA 密钥对   EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);   if (!ctx) handle_openssl_error();   if (EVP_PKEY_keygen_init(ctx) <= 0handle_openssl_error();   if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048) <= 0handle_openssl_error();   EVP_PKEY* pkey = NULL;   if (EVP_PKEY_keygen(ctx, &pkey) <= 0handle_openssl_error();   EVP_PKEY_CTX_free(ctx);   //创建 X509 证书   X509* x509 = X509_new();   if (!x509) handle_openssl_error();   //设置版本号 (v3)   X509_set_version(x509, 2);   //设置序列号   ASN1_INTEGER_set(X509_get_serialNumber(x509), 123456789);   //设置有效期:从现在开始 365 天   X509_gmtime_adj(X509_get_notBefore(x509), 0);   X509_gmtime_adj(X509_get_notAfter(x509), 31536000L); // 365 days   //设置公钥   if (!X509_set_pubkey(x509, pkey)) handle_openssl_error();   //设置主题和颁发者(相同)   X509_NAME* name = X509_get_subject_name(x509);   X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char*)"Test Cert"-1-10);   X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char*)"MyOrg"-1-10);   X509_set_issuer_name(x509, name);   //使用 SHA-256 算法进行签名   const EVP_MD* digest = EVP_sha256();   //正常签名一次以生成签名字段   if (!X509_sign(x509, pkey, digest)) handle_openssl_error();   //获取签名长度   const ASN1_BIT_STRING* signature;   const X509_ALGOR* sig_alg;   X509_get0_signature(&signature, &sig_alg, x509);   int sig_len = signature->length;   unsigned char* zero_sig = new unsigned char[sig_len];   std::memset(zero_sig, 0, sig_len);   //替换签名字段为全 0   ASN1_BIT_STRING* mutable_sig = const_cast<ASN1_BIT_STRING*>(signature);   ASN1_BIT_STRING_set(mutable_sig, zero_sig, sig_len);   delete[] zero_sig;   //输出 PEM 格式的证书   BIO* bio = BIO_new(BIO_s_mem());   PEM_write_bio_X509(bio, x509);   BUF_MEM* mem;   BIO_get_mem_ptr(bio, &mem);   BIO_set_close(bio, BIO_NOCLOSE);   BIO_free(bio);   std::cout << "Generated certificate with zeroed signature:" << std::endl;   std::cout.write((char*)mem->data, mem->length);   std::cout << std::endl;   //清理资源   BUF_MEM_free(mem);   X509_free(x509);   EVP_PKEY_free(pkey);   return 0;}
编译
sudo apt install libssl-devg++ -o generate_ecc_cert generate_ecc_cert.cpp -lssl -lcrypto
工程中可能的用处
签名值改写的自由发挥空间很大,默认正常情况是对证书的 to be sign 字节数据做签名。若将自定义数据写入到签名验签的流程中,就可实现证书的额外数据扩展,当然这也需要验签方也要用同样的验签过程。更多应用场景读者可以自由发挥。
你还有什么证书的创意使用方法呢,欢迎在评论区讨论
往期回顾
#

微信逆向解密聊天记录[附详细步骤]

#

逆向解密某云安全产品漏洞扫描模块离线规则库

#

一次100%免杀Mimikatz的探索

#

开始你的第一个0Day漏洞挖掘

原文始发于微信公众号(青木生长):如何把肯德基订餐电话写到证书的签名值和公钥数据里

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年6月26日00:21:55
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   如何把肯德基订餐电话写到证书的签名值和公钥数据里https://cn-sec.com/archives/4169272.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息