Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 64 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,27 @@ Give domain names as arguments.

```sh
$ cert github.com google.co.jp
DomainName: github.com
IP: 192.30.255.113
Port: 443
Issuer: DigiCert SHA2 Extended Validation Server CA
NotBefore: 2016-03-10 09:00:00 +0900 JST
NotAfter: 2018-05-17 21:00:00 +0900 JST
CommonName: github.com
SANs: [github.com www.github.com]
DomainName: github.com
IP: 192.30.255.113
Port: 443
Issuer: DigiCert SHA2 Extended Validation Server CA
Validation: EV
Organization: GitHub, Inc.
NotBefore: 2016-03-10 09:00:00 +0900 JST
NotAfter: 2018-05-17 21:00:00 +0900 JST
CommonName: github.com
SANs: [github.com www.github.com]
Error:

DomainName: google.co.jp
IP: 216.58.196.227
Port: 443
Issuer: Google Internet Authority G3
NotBefore: 2017-10-17 19:59:51 +0900 JST
NotAfter: 2018-01-09 19:00:00 +0900 JST
CommonName: *.google.co.jp
SANs: [*.google.co.jp google.co.jp]
DomainName: google.co.jp
IP: 216.58.196.227
Port: 443
Issuer: Google Internet Authority G3
Validation: DV
NotBefore: 2017-10-17 19:59:51 +0900 JST
NotAfter: 2018-01-09 19:00:00 +0900 JST
CommonName: *.google.co.jp
SANs: [*.google.co.jp google.co.jp]
Error:

```
Expand All @@ -55,34 +58,38 @@ So you can get server certificate information of not only web server but also *m

```sh
$ cert github.com google.co.jp:443 imap.gmail.com:993
DomainName: github.com
IP: 192.30.255.113
Port: 443
Issuer: DigiCert SHA2 Extended Validation Server CA
NotBefore: 2016-03-10 09:00:00 +0900 JST
NotAfter: 2018-05-17 21:00:00 +0900 JST
CommonName: github.com
SANs: [github.com www.github.com]
DomainName: github.com
IP: 192.30.255.113
Port: 443
Issuer: DigiCert SHA2 Extended Validation Server CA
Validation: EV
Organization: GitHub, Inc.
NotBefore: 2016-03-10 09:00:00 +0900 JST
NotAfter: 2018-05-17 21:00:00 +0900 JST
CommonName: github.com
SANs: [github.com www.github.com]
Error:

DomainName: google.co.jp
IP: 172.217.27.163
Port: 443
Issuer: Google Internet Authority G3
NotBefore: 2017-10-17 19:59:51 +0900 JST
NotAfter: 2018-01-09 19:00:00 +0900 JST
CommonName: *.google.co.jp
SANs: [*.google.co.jp google.co.jp]
DomainName: google.co.jp
IP: 172.217.27.163
Port: 443
Issuer: Google Internet Authority G3
Validation: DV
NotBefore: 2017-10-17 19:59:51 +0900 JST
NotAfter: 2018-01-09 19:00:00 +0900 JST
CommonName: *.google.co.jp
SANs: [*.google.co.jp google.co.jp]
Error:

DomainName: imap.gmail.com
IP: 64.233.188.108
Port: 993
Issuer: Google Internet Authority G2
NotBefore: 2017-10-17 19:10:29 +0900 JST
NotAfter: 2017-12-29 09:00:00 +0900 JST
CommonName: imap.gmail.com
SANs: [imap.gmail.com]
DomainName: imap.gmail.com
IP: 64.233.188.108
Port: 993
Issuer: Google Internet Authority G2
Validation: DV
NotBefore: 2017-10-17 19:10:29 +0900 JST
NotAfter: 2017-12-29 09:00:00 +0900 JST
CommonName: imap.gmail.com
SANs: [imap.gmail.com]
Error:

```
Expand Down Expand Up @@ -127,18 +134,20 @@ Use `cert -f json`.
$ cert -f json github.com | jq .
[
{
"DomainName": "github.com",
"IP": "192.30.255.112",
"Port": "443",
"Issuer": "DigiCert SHA2 Extended Validation Server CA",
"CommonName": "github.com",
"SANs": [
"domainName": "github.com",
"ip": "192.30.255.112",
"port": "443",
"issuer": "DigiCert SHA2 Extended Validation Server CA",
"validationType": "EV",
"organization": "GitHub, Inc.",
"commonName": "github.com",
"sans": [
"github.com",
"www.github.com"
],
"NotBefore": "2016-03-10 09:00:00 +0900 JST",
"NotAfter": "2018-05-17 21:00:00 +0900 JST",
"Error": ""
"notBefore": "2016-03-10 09:00:00 +0900 JST",
"notAfter": "2018-05-17 21:00:00 +0900 JST",
"error": ""
}
]
```
Expand All @@ -149,14 +158,14 @@ Use `cert -f md`.

```sh
$ cert -f md github.com
DomainName | IP | Port | Issuer | NotBefore | NotAfter | CN | SANs | Error
--- | --- | --- | --- | --- | --- | --- | --- | ---
github.com | 192.30.255.113 | 443 | DigiCert SHA2 Extended Validation Server CA | 2016-03-10 09:00:00 +0900 JST | 2018-05-17 21:00:00 +0900 JST | github.com | github.com<br/>www.github.com<br/> |
DomainName | IP | Port | Issuer | Validation | Organization | NotBefore | NotAfter | CN | SANs | Error
--- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ---
github.com | 192.30.255.113 | 443 | DigiCert SHA2 Extended Validation Server CA | EV | GitHub, Inc. | 2016-03-10 09:00:00 +0900 JST | 2018-05-17 21:00:00 +0900 JST | github.com | github.com<br/>www.github.com<br/> |
```

DomainName | IP | Port | Issuer | NotBefore | NotAfter | CN | SANs | Error
--- | --- | --- | --- | --- | --- | --- | --- | ---
github.com | 192.30.255.113 | 443 | DigiCert SHA2 Extended Validation Server CA | 2016-03-10 09:00:00 +0900 JST | 2018-05-17 21:00:00 +0900 JST | github.com | github.com<br/>www.github.com<br/> |
DomainName | IP | Port | Issuer | Validation | Organization | NotBefore | NotAfter | CN | SANs | Error
--- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ---
github.com | 192.30.255.113 | 443 | DigiCert SHA2 Extended Validation Server CA | EV | GitHub, Inc. | 2016-03-10 09:00:00 +0900 JST | 2018-05-17 21:00:00 +0900 JST | github.com | github.com<br/>www.github.com<br/> |

### Specify output format by Go template

Expand Down
149 changes: 117 additions & 32 deletions cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"crypto/tls"
"crypto/x509"
"encoding/asn1"
"encoding/json"
"fmt"
"io/ioutil"
Expand Down Expand Up @@ -102,18 +103,38 @@ func SplitHostPort(hostport string) (string, string, error) {
}

type Cert struct {
DomainName string `json:"domainName"`
IP string `json:"ip"`
Port string `json:"port"`
Issuer string `json:"issuer"`
CommonName string `json:"commonName"`
SANs []string `json:"sans"`
NotBefore string `json:"notBefore"`
NotAfter string `json:"notAfter"`
Error string `json:"error"`
certChain []*x509.Certificate
DomainName string `json:"domainName"`
IP string `json:"ip"`
Port string `json:"port"`
Issuer string `json:"issuer"`
ValidationType string `json:"validationType"`
Organization string `json:"organization,omitempty"`
CommonName string `json:"commonName"`
SANs []string `json:"sans"`
NotBefore string `json:"notBefore"`
NotAfter string `json:"notAfter"`
Error string `json:"error"`
certChain []*x509.Certificate
}

const (
ValidationTypeDV = "DV"
ValidationTypeOV = "OV"
ValidationTypeEV = "EV"
)

var (
cabfDVPolicy = asn1.ObjectIdentifier{2, 23, 140, 1, 2, 1}
cabfOVPolicy = asn1.ObjectIdentifier{2, 23, 140, 1, 2, 2}
cabfEVPolicy = asn1.ObjectIdentifier{2, 23, 140, 1, 1}

oidBusinessCategory = asn1.ObjectIdentifier{2, 5, 4, 15}
oidJurisdictionLocality = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 60, 2, 1, 1}
oidJurisdictionState = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 60, 2, 1, 2}
oidJurisdictionCountry = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 60, 2, 1, 3}
oidSubjectSerialNumber = asn1.ObjectIdentifier{2, 5, 4, 5}
)

func cipherSuite() ([]uint16, error) {
if CipherSuite == "" {
return nil, nil
Expand All @@ -135,6 +156,65 @@ func tlsVersion() uint16 {
return 0
}

func hasPolicy(cert *x509.Certificate, oid asn1.ObjectIdentifier) bool {
for _, policy := range cert.PolicyIdentifiers {
if policy.String() == oid.String() {
return true
}
}
return false
}

func hasSubjectAttribute(cert *x509.Certificate, oid asn1.ObjectIdentifier) bool {
for _, name := range cert.Subject.Names {
if name.Type.String() == oid.String() {
return true
}
}
return false
}

func isEV(cert *x509.Certificate) bool {
if hasPolicy(cert, cabfEVPolicy) {
return true
}
if len(cert.Subject.Organization) == 0 {
return false
}
if !hasSubjectAttribute(cert, oidBusinessCategory) {
return false
}
if !hasSubjectAttribute(cert, oidSubjectSerialNumber) {
return false
}
return hasSubjectAttribute(cert, oidJurisdictionCountry) ||
hasSubjectAttribute(cert, oidJurisdictionState) ||
hasSubjectAttribute(cert, oidJurisdictionLocality)
}

func validationType(cert *x509.Certificate) string {
if isEV(cert) {
return ValidationTypeEV
}
if hasPolicy(cert, cabfOVPolicy) {
return ValidationTypeOV
}
if hasPolicy(cert, cabfDVPolicy) {
return ValidationTypeDV
}
if len(cert.Subject.Organization) > 0 {
return ValidationTypeOV
}
return ValidationTypeDV
}

func organization(cert *x509.Certificate, validationType string) string {
if validationType == ValidationTypeDV || len(cert.Subject.Organization) == 0 {
return ""
}
return strings.Join(cert.Subject.Organization, ", ")
}

var serverCert = func(host, port string) ([]*x509.Certificate, string, error) {
d := &net.Dialer{
Timeout: time.Duration(TimeoutSeconds) * time.Second,
Expand Down Expand Up @@ -178,18 +258,21 @@ func NewCert(hostport string) *Cert {
if UTC {
loc = time.UTC
}
vt := validationType(cert)

return &Cert{
DomainName: host,
IP: ip,
Port: port,
Issuer: cert.Issuer.CommonName,
CommonName: cert.Subject.CommonName,
SANs: cert.DNSNames,
NotBefore: cert.NotBefore.In(loc).String(),
NotAfter: cert.NotAfter.In(loc).String(),
Error: "",
certChain: certChain,
DomainName: host,
IP: ip,
Port: port,
Issuer: cert.Issuer.CommonName,
ValidationType: vt,
Organization: organization(cert, vt),
CommonName: cert.Subject.CommonName,
SANs: cert.DNSNames,
NotBefore: cert.NotBefore.In(loc).String(),
NotAfter: cert.NotAfter.In(loc).String(),
Error: "",
certChain: certChain,
}
}

Expand Down Expand Up @@ -239,15 +322,17 @@ func NewCerts(s []string) (Certs, error) {
return certs, nil
}

const defaultTempl = `{{range .}}DomainName: {{.DomainName}}
IP: {{.IP}}
Port: {{.Port}}
Issuer: {{.Issuer}}
NotBefore: {{.NotBefore}}
NotAfter: {{.NotAfter}}
CommonName: {{.CommonName}}
SANs: {{.SANs}}
Error: {{.Error}}
const defaultTempl = `{{range .}}DomainName: {{.DomainName}}
IP: {{.IP}}
Port: {{.Port}}
Issuer: {{.Issuer}}
Validation: {{.ValidationType}}
{{if .Organization}}Organization: {{.Organization}}
{{end}}NotBefore: {{.NotBefore}}
NotAfter: {{.NotAfter}}
CommonName: {{.CommonName}}
SANs: {{.SANs}}
Error: {{.Error}}

{{end}}
`
Expand All @@ -267,9 +352,9 @@ func (certs Certs) String() string {
return b.String()
}

const markdownTempl = `DomainName | IP | Port | Issuer | NotBefore | NotAfter | CN | SANs | Error
--- | --- | --- | --- | --- | --- | --- | --- | ---
{{range .}}{{.DomainName}} | {{.IP}} | {{.Port}} | {{.Issuer}} | {{.NotBefore}} | {{.NotAfter}} | {{.CommonName}} | {{range .SANs}}{{.}}<br/>{{end}} | {{.Error}}
const markdownTempl = `DomainName | IP | Port | Issuer | Validation | Organization | NotBefore | NotAfter | CN | SANs | Error
--- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ---
{{range .}}{{.DomainName}} | {{.IP}} | {{.Port}} | {{.Issuer}} | {{.ValidationType}} | {{.Organization}} | {{.NotBefore}} | {{.NotAfter}} | {{.CommonName}} | {{range .SANs}}{{.}}<br/>{{end}} | {{.Error}}
{{end}}
`

Expand Down
25 changes: 13 additions & 12 deletions cert_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ func ExampleCerts_String() {

fmt.Printf("%s", certs)
// Output:
// DomainName: example.com
// IP: 127.0.0.1
// Port: 443
// Issuer: CA for test
// NotBefore: 2017-01-01 00:00:00 +0000 UTC
// NotAfter: 2018-01-01 00:00:00 +0000 UTC
// CommonName: example.com
// SANs: [example.com www.example.com]
// DomainName: example.com
// IP: 127.0.0.1
// Port: 443
// Issuer: CA for test
// Validation: DV
// NotBefore: 2017-01-01 00:00:00 +0000 UTC
// NotAfter: 2018-01-01 00:00:00 +0000 UTC
// CommonName: example.com
// SANs: [example.com www.example.com]
// Error:
}

Expand All @@ -25,15 +26,15 @@ func ExampleCerts_Markdown() {

fmt.Printf("%s", certs.Markdown())
// Output:
// DomainName | IP | Port | Issuer | NotBefore | NotAfter | CN | SANs | Error
// --- | --- | --- | --- | --- | --- | --- | --- | ---
// example.com | 127.0.0.1 | 443 | CA for test | 2017-01-01 00:00:00 +0000 UTC | 2018-01-01 00:00:00 +0000 UTC | example.com | example.com<br/>www.example.com<br/> |
// DomainName | IP | Port | Issuer | Validation | Organization | NotBefore | NotAfter | CN | SANs | Error
// --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ---
// example.com | 127.0.0.1 | 443 | CA for test | DV | | 2017-01-01 00:00:00 +0000 UTC | 2018-01-01 00:00:00 +0000 UTC | example.com | example.com<br/>www.example.com<br/> |
}

func ExampleCerts_JSON() {
certs, _ := NewCerts([]string{"example.com"})

fmt.Printf("%s", certs.JSON())
// Output:
// [{"domainName":"example.com","ip":"127.0.0.1","port":"443","issuer":"CA for test","commonName":"example.com","sans":["example.com","www.example.com"],"notBefore":"2017-01-01 00:00:00 +0000 UTC","notAfter":"2018-01-01 00:00:00 +0000 UTC","error":""}]
// [{"domainName":"example.com","ip":"127.0.0.1","port":"443","issuer":"CA for test","validationType":"DV","commonName":"example.com","sans":["example.com","www.example.com"],"notBefore":"2017-01-01 00:00:00 +0000 UTC","notAfter":"2018-01-01 00:00:00 +0000 UTC","error":""}]
}
Loading