SM2证书链验证失败?自建Python验证器绕过cryptography白名单

我最近在好几个国密改造项目里连续踩到同一个坑:证书链上只要出现一张SM2证书,Python的cryptography库就率先抛异常,代码里按官方文档写好的链验证逻辑根本走不到握手成功那一步。报错永远是那一句“Unknown signature algorithm OID”或者“Unknown curve OID”,第一眼还以为是证书文件损坏,后来才发现是cryptography对国密SM2证书OID扩展支持有缺陷。这篇文章是我把整个排障过程、根因分析和最终代码级补丁完整复盘了一遍,重点会说清楚SM2证书里的关键OID、为什么Python cryptography会拒绝它们,以及怎么在不替换依赖的前提下,自己写一个可靠的SM2证书链验证器。如果你正在做国密改造、SM2 TLS卸载或者PKI体系迁移,这篇应该能直接帮你省掉好几天查资料的时间。

1. 问题现场:一张SM2证书卡住整个证书链验证

1.1 崩溃现场还原

先还原一下我遇到的报错。项目里拿到一张DER格式的SM2叶子证书,我像往常一样调用cryptography加载:

from cryptography import x509 with open("sm2_leaf.cer", "rb") as f: der = f.read() cert = x509.load_der_x509_certificate(der)

结果第一行就炸了:

ValueError: Unknown signature algorithm OID: 1.2.156.10197.1.501

当时我的反应是“证书坏了”?但用系统里的国密OpenSSL命令行工具去看,这张证书完全正常,签名算法、公钥、有效期都没问题。接着我用cryptography去解析证书里的公钥:

pub_key = cert.public_key()

又报:

ValueError: Unknown curve OID: 1.2.156.10197.1.301

到这里问题已经很明显了:不是证书坏了,是cryptography根本不认识SM2相关的OID。更麻烦的是,后续整个证书链验证逻辑全建立在cryptography的x509对象之上,第一步都过不去,后面做verify_directly_issued_by、PathBuilder、TLS握手全部跟着失败。

1.2 国密证书为什么在cryptography手里“不听话”

先说结论:SM2证书本身就是一个标准X.509证书,ASN.1结构完全符合RFC 5280的框架,问题出在证书里用于标识算法和曲线的OID是国密标准定义的私有OID,而cryptography在算法识别维护了一张白名单,SM2的OID没有进入这张名单,所以它在解析阶段就直接拒绝。

这背后其实是两件事叠加:

第一,cryptography做X.509解析时会先看SignatureAlgorithm里面的算法OID,如果不在已知列表里,它宁可抛异常也不尝试“先解析结构、后面再处理”。这种设计对标准算法很安全,但对国密这种私有算法特别不友好。

第二,SM2公钥使用的曲线是国密标准定义的sm2p256v1,OID是1.2.156.10197.1.301。在cryptography的椭圆曲线库里,它对国际标准曲线(比如secp256r1、secp384r1)做了完整映射,但对这条国密曲线没有任何定义。就算签名算法OID你勉强绕过去,公钥解析这一关还是会挂。

还有一个很多人容易误解的点:cryptography并不是单纯包装系统OpenSSL的简单封装,它对X.509的解析逻辑有自己独立实现和算法白名单。所以即使你的机器上装了一个支持SM2的国密OpenSSL,cryptography也不见得会用上,更不用说自动支持SM2证书链验证。我当时也试过换环境变量指向国密OpenSSL,发现完全没有效果,原因就在这里。

2. 突破口:搞懂SM2证书的OID到底长什么样

2.1 SM2证书里有哪些关键OID

要修复问题,必须先把SM2证书里涉及的OID认全。下面这张表我建议你直接收藏,排查的时候会反复用到:

OID含义出现位置cryptography的状态
1.2.156.10197.1.301SM2曲线/公钥算法(sm2p256v1)SubjectPublicKeyInfo.algorithm / parameters未识别,抛Unknown curve OID
1.2.156.10197.1.501SM3WithSM2签名算法Certificate.signatureAlgorithm,TBSCertificate.signature未识别,抛Unknown signature algorithm OID
1.2.156.10197.1.401SM3哈希算法通常作为SM2验签时的摘要算法未识别
1.2.840.10045.2.1id-ecPublicKey少数SM2证书公钥算法会使用这个国际通用OID,但parameters里带SM2曲线OID能识别公钥类型,但后续曲线参数依然报错

注意第一行里有两个容易混淆的场景:一部分SM2证书的公钥算法OID直接写1.2.156.10197.1.301,另一部分证书为了兼容国际标准,公钥算法写成id-ecPublicKey(1.2.840.10045.2.1),然后在parameters里放SM2曲线OID。两种都合理,但cryptography遇到第一种直接不认,遇到第二种虽然认识id-ecPublicKey,却因为parameters里的SM2曲线OID不在白名单,照样失败。

2.2 是什么导致了“总失败”

证书链验证不是单点操作,它至少包含几个环节:每一级证书的DN比对、有效期检查、基本约束检查、密钥用法检查、签名验证。签名验证又依赖两个关键能力:能解析上一级证书的公钥,能用上一级公钥去验证下一级证书的签名。

cryptography的问题在于它在最前面的“解析”环节就倒下了。叶子证书的签名算法OID不被识别就抛异常,那你连叶子证书对象都构建不出来,后面的链关系检查自然全部中断。这就像过闸机时护照本身没问题,但系统里没录入你这个国籍代码,第一道闸就把你拦住了,后面的安检、登机流程根本轮不到执行。

而且这个失败是“传染”的:证书链里只要有一张SM2证书,整个链验证就失败,哪怕根证书和中间证书都是RSA/ECDSA证书,只要叶子是SM2,整体就过不去。因为验证是从叶子往根走的,第一步就断了。

2.3 一个被忽略的事实:ASN.1结构其实符合通用X.509

排障时我做过一个花了不少时间的实验:把SM2证书里所有关键OID手动替换成cryptography认识的标准OID,再重新编码DER,发现cryptography能正常解析出Subject、Issuer、有效期、扩展项,甚至能读出TBS证书字节和签名字节。

这个实验说明了一个关键事实:SM2证书的ASN.1骨架完全是标准X.509结构,只是算法OID这一小段内容cryptography不认识。也就是说,问题不是“SM2证书不能用Python解析”,而是“cryptography这个库的OID白名单太封闭”。搞清楚这点,修复思路就清晰了:我们完全可以绕过cryptography的算法识别逻辑,自己解析SM2证书的通用结构,自己接管签名验证。

3. 补丁级修复:自建SM2证书链验证器

3.1 方案总览与选型建议

先对比几个常见修复方案,免得你走弯路:

方案改动量风险我的评价
升级cryptography到最新版小中可以试试,但不要指望,SM2 OID支持在常见版本里一直没有默认进入白名单
对证书DER做二进制OID替换小高不推荐,DER是严格编码,OID长度不一致会破坏整个TLV结构,就算能解析,验签也会失真
用asn1crypto + gmssl自建验证器中低推荐,结构解析用asn1crypto,密码运算用gmssl,纯Python可控性强
整体切换到某厂商国密SDK大中看场景,如果是封闭环境可以考虑,但要评估SDK维护成本和兼容性

我最后选了asn1crypto + gmssl这条路线。asn1crypto是一个非常专业的ASN.1解析库,它不会因为算法OID未知就拒绝整个证书,而是把算法OID原样保存在对象里,我们可以自己判断。gmssl则提供了SM2验签、SM3哈希的底层实现,不需要自己写椭圆曲线运算。

3.2 准备工作:安装两个小而美的依赖

pip install asn1crypto gmssl

asn1crypto用来解析X.509证书的DER结构,它能拿到TBSCertificate原始字节、签名值、SubjectPublicKeyInfo里的公钥比特串。gmssl提供SM2和SM3的密码学原语,重点是CryptSM2.verify()方法,它内部会完成SM3哈斯和SM2验签的完整步骤。

这里提醒一句,PyPI的gmssl是纯Python实现,跟某些系统里命令行工具gmssl不是同一个东西。我安装的是PyPI版本,功能对齐、API稳定就好。

3.3 第一步:用asn1crypto原样解析SM2证书

核心代码并不复杂,先解析证书并取关键字段:

from asn1crypto import x509 SM3_WITH_SM2_OID = "1.2.156.10197.1.501" SM2_CURVE_OID = "1.2.156.10197.1.301" def load_sm2_cert(der_bytes: bytes): cert = x509.Certificate.load(der_bytes) sign_oid = cert['signature_algorithm']['algorithm'].dotted if sign_oid != SM3_WITH_SM2_OID: print(f"注意:签名算法不是SM3WithSM2,而是 {sign_oid}") return cert

这一步做了一件事:读取证书的签名算法OID,确认它是不是SM3WithSM2。asn1crypto不会像cryptography那样看见陌生OID就抛异常,它会很老实地把OID字符串原样暴露出来。这就是我们绕开算法白名单的第一层突破。

接着提取验证需要的关键数据:

cert = load_sm2_cert(der_bytes) # TBSCertificate 的 DER 字节,签名验证时要用 tbs_der = cert['tbs_certificate'].dump() # 签名值的原始字节 signature_bytes = cert['signature_value'].native # 公钥比特串,通常是 04 || X || Y public_key_bytes = cert['tbs_certificate']['subject_public_key_info']['public_key'].native

需要注意,signature_value.native得到的是去掉BIT STRING填充位后的原生字节,SM2签名通常是64字节,顺序为R(32字节)+ S(32字节)。public_key_bytes则是未压缩点格式,开头是0x04,接着两个32字节的大整数,分别是X和Y坐标。

3.4 第二步:手工恢复SM2公钥并完成验签

拿到公钥比特串后,我们需要把它交给gmssl的SM2对象:

from gmssl import sm2 def verify_sm2_signature(cert_der: bytes, issuer_der: bytes) -> bool: cert = x509.Certificate.load(cert_der) issuer = x509.Certificate.load(issuer_der) # 用签发者证书里的公钥 pub_point = issuer['tbs_certificate']['subject_public_key_info']['public_key'].native if pub_point[0] != 0x04: raise ValueError("只支持未压缩的SM2公钥格式") # gmssl的CryptSM2要求公钥为十六进制字符串 pub_key_hex = pub_point.hex() sm2_crypt = sm2.CryptSM2(public_key=pub_key_hex) # 被验签的数据必须是TBSCertificate的原始DER tbs_der = cert['tbs_certificate'].dump() # 签名值同样转为十六进制 signature_hex = cert['signature_value'].native.hex() return sm2_crypt.verify(signature_hex, tbs_der)

这个函数内部的流程对应了SM2证书签名验证的标准过程:取出签发者公钥,计算待验签数据(TBSCertificate DER),然后调用SM2原语完成SM3摘要和椭圆曲线验签。gmssl把这些密码运算封装好了,我们不需要关心Big Number运算。

这里有一个容易被忽略的点:cert['tbs_certificate'].dump()返回的是asn1crypto重新编码后的TBS字节,而不是证书文件里那段原始字节。理论上标准DER证书的重新编码结果和原始字节是一致的,但如果你拿到的证书存在非规范编码(比如DER长度用了非最短形式、BOOL字段有冗余),重新编码就会破坏签名结果。真遇到验签失败但确认公钥无误的情况,要从这一步查起。

要绝对稳妥,可以写一个从原始DER截取TBS字节的工具函数。原理是解析最外层SEQUENCE,定位到第一个子元素并保留原始字节:

def extract_tbs_der(der_bytes: bytes) -> bytes: # 最外层是 SEQUENCE,第一个子元素就是 tbsCertificate # 这里简单实现,假设DER结构完整、没有无限长编码 def read_tlv(data, offset): tag = data[offset] offset += 1 first = data[offset] offset += 1 if first & 0x80 == 0: length = first else: length_bytes = first & 0x7F length = int.from_bytes(data[offset:offset + length_bytes], "big") offset += length_bytes value = data[offset:offset + length] return tag, value, offset + length tag, _, end = read_tlv(der_bytes, 0) if tag != 0x30: raise ValueError("不是合法的DER SEQUENCE") # 跳过外层后,读取第一个子TLV _, tbs_value, _ = read_tlv(der_bytes, 2) return tbs_value

实际使用时,优先用这个extract_tbs_der返回的字节作为验签输入,只有它确定正常后,才考虑用dump()简化代码。

3.5 第三步:完整的SM2证书链验证器

单张证书验签只是第一步,证书链验证还需要逐级检查DN、有效期、扩展属性、签名关系。我把这个逻辑封装成一个类:

from datetime import datetime, timezone class SM2ChainValidator: def __init__(self, trust_roots: list[bytes]): self.trust_roots = [x509.Certificate.load(der) for der in trust_roots] self.root_ders = trust_roots def verify(self, chain_ders: list[bytes], now: datetime | None = None): if now is None: now = datetime.now(timezone.utc) if len(chain_ders) < 1: raise ValueError("证书链不能为空") chain = [x509.Certificate.load(der) for der in chain_ders] # 叶子证书必须排在最前面,逐级往上 for index in range(len(chain)): cert = chain[index] issuer = chain[index + 1] if index + 1 < len(chain) else None # 到达末端时找受信根 if issuer is None: issuer = self._match_trust_root(cert) if issuer is None: raise ValueError("找不到匹配的受信根证书") ok, msg = self._verify_issued_by(cert, issuer, now) if not ok: raise ValueError(f"第 {index + 1} 级证书验证失败: {msg}") return True def _match_trust_root(self, cert): issuer_der = cert['tbs_certificate']['issuer'].dump() for root in self.trust_roots: if issuer_der == root['tbs_certificate']['subject'].dump(): return root return None def _verify_issued_by(self, cert, issuer, now): # 1. DN 匹配 cert_issuer = cert['tbs_certificate']['issuer'].dump() issuer_subject = issuer['tbs_certificate']['subject'].dump() if cert_issuer != issuer_subject: return False, "证书签发者与上一级主体不匹配" # 2. 有效期 not_before = cert['tbs_certificate']['validity']['not_before'].native not_after = cert['tbs_certificate']['validity']['not_after'].native if not_before > now or not_after < now: return False, "证书不在有效期内" # 3. 签发者CA基本约束(叶子证书除外,这里不区分,生产环境可细化) ext_list = issuer['tbs_certificate']['extensions'] for ext in ext_list: if ext['extn_id'].dotted == "2.5.29.19": # basicConstraints value = ext['extn_value'].parsed if value['ca'].native is not True: return False, "签发者证书不允许作为CA" # 4. SM2 签名验证 signature_ok = verify_sm2_signature(cert.dump(), issuer.dump()) if not signature_ok: return False, "SM2签名验证不通过" return True, "ok"

代码里我用的是ext['extn_id'].dotted,这是OID的字符串形式,比如basicConstraints的OID是2.5.29.19。如果你要检查KeyUsage,可以用2.5.29.15,值里继续判断key_cert_sign等标志位。生产环境建议把基本约束、密钥用法、名称约束、CRL分发点都检查一遍,我这里为了代码可读性只放了最核心的链路关系。

这个类的使用方式:

with open("root.cer", "rb") as f: root_der = f.read() with open("mid.cer", "rb") as f: mid_der = f.read() with open("leaf.cer", "rb") as f: leaf_der = f.read() validator = SM2ChainValidator(trust_roots=[root_der]) validator.verify([leaf_der, mid_der])

4. 修复实操:三张证书跑通全链路

4.1 测试环境搭建

我先用国密OpenSSL工具链生成了一套完整的测试PKI体系:根CA、中间CA、叶子证书,叶子证书用于TLS服务端。工具链生成SM2证书的具体命令我不展开了,不同环境差别比较大,总之你能拿到三张DER或者PEM格式的SM2证书就行。

注意证书链数组的顺序,必须是叶子证书在最前面,之后是中间证书,最后才是根证书。这个顺序在TLS场景里也是标准要求,服务端下发证书链时同样如此。

4.2 修复前后的运行结果

修复前用cryptography验证:

from cryptography import x509 try: leaf = x509.load_der_x509_certificate(leaf_der) mid = x509.load_der_x509_certificate(mid_der) root = x509.load_der_x509_certificate(root_der) leaf.verify_directly_issued_by(mid) except Exception as e: print("cryptography验证失败:", type(e).__name__, e)

输出:

cryptography验证失败: ValueError Unknown signature algorithm OID: 1.2.156.10197.1.501

修复后用我们自写的SM2ChainValidator验证:

validator = SM2ChainValidator(trust_roots=[root_der]) ok = validator.verify([leaf_der, mid_der]) print("SM2证书链验证结果:", ok)

输出:

SM2证书链验证结果: True

这个结果说明,整套代码已经把SM2证书链的验证逻辑完整接管了,不再依赖cryptography的算法白名单。

4.3 踩坑实录:我在修复过程中遇到的两件怪事

第一件是TBS字节重编码问题。最初我用cert['tbs_certificate'].dump()做验签输入,遇到一张由某款老版本工具生成的SM2证书,签名总是验证失败。后来我把原始DER里截出来的TBS字节和dump()结果做了逐字节对比,发现有几处长度字段编码不同。解决办法就是前面提到的extract_tbs_der,用原始字节替代重新编码,签名立刻通过。建议所有验签逻辑都默认走原始字节截取这条路。

第二件是gmssl对公钥格式的“隐藏要求”。有一次从证书里取出的公钥比特串是32字节的X坐标,但我一开始没注意它是不是以0x04开头,直接传给CryptSM2,结果验签永远返回False。后来核实才发现,SM2公钥必须是未压缩点格式(04 || X || Y),如果证书里公钥用了压缩格式或者某种怪异编码,gmssl根本不接受。我在代码里强制检查pub_point[0] != 0x04并抛错,就是为了避免这种静默失败。

4.4 混合证书链怎么办

实际生产环境里有一种情况很常见:根证书和中间证书是RSA或者ECDSA,只有叶子证书是SM2。这种情况不要直接把整条链丢给SM2验证器,更合适的做法是分类处理:标准算法段继续用cryptography验证,SM2这一段用我们的自研验证器。

具体可以这样:先判断每一级证书的签名算法OID,如果是SM3WithSM2就走verify_sm2_signature,否则尝试用cryptography的verify_directly_issued_by。这样既不用推翻已有的标准证书验证逻辑,又解决了SM2证书被白名单拦截的问题。

5. 常见问题排查速查表

错误或问题可能原因排查/解决办法
ValueError: Unknown signature algorithm OID: 1.2.156.10197.1.501cryptography白名单不识别SM3WithSM2改用asn1crypto解析,用gmssl验签,不要试图patch cryptography内部
ValueError: Unknown curve OID: 1.2.156.10197.1.301cryptography不识别SM2曲线SM2曲线参数与P-256不同,不能简单替换为secp256r1,必须走gmssl
自研验证器报“SM2签名验证不通过”TBS字节被重新编码用extract_tbs_der从原始DER截取TBS
自研验证器报公钥格式错误公钥不是`04
Python ssl模块TLS握手失败Python标准库依赖的OpenSSL不支持国密套件这不是cryptography层面能解决的,需要国密版OpenSSL或者国密网关
证书链顺序报错传入的数组不是叶子在前、根在后按“叶子、中间、根”顺序传入验证器

5.1 几个容易被忽视的隐性坑

第一个坑是“证书链里混签”。如果中间证书是国密SM2,叶子证书却是RSA,表面看是标准算法,但中间证书的签名算法还是SM3WithSM2。你在验证叶子证书时用cryptography没问题,但验证中间证书时依然会被SM2 OID卡住。所以判定标准应该是每一级证书的签名算法OID,而不是整个链是否有SM2证书。

第二个坑是“证书扩展项审查”。证书链验证不只是验签,还要检查叶子证书是否允许用于TLS serverAuth、中间证书是否有CA基本约束、根证书是否在受信库。我的示例代码只实现了核心链路关系,生产环境一定要补齐扩展检查,尤其是KeyUsage的digitalSignature和keyCertSign标志。很多国密证书工具生成的证书扩展项不标准,这一步最容易忽略。

第三个坑是“时间处理”。asn1crypto解析的not_before和not_after会返回datetime对象,但它可能是naive datetime(不包含时区)。如果你用带时区的now去比较,Python会直接抛TypeError。我在代码里统一用了datetime.now(timezone.utc)并在比较前确保时间都带时区,如果你直接把native结果传给比较函数,建议先做时区归一化。

5.2 我个人的实操体会

这套自建验证器方案我在真实项目里跑了几个月,最深的体会是:不要试图在cryptography库层面上打补丁。它的算法白名单分布在Rust后端和Python绑定两层,monkey patch很难生效,就算你侥幸patch上了,库一升级又要重新来一遍。把SM2证书的解析和验签独立成一个模块,反而最稳。这个模块只依赖asn1crypto和gmssl,两个库都很稳定,后续无论是适配新的国密证书格式,还是对接不同PKI体系,改动面都集中在自己的代码里,出问题也好排查。

最后再分享一个小技巧:如果你只是排查问题,不想写完整验证器,可以先手工跑一遍“提取TBS + gmssl验签”这几行代码,确认是不是cryptography的OID白名单问题。只要原始DER能用asn1crypto解析、TBS字节和签名值能取出来、gmssl验签通过,那就完全可以确定cryptography是唯一瓶颈,接下来放心写补丁就行。

相关新闻

MATLAB手写共轭梯度法与梯度下降法:原理、代码与实测对比

MATLAB手写共轭梯度法与梯度下降法:原理、代码与实测对比

在做模拟项目X的时候&#xff0c;我需要用MATLAB程序分别实现共轭梯度法和梯度下降法&#xff0c;求解一个典型的无约束最优化问题。一开始我很自然地打开MATLAB就想用 fminunc 一把梭&#xff0c;但真正把两种算法放在同一套测试用例下对比之后才发现&#xff0c;内置优化器…

2026/10/20 17:48:40 阅读更多 →
Llama生态全解析:从开源模型到AI原生应用基础设施

Llama生态全解析:从开源模型到AI原生应用基础设施

这两年我一直在琢磨一个很现实的问题&#xff1a;当一个开源大模型被越来越多的人装进自己的应用之后&#xff0c;它到底靠什么持续活下去&#xff1f;Llama给出的答案很有意思——不是模型本身&#xff0c;而是围绕它长出来的那一整片生态。从早期只有几个参数档位的“模型下载…

2026/10/20 17:48:40 阅读更多 →
Python流程控制详解:分支与循环的实战指南

Python流程控制详解:分支与循环的实战指南

1. 从“顺序直行”到“控制分流”&#xff1a;理解流程控制的本质这章是Python入门路上一个真正的分水岭。前面几章学变量、学数据类型、学运算符&#xff0c;其实都是在“准备材料”——告诉Python内存里有什么、怎么算。但程序真正干活的逻辑&#xff0c;是从流程控制开始的。…

2026/10/20 17:47:35 阅读更多 →

最新新闻

CStatic控件实现GDI双缓冲实时曲线绘制

CStatic控件实现GDI双缓冲实时曲线绘制

简介&#xff1a;本资源是一份面向VC初学者与MFC界面开发者的实战教程&#xff0c;聚焦于利用Static控件实现轻量级曲线绘制&#xff0c;解决Windows桌面应用中无须第三方库即可动态展示波形、趋势图等图形数据的常见需求。压缩包共41个文件&#xff0c;涵盖10个头文件&#xf…

2026/10/20 18:31:25 阅读更多 →
PHP zval内存机制与__debugInfo漏洞原理剖析

PHP zval内存机制与__debugInfo漏洞原理剖析

1. 这道题不是考PHP语法&#xff0c;是考你有没有真正“看见”PHP的内存 刚看到“[蓝帽杯 2021]One Pointer PHP”这个标题时&#xff0c;我下意识点开题目页面&#xff0c;扫了一眼源码就准备关掉——又是一道“PHP弱类型反序列化”的老套路&#xff1f;结果在本地搭环境复现…

2026/10/20 18:31:25 阅读更多 →
手机号反查QQ号合规指南:三步通过官方流程确认绑定关系

手机号反查QQ号合规指南:三步通过官方流程确认绑定关系

手机号反查QQ号&#xff0c;听着像是个挺玄的功能&#xff0c;其实在官方体系里说穿了就是一条标准的“找回账号”路径。大多数人都不知道&#xff0c;是因为搜索时总被各种收费工具页面截胡&#xff0c;反而把官方入口给忽略了。这篇文章我把边界说清楚&#xff1a;什么情况下…

2026/10/20 18:31:25 阅读更多 →
AutoRemesher 自适应网格密度实战:如何按曲率控制四边形分布

AutoRemesher 自适应网格密度实战:如何按曲率控制四边形分布

AutoRemesher 自适应网格密度实战&#xff1a;如何按曲率控制四边形分布 【免费下载链接】autoremesher Automatic quad remeshing tool 项目地址: https://gitcode.com/GitHub_Trending/au/autoremesher 拿到一个扫描或雕刻出来的高模 obj&#xff0c;动辄几百万三角形…

2026/10/20 18:31:25 阅读更多 →
VOS3000操作手册实战:软交换计费配置与呼叫排障全指南

VOS3000操作手册实战:软交换计费配置与呼叫排障全指南

简介&#xff1a;VOS3000操作手册是一份面向运营商、呼叫中心运维人员及通信技术学习者的系统化文档&#xff0c;完整覆盖从入门到精通的软交换平台操作路径。手册围绕系统登录、费率与套餐管理、账户体系、呼叫跟踪、业务管理、语音管理、数据查询与报表、话单分析等核心模块展…

2026/10/20 18:31:25 阅读更多 →
Claude Code实战:宠物健康管理系统全栈开发复盘

Claude Code实战:宠物健康管理系统全栈开发复盘

1. 项目概述&#xff1a;这不是一个“AI写代码”的演示&#xff0c;而是一次真实产品级开发的完整复盘“Claude Code从零搭建新项目全流程AI实现-宠物生命周期管理App”——这个标题里藏着三个关键信息点&#xff1a;Claude Code是核心工具链&#xff0c;从零搭建强调过程完整性…

2026/10/20 18:30:19 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →