Add CAA checker, based on TLS observations
Some checks are pending
continuous-integration/drone/push Build is pending
Some checks are pending
continuous-integration/drone/push Build is pending
This commit is contained in:
parent
50257e630c
commit
0d35040a40
7 changed files with 601 additions and 159 deletions
34
checkers/caa.go
Normal file
34
checkers/caa.go
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2026 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package checkers
|
||||
|
||||
import (
|
||||
caa "git.happydns.org/checker-caa/checker"
|
||||
sdk "git.happydns.org/checker-sdk-go/checker"
|
||||
"git.happydns.org/happyDomain/internal/checker"
|
||||
)
|
||||
|
||||
func init() {
|
||||
prvd := caa.Provider()
|
||||
checker.RegisterObservationProvider(prvd)
|
||||
checker.RegisterExternalizableChecker(prvd.(sdk.CheckerDefinitionProvider).Definition())
|
||||
}
|
||||
|
|
@ -27,5 +27,6 @@ package main
|
|||
//go:generate go run tools/gen_rr_typescript.go web/src/lib/dns_rr.ts
|
||||
//go:generate go run tools/gen_service_specs.go -o web/src/lib/services_specs.ts
|
||||
//go:generate go run tools/gen_dns_type_mapping.go -o internal/usecase/service_specs_dns_types.go
|
||||
//go:generate go run tools/gen_caa_issuers.go -o web/src/lib/services/caa-issuers.json https://ccadb.my.salesforce-sites.com/ccadb/AllCAAIdentifiersReportCSVV2
|
||||
//go:generate swag init --parseDependency --exclude internal/api-admin/ --generalInfo internal/api/route/route.go
|
||||
//go:generate swag init --parseDependency --output docs-admin --exclude internal/api/ --generalInfo internal/api-admin/route/route.go
|
||||
|
|
|
|||
1
go.mod
1
go.mod
|
|
@ -8,6 +8,7 @@ require (
|
|||
git.happydns.org/checker-alias v0.1.0
|
||||
git.happydns.org/checker-authoritative-consistency v0.1.0
|
||||
git.happydns.org/checker-blacklist v0.1.0
|
||||
git.happydns.org/checker-caa v0.1.0
|
||||
git.happydns.org/checker-dane v0.1.3
|
||||
git.happydns.org/checker-dangling v0.1.0
|
||||
git.happydns.org/checker-dav v0.1.0
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -14,6 +14,8 @@ git.happydns.org/checker-authoritative-consistency v0.1.0 h1:+0XvJFC7tFVf0Dgruew
|
|||
git.happydns.org/checker-authoritative-consistency v0.1.0/go.mod h1:hPxEDSyrPq+KY9YU5QoZ1btecw/cU/Miouuacaz4wzk=
|
||||
git.happydns.org/checker-blacklist v0.1.0 h1:IV44Lxnw0dLBhoyAkAlq9A+hTB5B4RF4vLiW+nX21gg=
|
||||
git.happydns.org/checker-blacklist v0.1.0/go.mod h1:DRHkpULz8F6dKm0LUErAAQln0x8XByg+/UxbUY46oZk=
|
||||
git.happydns.org/checker-caa v0.1.0 h1:L0kg9dqdJqmjaPrgbLtBvgEE6+e+7EVSSRPB5pIzNIQ=
|
||||
git.happydns.org/checker-caa v0.1.0/go.mod h1:7ecPoFRYT0+Fl5DG17Xvz9Xh2alwgEpSSaE2rp0EcT0=
|
||||
git.happydns.org/checker-dane v0.1.3 h1:9VpQ4FrWJE/O6MZ08FCk1vmHsr3u5V7478als9Y4jl8=
|
||||
git.happydns.org/checker-dane v0.1.3/go.mod h1:md5SQA8M1QGq9MoXe3QVV+m55I+r8lU4iYx5KzvkbII=
|
||||
git.happydns.org/checker-dangling v0.1.0 h1:gZVyHAKG2U1FXBt7cPnZsr45JQWZ21jlThKhHckb+i8=
|
||||
|
|
|
|||
405
tools/gen_caa_issuers.go
Normal file
405
tools/gen_caa_issuers.go
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2022-2026 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
subjectColumn = "Subject"
|
||||
domainColumn = "Recognized CAA Domains"
|
||||
)
|
||||
|
||||
// row represents a single CCADB intermediate and the CAA domains it recognizes.
|
||||
type row struct {
|
||||
owner string
|
||||
domains []string
|
||||
}
|
||||
|
||||
// score tracks how well a candidate owner matches a CAA domain across all
|
||||
// rows that mention that domain.
|
||||
type score struct {
|
||||
rowCount int
|
||||
minSize int
|
||||
nameMatch bool
|
||||
}
|
||||
|
||||
func main() {
|
||||
output := flag.String("o", "", "path to write the generated JSON file")
|
||||
flag.Parse()
|
||||
|
||||
if *output == "" {
|
||||
fatal("missing required -o flag")
|
||||
}
|
||||
if flag.NArg() < 1 {
|
||||
fatal("missing CCADB CSV URL (first positional argument)")
|
||||
}
|
||||
url := flag.Arg(0)
|
||||
|
||||
rows, err := fetchAndParse(url)
|
||||
if err != nil {
|
||||
fatal(err.Error())
|
||||
}
|
||||
|
||||
mapping := buildDomainToOwner(rows)
|
||||
|
||||
if err := writeJSON(*output, mapping); err != nil {
|
||||
fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func fetchAndParse(url string) ([]row, error) {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GET %s: %w", url, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("GET %s: unexpected status %s", url, resp.Status)
|
||||
}
|
||||
|
||||
return parseCSV(resp.Body)
|
||||
}
|
||||
|
||||
func parseCSV(r io.Reader) ([]row, error) {
|
||||
reader := csv.NewReader(r)
|
||||
reader.FieldsPerRecord = -1
|
||||
|
||||
header, err := reader.Read()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read header: %w", err)
|
||||
}
|
||||
|
||||
subjectIdx := indexOf(header, subjectColumn)
|
||||
domainIdx := indexOf(header, domainColumn)
|
||||
if subjectIdx < 0 {
|
||||
return nil, fmt.Errorf("column %q not found in CSV header", subjectColumn)
|
||||
}
|
||||
if domainIdx < 0 {
|
||||
return nil, fmt.Errorf("column %q not found in CSV header", domainColumn)
|
||||
}
|
||||
|
||||
var rows []row
|
||||
for {
|
||||
record, err := reader.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read row: %w", err)
|
||||
}
|
||||
if subjectIdx >= len(record) || domainIdx >= len(record) {
|
||||
continue
|
||||
}
|
||||
|
||||
owner := extractOrganization(record[subjectIdx])
|
||||
if owner == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
domains := splitDomains(record[domainIdx])
|
||||
if len(domains) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
rows = append(rows, row{owner: owner, domains: domains})
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
// buildDomainToOwner inverts the CCADB rows into a CAA-domain → owner mapping.
|
||||
//
|
||||
// For each CAA identifier, the "authoritative" owner is selected by:
|
||||
// 1. Preferring owners whose name contains a significant label of the CAA
|
||||
// domain (e.g. "digicert.com" prefers "DigiCert, Inc." over cross-signed
|
||||
// subordinates that also list digicert.com in their recognized set).
|
||||
// 2. Then highest row count: real root CAs have many intermediates all
|
||||
// recognizing their own identifier.
|
||||
// 3. Then smallest minimum Recognized-CAA-Domains set: roots typically
|
||||
// recognize just their own identifier, while subordinates inherit larger
|
||||
// sets from cross-signing parents.
|
||||
// 4. Alphabetical for determinism.
|
||||
//
|
||||
// Owner names are grouped case-insensitively to collapse CCADB casing
|
||||
// inconsistencies (e.g. "Cloudflare, Inc." vs "CLOUDFLARE, INC."), preferring
|
||||
// the variant with the fewest all-caps words.
|
||||
func buildDomainToOwner(rows []row) map[string]string {
|
||||
canonical := canonicalOwners(rows)
|
||||
|
||||
scores := map[string]map[string]*score{}
|
||||
|
||||
for _, r := range rows {
|
||||
owner := canonical[strings.ToLower(r.owner)]
|
||||
size := len(r.domains)
|
||||
for _, dn := range r.domains {
|
||||
if _, ok := scores[dn]; !ok {
|
||||
scores[dn] = map[string]*score{}
|
||||
}
|
||||
s, ok := scores[dn][owner]
|
||||
if !ok {
|
||||
s = &score{minSize: size, nameMatch: ownerMatchesDomain(owner, dn)}
|
||||
scores[dn][owner] = s
|
||||
}
|
||||
s.rowCount++
|
||||
if size < s.minSize {
|
||||
s.minSize = size
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out := make(map[string]string, len(scores))
|
||||
for dn, byOwner := range scores {
|
||||
var bestOwner string
|
||||
var best *score
|
||||
for owner, s := range byOwner {
|
||||
if best == nil || scoreBetter(s, owner, best, bestOwner) {
|
||||
best = s
|
||||
bestOwner = owner
|
||||
}
|
||||
}
|
||||
out[dn] = bestOwner
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func scoreBetter(a *score, ao string, b *score, bo string) bool {
|
||||
if a.nameMatch != b.nameMatch {
|
||||
return a.nameMatch
|
||||
}
|
||||
if a.rowCount != b.rowCount {
|
||||
return a.rowCount > b.rowCount
|
||||
}
|
||||
if a.minSize != b.minSize {
|
||||
return a.minSize < b.minSize
|
||||
}
|
||||
return ao < bo
|
||||
}
|
||||
|
||||
// genericDomainLabels are labels that appear in CAA domains but don't identify
|
||||
// a specific CA brand (e.g. "pki.goog" is Google, not "pki"). Anything shorter
|
||||
// than 3 characters (TLDs) is also skipped.
|
||||
var genericDomainLabels = map[string]bool{
|
||||
"com": true, "net": true, "org": true, "gov": true, "edu": true,
|
||||
"co": true,
|
||||
"pki": true, "tls": true, "ssl": true, "www": true, "eca": true,
|
||||
"publicca": true, "epki": true, "cert": true, "trust": true,
|
||||
"certificate": true, "ca": true,
|
||||
}
|
||||
|
||||
// ownerMatchesDomain returns true if a significant label of the CAA domain
|
||||
// appears as a substring of the owner name (alphanumeric-only, lowercased).
|
||||
// Used to prefer self-referential owners (e.g. "DigiCert" for "digicert.com")
|
||||
// over cross-signed subordinates that also list the domain.
|
||||
func ownerMatchesDomain(owner, caaDomain string) bool {
|
||||
normName := alphaNumLower(owner)
|
||||
for _, label := range strings.Split(caaDomain, ".") {
|
||||
label = strings.ToLower(label)
|
||||
if len(label) < 3 || genericDomainLabels[label] {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(normName, label) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func alphaNumLower(s string) string {
|
||||
var b strings.Builder
|
||||
b.Grow(len(s))
|
||||
for _, r := range s {
|
||||
switch {
|
||||
case r >= 'a' && r <= 'z':
|
||||
b.WriteRune(r)
|
||||
case r >= 'A' && r <= 'Z':
|
||||
b.WriteRune(r + ('a' - 'A'))
|
||||
case r >= '0' && r <= '9':
|
||||
b.WriteRune(r)
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// canonicalOwners returns a map from lowercased owner name to the preferred
|
||||
// display variant. Preference: fewer all-caps words, then lexicographically
|
||||
// smallest (for determinism).
|
||||
func canonicalOwners(rows []row) map[string]string {
|
||||
variants := map[string]map[string]struct{}{}
|
||||
for _, r := range rows {
|
||||
key := strings.ToLower(r.owner)
|
||||
if _, ok := variants[key]; !ok {
|
||||
variants[key] = map[string]struct{}{}
|
||||
}
|
||||
variants[key][r.owner] = struct{}{}
|
||||
}
|
||||
|
||||
out := make(map[string]string, len(variants))
|
||||
for key, vs := range variants {
|
||||
picks := make([]string, 0, len(vs))
|
||||
for v := range vs {
|
||||
picks = append(picks, v)
|
||||
}
|
||||
sort.Slice(picks, func(i, j int) bool {
|
||||
ai, aj := allCapsWords(picks[i]), allCapsWords(picks[j])
|
||||
if ai != aj {
|
||||
return ai < aj
|
||||
}
|
||||
return picks[i] < picks[j]
|
||||
})
|
||||
out[key] = picks[0]
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// allCapsWords counts words (whitespace-delimited) that contain at least one
|
||||
// letter and are entirely uppercase — a proxy for "ALL CAPS" shouting that we
|
||||
// want to avoid when choosing a canonical display form.
|
||||
func allCapsWords(s string) int {
|
||||
n := 0
|
||||
for _, w := range strings.Fields(s) {
|
||||
hasLetter := false
|
||||
allUpper := true
|
||||
for _, r := range w {
|
||||
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') {
|
||||
hasLetter = true
|
||||
if r >= 'a' && r <= 'z' {
|
||||
allUpper = false
|
||||
}
|
||||
}
|
||||
}
|
||||
if hasLetter && allUpper {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func splitDomains(cell string) []string {
|
||||
var out []string
|
||||
for _, dn := range strings.FieldsFunc(cell, func(r rune) bool {
|
||||
return r == ',' || r == ' ' || r == '\t' || r == '\n' || r == '\r'
|
||||
}) {
|
||||
dn = strings.ToLower(strings.TrimSpace(dn))
|
||||
if isDomainName(dn) {
|
||||
out = append(out, dn)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// isDomainName filters free-form text leaking from the CCADB "Recognized CAA
|
||||
// Domains" cell (tokens like "None", "N/A", "Comma-separated", "list.", or
|
||||
// sentence fragments). Requires at least two non-empty DNS-like labels and a
|
||||
// TLD of at least two letters.
|
||||
func isDomainName(s string) bool {
|
||||
labels := strings.Split(s, ".")
|
||||
if len(labels) < 2 {
|
||||
return false
|
||||
}
|
||||
for _, l := range labels {
|
||||
if l == "" || !isDomainLabel(l) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
tld := labels[len(labels)-1]
|
||||
if len(tld) < 2 {
|
||||
return false
|
||||
}
|
||||
for _, r := range tld {
|
||||
if r < 'a' || r > 'z' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isDomainLabel(l string) bool {
|
||||
if l[0] == '-' || l[len(l)-1] == '-' {
|
||||
return false
|
||||
}
|
||||
for _, r := range l {
|
||||
switch {
|
||||
case r >= 'a' && r <= 'z':
|
||||
case r >= '0' && r <= '9':
|
||||
case r == '-':
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func writeJSON(path string, data map[string]string) error {
|
||||
buf, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal JSON: %w", err)
|
||||
}
|
||||
buf = append(buf, '\n')
|
||||
|
||||
tmp := path + ".tmp"
|
||||
if err := os.WriteFile(tmp, buf, 0o644); err != nil {
|
||||
return fmt.Errorf("write %s: %w", tmp, err)
|
||||
}
|
||||
if err := os.Rename(tmp, path); err != nil {
|
||||
return fmt.Errorf("rename %s -> %s: %w", tmp, path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// extractOrganization returns the O= (organization) value from an RFC-4514-ish
|
||||
// DN string as provided by CCADB (fields separated by "; ").
|
||||
func extractOrganization(subject string) string {
|
||||
for _, field := range strings.Split(subject, "; ") {
|
||||
field = strings.TrimSpace(field)
|
||||
if v, ok := strings.CutPrefix(field, "O="); ok {
|
||||
return strings.TrimSpace(v)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func indexOf(header []string, name string) int {
|
||||
for i, h := range header {
|
||||
if strings.TrimSpace(h) == name {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func fatal(msg string) {
|
||||
fmt.Fprintln(os.Stderr, "gen_caa_issuers: "+msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
146
web/src/lib/services/caa-issuers.json
Normal file
146
web/src/lib/services/caa-issuers.json
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
{
|
||||
"1and1.digitalcertvalidation.com": "DigiCert, Inc.",
|
||||
"accv.es": "ACCV",
|
||||
"acme.jprs.jp": "Japan Registry Services Co., Ltd.",
|
||||
"acme.trust.telia.com": "Telia Company AB",
|
||||
"actalis.it": "Actalis S.p.A.",
|
||||
"admin.ch": "Swiss Government PKI",
|
||||
"affirmtrust.com": "AffirmTrust",
|
||||
"almostfreessl.com": "SSLCom Group ltd.",
|
||||
"amazon.com": "Amazon",
|
||||
"amazonaws.com": "DigiCert, Inc.",
|
||||
"amazontrust.com": "DigiCert, Inc.",
|
||||
"anf.es": "ANF Autoridad de Certificacion",
|
||||
"aoc.cat": "Agencia Catalana de Certificacio (NIF Q-0801176-I)",
|
||||
"atos.net": "Atos",
|
||||
"aws.amazon.com": "Amazon",
|
||||
"awstrust.com": "DigiCert, Inc.",
|
||||
"bjca.cn": "BEIJING CERTIFICATE AUTHORITY",
|
||||
"buypass.com": "Buypass AS-983163327",
|
||||
"buypass.no": "Buypass AS-983163327",
|
||||
"camerfirma.com": "AC Camerfirma S.A.",
|
||||
"certainly.com": "Certainly",
|
||||
"certicamara.com": "CERTICAMARA S.A",
|
||||
"certifyid.com": "WISeKey",
|
||||
"certigna.com": "Certigna",
|
||||
"certigna.fr": "Certigna",
|
||||
"certsign.ro": "CERTSIGN SA",
|
||||
"certum.eu": "Asseco Data Systems S.A.",
|
||||
"certum.pl": "Asseco Data Systems S.A.",
|
||||
"cfca.com.cn": "China Financial Certification Authority",
|
||||
"cisco.com": "Cisco Systems",
|
||||
"comodo.com": "COMODO CA Limited",
|
||||
"comodoca.com": "COMODO CA Limited",
|
||||
"comsign.co.il": "ComSign Ltd.",
|
||||
"comsign.co.uk": "ComSign Ltd.",
|
||||
"comsigneurope.com": "ComSign Ltd.",
|
||||
"cybertrust.co.jp": "Cybertrust Japan Co., Ltd.",
|
||||
"cybertrust.ne.jp": "Cybertrust Japan Co., Ltd.",
|
||||
"d-trust.de": "D-Trust GmbH",
|
||||
"d-trust.net": "D-Trust GmbH",
|
||||
"darkmatter.ae": "DarkMatter LLC",
|
||||
"desc.gov.ae": "UAE Government",
|
||||
"dfn.de": "Deutsche Telekom Security GmbH",
|
||||
"digicert.com": "DigiCert, Inc.",
|
||||
"digicert.ne.jp": "DigiCert, Inc.",
|
||||
"digitalcertvalidation.com": "DigiCert, Inc.",
|
||||
"disig.sk": "Disig a.s.",
|
||||
"docusign.fr": "OpenTrust",
|
||||
"dtrust.de": "D-Trust GmbH",
|
||||
"dvv.fi": "Digi- ja vaestotietovirasto CA",
|
||||
"e-szigno.hu": "Microsec Ltd.",
|
||||
"e-tugra.com": "E-Tugra EBG A.S.",
|
||||
"e-tugra.com.tr": "E-Tugra EBG A.S.",
|
||||
"eca.hinet.net": "Chunghwa Telecom Co., Ltd.",
|
||||
"ecert.gov.hk": "Hongkong Post",
|
||||
"edicomgroup.com": "EDICOM CAPITAL SL",
|
||||
"elektronicznypodpis.pl": "Krajowa Izba Rozliczeniowa S.A.",
|
||||
"emsign.com": "eMudhra Technologies Limited",
|
||||
"entrust.net": "Entrust, Inc.",
|
||||
"epki.com.tw": "Chunghwa Telecom Co., Ltd.",
|
||||
"etugra.com": "E-Tugra EBG A.S.",
|
||||
"etugra.com.tr": "E-Tugra EBG A.S.",
|
||||
"fineid.fi": "Digi- ja vaestotietovirasto CA",
|
||||
"firmaprofesional.com": "Firmaprofesional S.A.",
|
||||
"fnmt.es": "FNMT-RCM",
|
||||
"gca.nat.gov.tw": "行政院",
|
||||
"gdca.com.cn": "Global Digital Cybersecurity Authority Co., Ltd.",
|
||||
"geotrust.com": "GeoTrust Inc.",
|
||||
"globalsign.com": "GlobalSign nv-sa",
|
||||
"globaltrust.eu": "e-commerce monitoring GmbH",
|
||||
"godaddy.com": "GoDaddy.com",
|
||||
"gtlsca.nat.gov.tw": "行政院",
|
||||
"harica.gr": "Hellenic Academic and Research Institutions CA",
|
||||
"hightrusted.com": "WISeKey",
|
||||
"hongkongpost.gov.hk": "Hongkong Post",
|
||||
"identrust.com": "IdenTrust",
|
||||
"imtrust.cn": "北京中科三方网络技术有限公司",
|
||||
"intermediatecertificate.digitalcertvalidation.com": "DigiCert, Inc.",
|
||||
"itrus.cn": "iTrusChina Co.,Ltd.",
|
||||
"izenpe.com": "IZENPE S.A.",
|
||||
"izenpe.eus": "IZENPE S.A.",
|
||||
"jprs.jp": "Japan Registry Services Co., Ltd.",
|
||||
"kamusm.gov.tr": "TUBITAK Kamu Sertifikasyon Merkezi",
|
||||
"letsencrypt.org": "Let's Encrypt",
|
||||
"microsoft.com": "Microsoft Corporation",
|
||||
"msctrustgate.com": "MSC Trustgate.com Sdn. Bhd.",
|
||||
"multicert.com": "MULTICERT - Serviços de Certificação Electrónica S.A.",
|
||||
"navercloudtrust.com": "NAVER Cloud Trust Services Corp.",
|
||||
"netlock.com": "NetLock Ltd.",
|
||||
"netlock.eu": "NetLock Ltd.",
|
||||
"netlock.hu": "NetLock Ltd.",
|
||||
"netlock.net": "NetLock Ltd.",
|
||||
"networksolutions.com": "Network Solutions L.L.C.",
|
||||
"nrca.go.th": "National Telecom Public Company Limited",
|
||||
"oaticerts.com": "Open Access Technology International Inc",
|
||||
"oiste.org": "OISTE Foundation",
|
||||
"pki.apple.com": "Apple Inc.",
|
||||
"pki.dfn.de": "Deutsche Telekom Security GmbH",
|
||||
"pki.eviden.com": "Eviden",
|
||||
"pki.goog": "Google Trust Services",
|
||||
"pki.hinet.net": "Chunghwa Telecom Co., Ltd.",
|
||||
"pkiworks.com": "CommScope",
|
||||
"postsignum.cz": "Česká pošta, s.p.",
|
||||
"publicca.hinet.net": "Chunghwa Telecom Co., Ltd.",
|
||||
"quovadisglobal.com": "DigiCert, Inc.",
|
||||
"rapidssl.com": "DigiCert, Inc.",
|
||||
"secomtrust.net": "SECOM Trust Systems CO.,LTD.",
|
||||
"sectigo.com": "Sectigo Limited",
|
||||
"sheca.com": "UniTrust",
|
||||
"skidsolutions.eu": "AS Sertifitseerimiskeskus",
|
||||
"solutissl.com": "Soluti",
|
||||
"ssl.com": "SSL Corporation",
|
||||
"ssl.gov.sa": "Baud Telecom Company",
|
||||
"ssl.gpki.go.kr": "Ministry of the Interior and Safety",
|
||||
"sslcomgroup.com": "SSLCom Group ltd.",
|
||||
"starfieldtech.com": "Starfield Technologies, Inc.",
|
||||
"startcomca.com": "StartCom CA",
|
||||
"startssl.com": "StartCom Ltd.",
|
||||
"stratossl.digitalcertvalidation.com": "DigiCert, Inc.",
|
||||
"swisssign.com": "SwissSign AG",
|
||||
"symantec.com": "Symantec Corporation",
|
||||
"telesec.de": "Deutsche Telekom Security GmbH",
|
||||
"telia.com": "Telia Company AB",
|
||||
"telia.fi": "Telia Company AB",
|
||||
"telia.se": "Telia Company AB",
|
||||
"thawte.com": "DigiCert, Inc.",
|
||||
"tls.hinet.net": "Chunghwa Telecom Co., Ltd.",
|
||||
"trust-provider.com": "Sectigo Limited",
|
||||
"trust.telia.com": "Telia Company AB",
|
||||
"trustasia.com": "TrustAsia Technologies, Inc.",
|
||||
"trustcor.ca": "TrustCor Systems S. de R.L.",
|
||||
"trustfactory.net": "TrustFactory(Pty)Ltd",
|
||||
"tuntrust.tn": "Agence Nationale de Certification Electronique",
|
||||
"twca.com.tw": "TWCA",
|
||||
"usertrust.com": "The USERTRUST Network",
|
||||
"vincasign.net": "VINTEGRIS SL",
|
||||
"visa.com": "VISA",
|
||||
"volusion.digitalcertvalidation.com": "DigiCert, Inc.",
|
||||
"web.com": "Network Solutions L.L.C.",
|
||||
"wisekey.com": "WISeKey",
|
||||
"wosign.com": "WoSign CA Limited",
|
||||
"www.certinomis.com": "Certinomis",
|
||||
"www.certinomis.fr": "Certinomis",
|
||||
"www.digicert.com": "DigiCert, Inc.",
|
||||
"www.identrust.com": "IdenTrust"
|
||||
}
|
||||
|
|
@ -19,167 +19,20 @@
|
|||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
export const issuers: Record<string, Array<string>> = {
|
||||
"Actalis S.p.A.": ["actalis.it"],
|
||||
"Amazon Trust Services LLC": [
|
||||
"amazon.com",
|
||||
"amazontrust.com",
|
||||
"awstrust.com",
|
||||
"amazonaws.com",
|
||||
"aws.amazon.com",
|
||||
],
|
||||
"ANF AC": ["anf.es"],
|
||||
"Asseco Data Systems (formely Certum)": ["certum.pl", "certum.eu"],
|
||||
Apple: ["pki.apple.com"],
|
||||
Atos: ["atos.net"],
|
||||
"Beijing CA": ["bjca.cn"],
|
||||
"Buypass AS": ["buypass.com", "buypass.no"],
|
||||
CAcert: ["cacert.org"],
|
||||
"AC Camerfirma S.A.": ["camerfirma.com"],
|
||||
CATCert: ["aoc.cat"],
|
||||
"Fastly/Certainly": ["certainly.com"],
|
||||
Certinomis: ["www.certinomis.com", "www.certinomis.fr"],
|
||||
Dhimyotis: ["certigna.fr"],
|
||||
Certizen: ["ecert.gov.hk", "hongkongpost.gov.hk"],
|
||||
certSIGN: ["certsign.ro"],
|
||||
"China Financial CA (CFCA)": ["cfca.com.cn"],
|
||||
"China Internet Network Information Center (CNNIC)": ["cnnic.cn"],
|
||||
"Chunghwa Telecom": [
|
||||
"pki.hinet.net",
|
||||
"tls.hinet.net",
|
||||
"eca.hinet.net",
|
||||
"epki.com.tw",
|
||||
"publicca.hinet.net",
|
||||
],
|
||||
"ComSign Ltd": ["comsign.co.il", "comsign.co.uk", "comsigneurope.com"],
|
||||
"Cybertrust Japan": ["cybertrust.ne.jp", "jcsinc.co.jp"],
|
||||
"Deutsche Telekom Security": ["telesec.de"],
|
||||
"DFN-PKI": ["pki.dfn.de", "dfn.de"],
|
||||
DigiCert: [
|
||||
"digicert.com",
|
||||
"symantec.com",
|
||||
"geotrust.com",
|
||||
"rapidssl.com",
|
||||
"thawte.com",
|
||||
"digitalcertvalidation.com",
|
||||
"volusion.digitalcertvalidation.com",
|
||||
"stratossl.digitalcertvalidation.com",
|
||||
"intermediatecertificate.digitalcertvalidation.com",
|
||||
"1and1.digitalcertvalidation.com",
|
||||
],
|
||||
"DigitalSign CD": ["digitalsign.pt"],
|
||||
"Disig, a.s.": ["disig.sk"],
|
||||
DocuSign: ["docusign.fr"],
|
||||
"D-Trust GmbH": ["dtrust.de", "d-trust.de", "dtrust.net", "d-trust.net"],
|
||||
DigitalTrust: ["digitaltrust.ae"],
|
||||
EDICOM: ["edicomgroup.com"],
|
||||
"eMudhra Technologies Limited": ["emsign.com"],
|
||||
Entrust: ["entrust.net", "affirmtrust.com"],
|
||||
"E-TUGRA Inc.": ["e-tugra.com", "e-tugra.com.tr", "etugra.com", "etugra.com.tr"],
|
||||
"AC Firmaprofesional CIF": ["firmaprofesional.com"],
|
||||
"Guang Dong CA Co.": ["gdca.com.cn"],
|
||||
GlobalSign: ["globalsign.com"],
|
||||
"Global Trust": ["globaltrust.eu"],
|
||||
"GoDaddy Inc.": ["godaddy.com", "starfieldtech.com"],
|
||||
"Google Trust Services (GTS)": ["pki.goog", "google.com"],
|
||||
"ACCV (Spain gov)": ["accv.es"],
|
||||
"FNMT (Spain gov)": ["fnmt.es"],
|
||||
"Agence Nationale de Certification Electronique (Tunisia gov)": ["tuntrust.tn"],
|
||||
GRCA: ["gca.nat.gov.tw"],
|
||||
HARICA: ["harica.gr"],
|
||||
"Hongkong Post": ["ecert.gov.hk", "hongkongpost.gov.hk"],
|
||||
IdenTrust: ["identrust.com", "www.identrust.com"],
|
||||
"iTrusChina Co.": ["itrus.com.cn", "itrus.cn"],
|
||||
"IZENPE S.A.": ["izenpe.com", "izenpe.eus"],
|
||||
"Japan Registry Services": ["jprs.co.jp"],
|
||||
"Kamu Sertifikasyon Merkezi": ["kamusm.gov.tr"],
|
||||
"KPN Corporate Market BV": ["kpn.com"],
|
||||
"Krajowa Izba Rozliczeniowa S.A. (KIR)": ["elektronicznypodpis.pl"],
|
||||
LAWtrust: ["lawtrust.co.za"],
|
||||
"Let's Encrypt": ["letsencrypt.org"],
|
||||
"Logius PKIoverheid": ["logius.nl"],
|
||||
"Microsec Ltd.": ["e-szigno.hu"],
|
||||
Microsoft: ["microsoft.com"],
|
||||
"Microsoft IT": ["ssladmin.microsoft.com"],
|
||||
"MSC Trustgate": ["msctrustgate.com"],
|
||||
"National Center for Digital Certification (NCDC)": ["ncdc.gov.sa"],
|
||||
"NAVER Business Platform Corp.": ["certificate.naver.com"],
|
||||
"NetLock Kft.": ["netlock.hu", "netlock.net", "netlock.eu"],
|
||||
Networking4all: ["trustproviderbv.digitalcertvalidation.com"],
|
||||
"Network Solutions LLC": ["networksolutions.com", "web.com"],
|
||||
"OISTE Foundation": ["wisekey.com", "hightrusted.com", "certifyid.com", "oiste.org"],
|
||||
"Open Access Technology International": ["oati.com"],
|
||||
"Prvni certifikacni autorita, a.s.": ["ica.cz"],
|
||||
PKIoverheid: ["www.pkioverheid.nl"],
|
||||
QuoVadis: [
|
||||
"quovadisglobal.com",
|
||||
"digicert.com",
|
||||
"digicert.ne.jp",
|
||||
"cybertrust.ne.jp",
|
||||
"symantec.com",
|
||||
"thawte.com",
|
||||
"geotrust.com",
|
||||
"rapidssl.com",
|
||||
"digitalcertvalidation.com",
|
||||
],
|
||||
"SECOM Trust Systems": ["secomtrust.net"],
|
||||
Sectigo: ["sectigo.com", "comodo.com", "comodoca.com", "usertrust.com", "trust-provider.com"],
|
||||
"Shanghai Electronic Certification Authority Co. Ltd": [
|
||||
"sheca.com",
|
||||
"imtrust.cn",
|
||||
"wwwtrust.cn",
|
||||
],
|
||||
"SK ID Solutions AS": ["skidsolutions.eu"],
|
||||
"SSL Corporation": ["ssl.com"],
|
||||
"Skaitmeninio sertifikavimo centras (SSC)": ["ssc.lt"],
|
||||
"SwissSign AG": [
|
||||
"swisssign.com",
|
||||
"swisssign.net",
|
||||
"swissign.com",
|
||||
"swisssign.ch",
|
||||
"swisssign.li",
|
||||
"swissign.li",
|
||||
"swisssign.org",
|
||||
"swisssign.biz",
|
||||
"swisstsa.ch",
|
||||
"swisstsa.li",
|
||||
"digitalid.ch",
|
||||
"digital-id.ch",
|
||||
"zert.ch",
|
||||
"rootsigning.com",
|
||||
"root-signing.ch",
|
||||
"ssl-certificate.ch",
|
||||
"managed-pki.ch",
|
||||
"managed-pki.de",
|
||||
"swissstick.com",
|
||||
"swisssigner.ch",
|
||||
"pki-posta.ch",
|
||||
"pki-poste.ch",
|
||||
"pki-post.ch",
|
||||
"trustdoc.ch",
|
||||
"trustsign.ch",
|
||||
"swisssigner.com",
|
||||
"postsuisseid.ch",
|
||||
"suisseid-service.ch",
|
||||
"signdemo.com",
|
||||
"sirb.com",
|
||||
],
|
||||
"SecureTrust Corporation": ["trustwave.com", "securetrust.com"],
|
||||
"TAIWAN-CA Inc. (TWCA)": ["twca.com.tw"],
|
||||
"Telia Finland Oyj": ["telia.com", "telia.fi", "telia.se"],
|
||||
"TrustCor Systems": ["trustcor.ca"],
|
||||
"T-Systems Enterprise Services": ["t-systems.com"],
|
||||
Visa: ["visa.com"],
|
||||
Zertificon: ["zertificon.com"],
|
||||
"360": ["browser.360.cn"],
|
||||
};
|
||||
import data from "./caa-issuers.json";
|
||||
|
||||
export const rev_issuers: Record<string, string> = {};
|
||||
export const rev_issuers: Record<string, string> = data;
|
||||
|
||||
for (const issuer in issuers) {
|
||||
for (const dn of issuers[issuer]) {
|
||||
rev_issuers[dn] = issuer;
|
||||
}
|
||||
export const issuers: Record<string, Array<string>> = {};
|
||||
|
||||
for (const dn in rev_issuers) {
|
||||
const owner = rev_issuers[dn];
|
||||
if (!issuers[owner]) issuers[owner] = [];
|
||||
issuers[owner].push(dn);
|
||||
}
|
||||
|
||||
for (const owner in issuers) {
|
||||
issuers[owner].sort();
|
||||
}
|
||||
|
||||
export default issuers;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue