这篇长文并不是枯燥、零碎地介绍 PKI、X.509、OID 等概念,而是从前因后果、历史沿革 的角度把这些东西串联起来,逻辑非常清晰,让读者知其然,更知其所以然。

证书和 PKI 的目标其实很简单:将名字关联到公钥(bind names to public keys)。

加密方式的演进:

 MAC         最早的验证消息是否被篡改的方式,发送消息时附带一段验证码|          双方共享同一密码,做哈希;最常用的哈希算法:HMAC|\/Signature   解决 MAC 存在的一些问题;双方不再共享同一密码,而是使用密钥对||\/PKC         公钥加密,或称非对称加密,最常用的一种 Signature 方式|          公钥给别人,私钥自己留着;|          发送给我的消息:别人用 *我的公钥* 加密;我用我的私钥解密\/Certificate   公钥加密的基础,概念:CA/issuer/subject/relying-party/...|          按功能来说,分为两种||---用于 *签名*(签发其他证书) 的证书|---用于 *加解密* 的证书

证书(certificate)相关格式及其关系(沉重的历史负担):

  最常用的格式   |      信息比 X.509 更丰富的格式       |       其他格式mTLS 等常用        Java 常用            微软常用.p7b .p7c          .pfx .p12X.509 v3            PKCS#7               PKCS#12        SSH 证书    PGP 证书     =====>  证书格式\                 |                    /                                           (封装格式,证书结构体)\                |                   /\               |                  /\              |                 /\-------------+----------------/|ASN.1 (类似于 JSON、ProtoBuf 等)                          =====>  描述格式|/-------------+----------------\/              |                 \/               |                  \/                |                   \/                 |                    \DER                 PEM                                                         =====>  编码格式
二进制格式           文本格式                                                             (序列化).der            .pem .crt .cer

一些解释:

  1. X.509 从结构上定义证书中应该包含的信息,例如签发者、秘钥等等;但使用哪个格式 (例如 JSON 还是 YAML 还是 ASN.1)来描述,并不属于 X.509 的内容;

  2. ASN.1 是 X.509 的描述格式(或者说用 ASN.1 格式来定义 X.509),类似于现在的 protobuf;

    • ASN 中有很多数据类型,除了常见的整形、字符串等类型,还有一个称为 OID 的特殊类型,用点分整数表示,例如2.5.4.3,有点像 URI 或 IP 地址,在设计上是全球唯一标识符,

    • ASN.1 只是一种描述格式,并未定义如何序列化为比特流,因此又引出了 ASN.1 的编码格式。

  3. ASN.1 与其编码格式的关系,与unicode 与 utf8 的关系类似。

    • DER:一种二进制编码格式

    • PEM:一种文本编码格式,通常以.pem.crt 或 .cer为后缀。

  4. 某些场景下,X.509 信息不够丰富,因此又设计了一些信息更丰富(例如可以包含证书 链、秘钥)的证书封装格式,包括 PKCS #7 和 #12。

    • 仍然用 ASN.1 格式描述

    • 基本都是用 DER 编码

以上提到的东西,再加上 CA、信任仓库、信任链、certificate path validation、CSR、证书生命周期管理、 SPIFFE 等还没有提到但也与加密相关的东西,统称为公钥基础设施(PKI)。

1 前言

证书(certificates)与PKI(public key infrastructure,公钥基础设施)很难。我认识的很多非常聪明的人也会绕过这一主题。我个人也很长时间没去碰这些内容,但说起来很讽刺,我没去碰的原因是不懂,所以不好意思问 —— 然后更不懂,自然更不好意思问—— 如此形成恶性循环。

1.1 为什么要学习 PKI

最终我还是硬着头皮学习了这些东西,因为我觉得PKI 能使一个人在加解密层面(乃至更大的安全层面)去思考如何定义一个系统。具体来说,PKI 技术,

  • 都是通用的、厂商无关的(universal and vendor neutral);

  • 适用于任何地方,因此即使系统可分布在世界各地,彼此之间也能安全地通信;

  • 在概念上很简单,并且非常灵活;如果使用我们的 TLS everywhere[2] 模型, 那甚至连 VPN 都不需要了。

总之一句话:非常强大!

1.2 本文目的

在深入理解了 PKI 之后,我很后悔没有早点学这些东西。

  1. PKI 非常强大且有趣,虽然它背后的数学原理很复杂,一些相关标准也设计地非常愚蠢(巴洛克式的复杂),但其核心概念其实非常简单;

  2. 证书是识别(identify)代码和设备的最佳方式, 而 identity(身份)对安全、监控、指标等很多东西都非常有用;

  3. 使用证书并不是太难,不会难于学习一门新语言或一种新数据库。

那为什么大家对这些内容望而却步呢?我认为主要是缺少很好的文档,所以经常看地云里雾里,半途而弃。

本文试图弥补这一缺失。我认为大部分工程师花一个小时读完本文后,头脑中都将有具备了那些关于加解密的最重要概念和最常见使用场景—— 这正是本文的目的 —— 一小时只是很小的一个投资,而且这些内容是无法通过其他途径学到的。

本文将用到以下两个开源工具:

  • step CLI[3]

  • step certificates[4]

1.3 极简 TL; DR(太长不读)

证书和 PKI 的目的是:将名字关联到公钥(bind names to public keys)。

这是关于证书和 PKI 的最高抽象,其他都属于实现细节。

2 术语

本文将用到以下术语。

2.1 Entity(实体)

Entity 是任何存在的东西(anything that exists) —— 即使只在逻辑或概念上存在(even if only exists logically or conceptually)。例如,

  • 你的计算机是一个 entity,

  • 你写的代码也是一个 entity,

  • 你自己也是一个 entity,

  • 你吃的杂粮饼也是一个 entity,

  • 你六岁时见过的幽灵也是一个 entity —— 即使你妈告诉你幽灵并不存在,这只是你的臆想。

2.2 Identity(身份)

每个 entity(实体)都有一个 identity(身份)。要精确定义这个概念比较困难,这么来说吧:identity 是使你之所以为你(what makes you you)的东西,懂吗?

具体到计算机领域,identity 通常用一系列属性来表示,描述某个具体的 entity, 这里的属性包括 group、age、location、favorite color、shoe size 等等。

2.3 Identifier(身份标识符)

Identifier 跟 identity 还不是一个东西:每个 identifier 都是一个唯一标识符, 也唯一地关联到某个有 identity 的 entity。

例如,我是 Mike,但 Mike 并不是我的 identity,而只是个 name —— 虽然二者在我们 小范围的讨论中是同义的。

2.4 Claim(声明) & Authentication(认证)

  • 一个 entity 能claim(声明)说,它拥有某个或某些 name。

  • 其他 entity 能够对这个 claim 进行认证(authenticate),以确认这份声明的真假。

    一般来说,认证的目的是确认某些 claim 的合法性。

  • Claim 不是只能关联到 name,还可以关联到别的东西。例如,我能 claim 任何东西:my age, your age, access rights, the meaning of life 等等。

2.5 Subscriber & CA & relying party (RP)

  • 能作为一个证书的 subject 的 entity,称为subscriber(证书 owner)或end entity。

    对应地,subscriber 的证书有时也称为 end entity certificates 或 leaf certificates, 原因在后面讨论 certificate chains 时会介绍。

  • CA(certificate authority,证书权威)是给 subscriber 颁发证书的 entity,是一种 certificate issuer(证书颁发者)。

    CA 的证书,通常称为root certificate或 intermediate certificate,具体取决于 CA 类型。

  • Relying party 是使用证书的用户(certificate user),它验证由 CA 颁发(给 subscriber)的证书是否合法。

    一个 entity 可以同时是一个 subscriber 和一个 relying party。也就是说,单个 entity 既有自己的证书,又使用其他证书来认证 remote peers, 例如双向 TLS(mutual TLS,mTLS)场景。

2.6 小结

对于我们接下来的讨论,这些术语就够了。下面将进入正题,看如何在实际中实现证书的声明和认证。

想了解更多相关术语,可参考 RFC 4949[5]。

3 MAC(消息认证码)和 signature(签名)

3.1 MAC(message authentication code)和 HMAC(hash-based MAC)

MAC(消息认证码)是一小段数据,用于验证某个 entity 发送的消息未被篡改。其基本原理如下图所示:

MAC/HMAC 原理。图片来自:[okta.com](https://www.okta.com/identity-101/hmac/ "okta.com")

  1. 对消息(message)和双方都知道的一个密码(shared secret,a password)做哈希,得到的哈希值就是 MAC;

  2. 发送方将消息连带 MAC 一起发给接收方;

  3. 接收方收到消息之后,用同一个密码来计算 MAC,然后跟消息中提供的 MAC 对比。如果相同,就证明未被篡改。

关于哈希:

  • 哈希是单向的,因此无法从输出反推输入。这一点至关重要:否则截获消息的人就可以根据 MAC 和哈希函数反推出你的 secrets。

  • 此外,生成 MAC 的哈希算法选择也至关重要,本文不会展开,但 提醒一点:不要试图用自己设计的 MAC 算法。

  • 最常用的 MAC 算法是 HMAC[6](hash-based message authentication code)。

3.2 Signature(签名)

讨论 MAC 其实是为了引出 signature(签名)这一主题。

签名在概念上与 MAC 类似,但不是用共享 secret 的方式, 而是使用一对秘钥(key pair):

  • MAC 方式中,至少有两个 entity 需要知道共享的 secret,也就是消息的发送方和接 收方。双方都可以生成 MAC,因此给定一个合法的 MAC,我们是无法知道是谁生成的。

  • 签名就不同了。签名能用公钥(public key)验证,但只能用相应的私钥(private key)生成。因此对于接收方来说,它只能验证签名是否合法,而无法生成同样的签名。

如果只有一个 entity 知道秘钥,那这种特性称为non-repudiation(不可否认性):持有私钥的人无法否认(repudiate)数据是由他签名的这一事实。

3.3 小结

MAC 与 signature 都叫做签名,是因为它们和现实世界中的签名是很像的。例如,如果想 让某人同意某事,并且事后还能证明他们当时的确同意了,就把问题写下来,然后让他们 手写签字(签名)。

4 Public key cryptography(公钥加密,或称非对称加密)

证书和 PKI 的基础是公钥加密(public key cryptography), 也叫非对称加密(asymmetric cryptography)。

4.1 秘钥对

公钥加密系统使用秘钥对(key pair)加解密。一个秘钥对包含:

  1. 一个私钥(private key):owner 持有,解密用,不要分享给任何人;

    这一点非常重要,值得重复一遍:公钥加密系统的安全性取决于私钥(private key)的机密性。

  2. 一个公钥(public key):加密用,可分发和共享给别人;

秘钥可以做的事情:

  1. 加解密:公钥(public key)加密,私钥(private key)解密。

  2. 签名:私钥(private key)对数据进行签名(sign some data);任何有公钥的人都可以对签名进行验证,证明这个签名确实是私钥生成的。

4.2 公钥加密系统使计算机能“看到”对方

公钥加密是数学给计算机科学的神秘礼物,其数学基础[7]显然很复杂,但如果只是使用,那并不理解它的每一步数学原理。公钥加密使计算机能做一些之前无法做的事情:它们现在能看到对方是谁了。

这句话的意思是说,公钥加密使一台计算(或代码)能向其他计算机或程序证明,不用直接分享某些信息,它也能知道该信息。更具体来说,

  • 以前要证明你有密码,就必须向别人展示这个密码。但展示之后,任何有这个密码的人就都能使用它了。

  • 私钥却与此不同。你能通过私钥对我的身份进行认证(authenticate my identity),但却无法假冒我。

    例如,你发给我一个大随机数,我对这个随机数进行签名,然后将再发送给你。你能用公钥对这个签名进行认证,确认这个签名(消息)确实来自我。这就是一种证明你在和我(而不是别的其他的人)通信的很好证据。这使得网络上的 计算机能有效地知道它们在和谁通信。

    这听起来是一件如此理所当然的事情,但仔细地想一下,网络上只有流动的 0 和 1, 你怎么知道消息来自谁,在和谁通信?因此公钥加密系统是一个非常伟大的发明。

5 证书(certificate):计算机和代码的驾驶证

前面说道,公钥加密系统使我们能知道和谁在通信,但这个的前提是:要知道(有)对方的公钥。

那么,如果对方不知道我的公钥怎么办?这就轮到证书出场了。

想一下,我们需求其实非常简单:

  • 首先要将公钥和它的 owner信息发给对方;

  • 但光有这个信息还不行,还要让对方相信这些信息;

    证书就是用来解决这个问题的,解决方式是请一个双方都信任的权威机构对以上信息作出证明(签名)。

5.1 证书的内容:(subscriber 的)公钥+名字

  • 证书是一个数据结构,其中包含一个 public key 和一个 name;

  • 权威机构对证书进行签名,签名的大概意思是:public key xxx 关联到了 name xx;

    对证书进行签名的 entity 称为issuer(或 certificate authority, CA), 证书中的 entity 称为subject。

举个例子,如果某个 Issuer 为 Bob 签发了一张证书,其中的内容就可以解读如下:

Some Issuer says Bob's public key is 01:23:42...

证书是权威机构颁发的身份证明,并没有什么神奇之处

其中 Some Issuer 是证书的签发者(证书权威),证书是为了证明这是 Bob 的公钥,Some Issuer 也是这个声明的签字方。

5.2 证书的本质:基于对 issuer 公钥的信任来学习其他公钥

由上可知,如果知道 Some Issuer 的公钥,就可以通过验证签名的方式来对它(用私钥)签发的证书进行认证(authenticate)。如果如果你信任 Some Issuer,那你就可以信任这个声明。

因此,证书使大家能基于对 issuer 公钥的信任和知识,来学习到其他 entity 的公钥(上面的例子中就是 Bob)。这就是证书的本质。

5.3 与驾照的类比

证书就像是计算机/代码的驾照或护照。如果你之前从未见过我,但信任车管局,那你可以 用我的驾照做认证:

  • 首先验证驾照是真的(检查 hologram 等),

  • 然后人脸和照片上对的上,

  • 然后看名字是我,等等。

计算机用证书做类似的事情:如果之前从未和其他电脑通信,但信任 一些证书权威,那可以用证书来认证:

  • 首先验证证书是合法的(用证书签发者的公钥检查签名等),

  • 然后提取证书中的(subscriber 的)公钥和名字,

  • 然后用 subscriber 的公钥,通过网络验证该 subscriber 的签名;

  • 查看名字是否正确等等。

5.4 证书内容解析举例

下面是个真实的证书:

还是与驾照类比:

  • 驾照:描述了你是否有资格开车;

  • 证书:描述你是否是一个 CA,你的公钥能否用来签名或加密。

  • 二者都有有效期。

上图中有大量的细节,很多东西将在下面讨论到。但归根结底还是本文最开始总结的那句话 :证书不过是一个将名字关联到公钥(bind names to public keys)的东西。其他都是实现细节。

6 证书编码格式及历史演进

接下来看一看证书在底层的表示(represented as bits and bytes)。

这部分内容复杂且相当令人沮丧。事实上,我怀疑证书和秘钥诡异的编码方式是导致 PKI 如此混乱和令人沮丧的根源。

6.1 X.509 证书

一般来说,人们提到“证书”而没有加额外限定词时,指的都是 X.509 v3 证书。

  • 更准确地说,他们指的是RFC 5280[8] 中描述、 CA/Browser Forum Baseline Requirements[9]中进一步完善的 PKIX 变种。

  • 换句话说,指的是浏览器理解并用来做 HTTPS(HTTP over TLS)的那些证书。

也有其他的证书格式,例如著名的SSH 和 PGP 都有它们各自的格式。

本文主要关注 X.509,理解了 X.509,其他格式都是类似的。由于这些证书使用广泛,因此有很好的函数库,而且也用在浏览器之外的场景。毫无疑问,它们是 internal PKI 颁发的最常见证书格式。重要的是,这些证书在很多 TLS/HTTPS 客户端/服 务端程序中都是开箱即用的。

X.509 起源:电信领域

了解一点 X.509 的历史对理解它会有很大帮助。

X.509 在1988年作为国际电信联盟(ITU)X.500 项目的一部分首次标准化。这是通信(telecom)领域的标准,想通过它构建一个全球电话簿(global telephone book)。虽然这个项目没有成功,但却留下了一些遗产,X.509 就是其中之一。

如果查看 X.509 的证书,会看到其中包含了 locality、state、country 等信息, 之前可能会有疑问为什么为 web 设计的证书会有这些东西,现在应该明白了,因为X.509 并不是为 web 设计的。

6.2 ASN.1:数据抽象格式

X.509 构建在 ASN.1 (Abstract Syntax Notation,抽象语法标注)之上,后者是另一个 ITU-T 标准 (X.208 and X.680)。

ASN.1 定义数据类型,

  • 可以将 ASN.1 理解成X.509 的 JSON,

  • 但实际上更像 protobuf、thrift 或 SQL DDL。

RFC 5280用 ASN.1 来定义 X.509 证书,其中包括名字、秘钥、签名等信息。

6.3 OID (object identitfier)

ASN.1 除了有常见的数据类型,如整形、字符串、集合、列表等, 还有一个不常见但很重要的类型:OID(object identifier,对象标识符)。

  • OID与 URI 有些像,但比 URI 要怪。

  • OID (在设计上)是全球唯一标识符。

  • 在结构上,OID 是在一个 hierarchical namespace 中的一个整数序列(例如 2.5.4.3)。

可以用 OID 来 tag 一段数据的类型。例如,一个 string 本来只是一个 string,但可 以 tag 一个 OID 2.5.4.3,然后就变成了一个特殊 string:这是X.509 的通用名字(common name)字段。

6.4 ASN.1 编码格式

ASN.1 只是抽象(abstract),因为这个标准并未定义在数据层应该如何表示(represented as bits and bytes)。ASN.1 与其编码格式的关系,就像unicode 与 utf8 的区别。因此,有很多种编码规则(encoding rules),描述具体如何表示 ASN.1 数据。原以为增加这层额外的抽象会有所帮助,但实际证明大部分情况下反而徒增烦恼。

DER (distinguished encoding rules):二进制格式

ASN.1 有很多种编码规则[10], 但用于 X.509 和其他加密相关的,只有一种常见格式:DER —— 虽然有时也会用到 non-canonical 的 basic encoding rules (BER,基础编码规则) 。

DER 是非常简单的 TLV(type-length-value)编码,但实际上用户无需 关心这些,因为函数库封装好了。但不要高兴得太早 —— 虽然我们不必关心 DER 的编解码, 但要能判断给定的某个 X.509 证书是 DER 还是其他类型编码的。这里的其他类型包括:

  1. 一些比 DER 更友好的格式,

  2. 封装了证书及其他额外信息的格式(something more than just a certificate)。

DER 编码的证书通常以.der为后缀。

PEM (privacy enhanced email):文本格式

DER 是二进制格式,不便复制粘贴。因此大部分证书都是以PEM[11] 格式打包的,这是另一个历史怪胎。

如果你熟悉 MIME[12] 的话,二者是比较类似的:由 header、base64 编码的 payload、footer 三部分组成。header 中有标签(label)来描述 payload。例如下面是一个PEM 编码的 X.509 证书:

-----BEGIN CERTIFICATE-----
MIIBwzCCAWqgAwIBAgIRAIi5QRl9kz1wb+SUP20gB1kwCgYIKoZIzj0EAwIwGzEZ
MBcGA1UEAxMQTDVkIFRlc3QgUm9vdCBDQTAeFw0xODExMDYyMjA0MDNaFw0yODEx
BgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBRc+LHppFk8sflIpm/XKpbNMwx3
SDAfBgNVHSMEGDAWgBTirEpzC7/gexnnz7ozjWKd71lz5DAKBggqhkjOPQQDAgNH
ADBEAiAejDEfua7dud78lxWe9eYxYcM93mlUMFIzbWlOJzg+rgIgcdtU9wIKmn5q
FU3iOiRP5VyLNmrsQD3/ItjUN1f1ouY=
-----END CERTIFICATE-----

但令人震惊的时,即便如此简单的功能,在实现上也已经出现混乱:PEM labels 在不同工具之间是不一致的。RFC 7468[13] 试图标准化 PEM 的使用规范, 但也并不完整,不是所有工具都遵循这个规范。

PEM 编码的证书通常以.pem.crt 或 .cer为后缀。再次提醒,这只是“通常”情况,实际上某些工具可能并不遵循这些惯例。

下面介绍几个前面提到的“其他类型的打包格式”。

6.5 比 X.509 信息更丰富的证书打包(封装)格式

X.509 只是一种常用的证书格式,但有人觉得这种格式能装的信息不够多,因此 又定义了一些比 X.509更大的数据结构(但仍然用 ASN.1), 能将证书、秘钥以及其他东西封装(打包)到一起。因此,有时说我需要“一个证书”时,其 实真正说的是包(package)中包含的那个“证书”(a certificate in one of these envelopes),而不是这个包本身。

PKCS #7:Java 中常用

你可能会遇到的是一个称为 PKCS(Public Key Cryptography Standards,公钥加密标准)的标准的一部分, 它由 RSA labs 发布(真实历史要 更加复杂一些[14],本文不展开)。

其中的第一个标准是 PKCS#7[15],后面被 IETF 重新冠名为 Cryptographic Message Syntax[16] (CMS) ,其中可以包含多个证书(以 full certificate chain 方式编码,后面会看到)。

PKCS#7在 Java 中使用广泛。常见扩展名是 .p7b and .p7c

PKCS #12:微软常用

另一个常见的打包格式 

关于证书(certificate)和公钥基础设施(PKI)的一切相关推荐

  1. 公钥基础设施 PKI 技术与应用发展

    公钥基础设施 PKI 技术与应用发展 一.概述 PKI是"Public Key Infrastructure"的缩写,意为"公钥基础设施".简单地说,PKI技术 ...

  2. IRIS 2021 技术文档 First Look 28 - InterSystems 公钥基础设施 (PKI)

    本文档介绍了 InterSystems 公钥基础设施(PKI),它可以在开发组织的安全策略中发挥重要作用.它提供有关公钥加密.证书颁发机构和 PKI 的信息.然后介绍一些与使用 InterSystem ...

  3. 信安小白,一篇博文讲明白数字证书和PKI(公钥基础设施)

    数字证书 系列博文 前言 一.数字证书 二.CA(Certification Authority)认证 2.1 CA认证信息 2.2 CA结构 2.3 一般的证书产生流程 2.4 完整流程 三.PKI ...

  4. 公钥基础设施 (Public-key infrastructure PKI)

    公钥基础设施 PKI 1.功能 2.设计 3.认证方法 3.1证书机构(CA) 3.2信任网络(Wot) 3.3简单的公共关键基础设施(SKPI) 3.4分散的PKI 3.5基于区块链的 PKI 4. ...

  5. 什么是 PKI? 公钥基础设施的定义和指南

    公钥基础设施 (PKI) 管理 Internet 通信中的身份和安全性,以保护人员.设备和数据. 组织依靠 PKI 解决方案来验证和加密流经 Web 服务器.数字身份.连接设备和应用程序的信息.随着组 ...

  6. 给工程师:关于证书(certificate)和公钥基础设施(PKI)的一切

    公众号关注 「奇妙的 Linux 世界」 设为「星标」,每天带你玩转 Linux ! 译者序 本文翻译自 2018 年的一篇英文博客:Everything you should know about ...

  7. PKI 公钥基础设施原理与应用

    文章目录 PKI 是什么 PKI 详细介绍 PKI 的组成 核心算法 CA 机构 数字证书 证书撤销机制 应用:访问控制 参考资料 PKI 是什么 Public Key Infrastructure( ...

  8. RFC2459 Internet X.509 公钥基础设施:证书和CRL简介

    组织:中国互动出版网(http://www.china-pub.com/) RFC文档中文翻译计划(http://www.china-pub.com/compters/emook/aboutemook ...

  9. rfc2459:Internet X.509 公钥基础设施:证书和 CRL 简介

    组织:中国互动出版网(http://www.china-pub.com/) RFC文档中文翻译计划(http://www.china-pub.com/compters/emook/aboutemook ...

最新文章

  1. python工程师-史上最全Python工程师常见面试题集锦,有这一份就够了
  2. oracle实时监控触发邮件,利用EasySQLMAIL实现数据库订单监控和邮件发送
  3. java 中文 图片_java之服务器端写图片中文乱码
  4. ICE专题:实现简单的聊天室(一)
  5. 华为云classroom应用_华为任正非:将来所有应用都会长在云土地上,但现在还不是...
  6. wps生成正态分布的随机数_量子计算与机器学习: 量子生成对抗网络QGAN
  7. 数据报套接字服务端和客户端
  8. termux apache php,要啥自行车之Termux:将我们的(Android)安卓手机打造成全能的服务器...
  9. Java查看字节码工具
  10. c常用算法程序集_10万赞的AI算法集:含python,java,C,C++多种语言
  11. 【学步者日记】UnityEditor扩展菜单以及ScriptableObject
  12. python机器学习案例系列教程——算法总结
  13. SQL注入靶场 RedTiger通关教程(level1~level10)
  14. Android openGl开发详解(二)
  15. 去除Opera与迅雷的关联
  16. 计算机网络第一章-概叙 思维导图
  17. HDU 2209 C - 翻纸牌游戏
  18. 《蹭课神器》Alpha版使用说明
  19. 对抗机器学习:Generating Adversarial Malware Examples for Black-box Attacks Based on GAN
  20. UML 活动图 ---- 建模业务过程

热门文章

  1. Base编码(贝斯家族)
  2. 三人行-有分享才会有行动
  3. Hyperledger Explorer 区块链浏览器
  4. 【毕业设计】基于单片机的门禁系统 - 嵌入式 物联网
  5. 实时控制软件第二周作业 停车场门禁控制系统的状态机设计
  6. 脉冲发生器c语言程序,可编程脉冲信号发生器的.doc
  7. Kali Linux渗透测试——WEB渗透(一)
  8. VB/VBA的变量,竟还有这些秘密
  9. Excel VBA 金融建模 培训
  10. telnet如何开启?