证书搜索
搜索是 crt.sh 的核心,也是本 SDK 最复杂的方法。它把 crt.sh 高级搜索页(?a=1)的全部能力结构化了。
能力概览
🔍22 种搜索类型
按域名、CN、SAN、SHA 指纹、CA、邮箱、IP 等 22 个维度检索
🎯7 种匹配模式
精确 / ILIKE / LIKE / single / any / FTS / 自动
🧩分组与排序
none / icaid 分组,3 种排序方向
🛡️5 个 linter
搜索时同时跑 cablint/x509lint/zlint/keylint/all
22 种搜索类型
不知道该用哪个?按手头线索走一遍决策树:
flowchart TD START["手头有什么?"] --> D{按域名?} D -->|是| DN["通用 Q
?q=example.com"] D -->|否| FP{有指纹?} FP -->|SHA-256/SHA-1| C["c
?c=fingerprint"] FP -->|否| ID{有 crt.sh ID?} ID -->|证书 ID| ID1["id
?id=12345"] ID -->|CT 条目 ID| ID2["ctid
?ctid=..."] ID -->|否| SUB{按主体字段?} SUB -->|CN| CN1["CN
?CN=name"] SUB -->|SAN 域名| SAN1["dNSName
?dNSName=domain"] SUB -->|SAN 邮箱| SAN2["rfc822Name"] SUB -->|序列号| SN["serial"] SUB -->|否| CA{查 CA?} CA -->|按 ID| CAID["CAID"] CA -->|按名称| CAN["CAName/ca"] CA -->|否| OTHER["SKI / SPKISHA / O / OU / E ..."]
?q=example.com"] D -->|否| FP{有指纹?} FP -->|SHA-256/SHA-1| C["c
?c=fingerprint"] FP -->|否| ID{有 crt.sh ID?} ID -->|证书 ID| ID1["id
?id=12345"] ID -->|CT 条目 ID| ID2["ctid
?ctid=..."] ID -->|否| SUB{按主体字段?} SUB -->|CN| CN1["CN
?CN=name"] SUB -->|SAN 域名| SAN1["dNSName
?dNSName=domain"] SUB -->|SAN 邮箱| SAN2["rfc822Name"] SUB -->|序列号| SN["serial"] SUB -->|否| CA{查 CA?} CA -->|按 ID| CAID["CAID"] CA -->|按名称| CAN["CAName/ca"] CA -->|否| OTHER["SKI / SPKISHA / O / OU / E ..."]
关键
搜索类型本身就是 URL 参数名。?CN=example.com,不是 ?searchtype=CN&common_name=example.com。
| 类型 | 含义 | URL 形态 |
|---|---|---|
""(空) | 通用域名搜索 | ?q=example.com |
c | 证书指纹(SHA-1 或 SHA-256) | ?c=fingerprint |
id | crt.sh 证书 ID | ?id=12345 |
ctid | CT 条目 ID | ?ctid=id |
serial | 序列号 | ?serial=serial |
ski | Subject Key Identifier | ?ski=hex |
spkisha1 | SHA-1(SubjectPublicKeyInfo) | ?spkisha1=hex |
spkisha256 | SHA-256(SubjectPublicKeyInfo) | ?spkisha256=hex |
subjectsha1 | SHA-1(Subject) | ?subjectsha1=hex |
sha1 | SHA-1(Certificate) | ?sha1=hex |
sha256 | SHA-256(Certificate) | ?sha256=hex |
ca | CA(通用) | ?ca=name |
CAID | CA ID | ?CAID=id |
CAName | CA 名称 | ?CAName=name |
Identity | Identity | ?Identity=val |
CN | commonName(Subject) | ?CN=name |
E | emailAddress(Subject) | ?E=email |
OU | organizationalUnitName | ?OU=val |
O | organizationName | ?O=val |
dNSName | dNSName(SAN) | ?dNSName=domain |
rfc822Name | rfc822Name(SAN) | ?rfc822Name=email |
iPAddress | iPAddress(SAN) | ?iPAddress=ip |
7 种匹配模式
match 控制 identity 字符串怎么匹配:
| 模式 | SQL 等价 | 含义 |
|---|---|---|
"" | Auto | 让 crt.sh 自己选 |
= | = | 精确匹配 |
ILIKE | ILIKE | 大小写不敏感模式匹配 |
LIKE | LIKE | 大小写敏感模式匹配 |
single | — | 匹配单个 identity 值 |
any | — | 匹配任意 identity 值 |
FTS | 全文搜索 | 跨所有字段全文检索 |
请求构造流程
flowchart TD P["QueryParams"] --> V["Validate 校验类型/模式"] V --> B["buildQuery 翻译成 URL"] B --> SET["设置参数:
搜索类型作 key
exclude=expired
deduplicate=Y
iCAID/linter/group/sort/dir/page"] SET --> EX{端点支持 JSON?} EX -->|"id/ctid 不支持"| HTML["不加 output=json
返回 HTML"] EX -->|其他| JSON["加 output=json
返回 JSON 数组"] JSON --> SDK["GET crt.sh?params"] HTML --> SDK SDK --> U["json.Unmarshal → []Certificate"] SDK --> PG["解析 Link header → Pagination"]
搜索类型作 key
exclude=expired
deduplicate=Y
iCAID/linter/group/sort/dir/page"] SET --> EX{端点支持 JSON?} EX -->|"id/ctid 不支持"| HTML["不加 output=json
返回 HTML"] EX -->|其他| JSON["加 output=json
返回 JSON 数组"] JSON --> SDK["GET crt.sh?params"] HTML --> SDK SDK --> U["json.Unmarshal → []Certificate"] SDK --> PG["解析 Link header → Pagination"]
分页
crt.sh 通过 HTTP Link header 暴露分页:
sequenceDiagram participant C as Client participant S as crt.sh C->>S: GET 第1页 S-->>C: JSON + Link 头指向第2页 C->>C: parseLinkHeader 得到 NextPage=2 C->>S: GET 第2页 S-->>C: JSON(无 next Link) Note over C: 到底,停止
IterateCertificates 在此基础上做自动翻页。
典型用法
go
certs, _, _ := client.SearchCertificates(ctx, crtsh.QueryParams{
Q: "example.com",
Deduplicate: true,
ExcludeExpired: true,
})go
certs, _, _ := client.SearchCertificates(ctx, crtsh.QueryParams{
SearchType: "sha256",
SHA256: "abcd...",
})go
certs, _, _ := client.SearchCertificates(ctx, crtsh.QueryParams{
Q: "example.com",
IssuerCAID: "12345",
Group: "icaid",
})go
certs, _, _ := client.SearchCertificates(ctx, crtsh.QueryParams{
Q: "example.com",
Linter: "zlint",
LintType: "issues",
})关键陷阱(封装已解决)
陷阱一:id/ctid 不支持 JSON
?id= 与 ?ctid= 端点 crt.sh 不支持 output=json。buildQuery 自动跳过 output=json。
陷阱二:布尔参数格式
exclude=expired 而非 excludeExpired=on,deduplicate=Y 而非 deduplicate=on。
陷阱三:linter 名即参数名
?zlint=issues,不是 ?linter=zlint&linttype=issues。
陷阱四:大小写敏感
CAID、CAName、Identity、CN、dNSName、rfc822Name、iPAddress 必须原样大小写。
下一篇