diff --git a/.gitignore b/.gitignore index 95864b0..4bdb5cd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ certigo -certigo.exe \ No newline at end of file +certigo.exe +man/ +shell/ diff --git a/build b/build index 8d4300f..99dc963 100755 --- a/build +++ b/build @@ -17,3 +17,8 @@ go build -o bin/certigo ${REPO_PATH} # Generate man page mkdir -p man bin/certigo --help-man > man/certigo.1 + +# Generate completion scripts +mkdir -p shell +bin/certigo --completion-script-zsh > shell/certigo.completion.zsh +bin/certigo --completion-script-bash > shell/certigo.completion.bash diff --git a/glide.lock b/glide.lock index 3607967..39047f1 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: f13313281515b6941a7f67c8dc051c6cd76314f3708fe93d1f701278856cfc1a -updated: 2017-08-02T15:41:22.000418583-07:00 +hash: f7ccb11674e8a2d85c7cdc160a5dd7a3160b1280f9443879235148659939ffb5 +updated: 2017-08-07T15:26:47.237893811-07:00 imports: - name: github.com/alecthomas/template version: a0175ee3bccc567396460bf5acd36800cb10c49c @@ -27,6 +27,8 @@ imports: repo: https://github.com/mattn/go-isatty - name: github.com/satori/go.uuid version: 879c5887cd475cd7864858769793b2ceb0d44feb +- name: github.com/spiffe/go-spiffe + version: 2a3d812e4ae000302a242cb42490ff395e9508de - name: golang.org/x/crypto version: 5bcd134fee4dd1475da17714aac19c0aa0142e2f subpackages: diff --git a/glide.yaml b/glide.yaml index 8163221..438cf3a 100644 --- a/glide.yaml +++ b/glide.yaml @@ -12,3 +12,4 @@ import: version: ^1.2.0 - package: github.com/Masterminds/sprig version: ^2.12.0 +- package: github.com/spiffe/go-spiffe diff --git a/lib/display.go b/lib/display.go index e8c2f55..bc2b25b 100644 --- a/lib/display.go +++ b/lib/display.go @@ -58,11 +58,14 @@ Key Usage:{{range .KeyUsage | keyUsage}} Extended Key Usage:{{range .ExtKeyUsage}} {{. | extKeyUsage}}{{end}}{{end}} {{- if .AltDNSNames}} -Alternate DNS Names: +DNS Names: {{wrapWith .Width "\n\t" (join ", " .AltDNSNames)}}{{end}} {{- if .AltIPAddresses}} -Alternate IP Addresses: +IP Addresses: {{wrapWith .Width "\n\t" (join ", " .AltIPAddresses)}}{{end}} +{{- if .URINames}} +URI Names: + {{wrapWith .Width "\n\t" (join ", " .URINames)}}{{end}} {{- if .EmailAddresses}} Email Addresses: {{wrapWith .Width "\n\t" (join ", " .EmailAddresses)}}{{end}} @@ -80,11 +83,14 @@ Issuer: {{.Issuer.Name | printShortName }} Name Constraints{{if .PermittedDNSDomains.Critical}} (critical){{end}}: {{range .NameConstraints.PermittedDNSDomains}} {{.}}{{end}}{{end}} {{- if .AltDNSNames}} -Alternate DNS Names: +DNS Names: {{wrapWith .Width "\n\t" (join ", " .AltDNSNames)}}{{end}} {{- if .AltIPAddresses}} -Alternate IP Addresses: +IP Addresses: {{wrapWith .Width "\n\t" (join ", " .AltIPAddresses)}}{{end}} +{{- if .URINames}} +URI Names: + {{wrapWith .Width "\n\t" (join ", " .URINames)}}{{end}} {{- if .EmailAddresses}} Email Addresses: {{wrapWith .Width "\n\t" (join ", " .EmailAddresses)}}{{end}} diff --git a/lib/encoder.go b/lib/encoder.go index 8f78ccc..a14b728 100644 --- a/lib/encoder.go +++ b/lib/encoder.go @@ -32,6 +32,8 @@ import ( "strconv" "strings" "time" + + "github.com/spiffe/go-spiffe" ) var keyUsages = []x509.KeyUsage{ @@ -189,6 +191,7 @@ type simpleCertificate struct { ExtKeyUsage []simpleExtKeyUsage `json:"extended_key_usage,omitempty"` AltDNSNames []string `json:"dns_names,omitempty"` AltIPAddresses []net.IP `json:"ip_addresses,omitempty"` + URINames []string `json:"uri_names,omitempty"` EmailAddresses []string `json:"email_addresses,omitempty"` Warnings []string `json:"warnings,omitempty"` PEM string `json:"pem,omitempty"` @@ -231,6 +234,11 @@ func createSimpleCertificate(name string, cert *x509.Certificate) simpleCertific PEM: string(pem.EncodeToMemory(EncodeX509ToPEM(cert, nil))), } + uriNames, err := spiffe.GetURINamesFromCertificate(cert) + if err != nil { + out.URINames = uriNames + } + if cert.BasicConstraintsValid { out.BasicConstraints = &basicConstraints{ IsCA: cert.IsCA, diff --git a/tests/dump-cert-chain-to-text.t b/tests/dump-cert-chain-to-text.t index e6bc497..0bf7409 100644 --- a/tests/dump-cert-chain-to-text.t +++ b/tests/dump-cert-chain-to-text.t @@ -137,7 +137,7 @@ Dump a live cert chain (squareup-chain.crt) Extended Key Usage: \tServer Auth (esc) \tClient Auth (esc) - Alternate DNS Names: + DNS Names: \twww.squareup.com, squareup.com, (esc) \taccount.squareup.com, mkt.com, www.mkt.com, (esc) \tmarket.squareup.com, gosq.com, www.gosq.com, (esc) diff --git a/tests/dump-leaf-to-not-verbose.t b/tests/dump-leaf-to-not-verbose.t index a738753..3ee774a 100644 --- a/tests/dump-leaf-to-not-verbose.t +++ b/tests/dump-leaf-to-not-verbose.t @@ -31,8 +31,8 @@ Dump an example certificate (example-leaf.crt) Valid: 2016-06-10 22:14 UTC to 2023-04-15 22:14 UTC Subject: CN=example-leaf Issuer: CN=example-leaf - Alternate DNS Names: + DNS Names: \tlocalhost (esc) - Alternate IP Addresses: + IP Addresses: \t127.0.0.1, ::1 (esc) diff --git a/tests/dump-leaf-to-text.t b/tests/dump-leaf-to-text.t index 071a7a0..1324280 100644 --- a/tests/dump-leaf-to-text.t +++ b/tests/dump-leaf-to-text.t @@ -46,8 +46,8 @@ Dump an example certificate (example-leaf.crt) Extended Key Usage: \tClient Auth (esc) \tServer Auth (esc) - Alternate DNS Names: + DNS Names: \tlocalhost (esc) - Alternate IP Addresses: + IP Addresses: \t127.0.0.1, ::1 (esc) diff --git a/vendor/github.com/spiffe/go-spiffe/LICENSE b/vendor/github.com/spiffe/go-spiffe/LICENSE new file mode 100644 index 0000000..28d2f2a --- /dev/null +++ b/vendor/github.com/spiffe/go-spiffe/LICENSE @@ -0,0 +1,13 @@ +Copyright 2017 Scytale, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/github.com/spiffe/go-spiffe/Makefile b/vendor/github.com/spiffe/go-spiffe/Makefile new file mode 100644 index 0000000..e4a4344 --- /dev/null +++ b/vendor/github.com/spiffe/go-spiffe/Makefile @@ -0,0 +1,5 @@ +test: vet + go test + +vet: + go vet diff --git a/vendor/github.com/spiffe/go-spiffe/README.md b/vendor/github.com/spiffe/go-spiffe/README.md new file mode 100644 index 0000000..bcfcc44 --- /dev/null +++ b/vendor/github.com/spiffe/go-spiffe/README.md @@ -0,0 +1,11 @@ +# go-spiffe library + +### Overview +The go-spiffe library provides functionality to parse and verify SPIFFE identities encoded in X.509 certificates as described in the [SPIFFE Verifiable Identity Document (SVID) Document Specification](https://github.com/spiffe/svid/blob/master/SPECIFICATION.md). + +#### func GetUrisInSubjectAltName + +```go +func GetUrisInSubjectAltName(certificateString string) (uris []string, err error) +``` +Parses an X.509 certificate in PEM format and gets the URIs from the Subject Alternative Name extension. \ No newline at end of file diff --git a/vendor/github.com/spiffe/go-spiffe/spiffe.go b/vendor/github.com/spiffe/go-spiffe/spiffe.go new file mode 100644 index 0000000..e6ff4dd --- /dev/null +++ b/vendor/github.com/spiffe/go-spiffe/spiffe.go @@ -0,0 +1,95 @@ +package spiffe + +import ( + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/pem" + "errors" +) + +var oidExtensionSubjectAltName = asn1.ObjectIdentifier{2, 5, 29, 17} + +func getURINamesFromSANExtension(sanExtension []byte) (uris []string, err error) { + // RFC 5280, 4.2.1.6 + + // SubjectAltName ::= GeneralNames + // + // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + // + // GeneralName ::= CHOICE { + // otherName [0] OtherName, + // rfc822Name [1] IA5String, + // dNSName [2] IA5String, + // x400Address [3] ORAddress, + // directoryName [4] Name, + // ediPartyName [5] EDIPartyName, + // uniformResourceIdentifier [6] IA5String, + // iPAddress [7] OCTET STRING, + // registeredID [8] OBJECT IDENTIFIER } + var seq asn1.RawValue + var rest []byte + if rest, err = asn1.Unmarshal(sanExtension, &seq); err != nil { + return + } else if len(rest) != 0 { + err = errors.New("x509: trailing data after X.509 extension") + return + } + if !seq.IsCompound || seq.Tag != 16 || seq.Class != 0 { + err = asn1.StructuralError{Msg: "bad SAN sequence"} + return + } + + rest = seq.Bytes + for len(rest) > 0 { + var v asn1.RawValue + rest, err = asn1.Unmarshal(rest, &v) + if err != nil { + return + } + if v.Tag == 6 { + uris = append(uris, string(v.Bytes)) + } + } + + return +} + +func getExtensionsFromAsn1ObjectIdentifier(certificate *x509.Certificate, id asn1.ObjectIdentifier) []pkix.Extension { + var extensions []pkix.Extension + + for _, extension := range certificate.Extensions { + if extension.Id.Equal(id) { + extensions = append(extensions, extension) + } + } + + return extensions +} + +// GetUrisInSubjectAltName takes a parsed X.509 certificate and gets the URIs from the SAN extension. +func GetURINamesFromCertificate(cert *x509.Certificate) (uris []string, err error) { + for _, ext := range getExtensionsFromAsn1ObjectIdentifier(cert, oidExtensionSubjectAltName) { + uris, err = getURINamesFromSANExtension(ext.Value) + if err != nil { + return + } + } + + return uris, nil +} + +// GetUrisInSubjectAltNameEncoded parses a PEM-encoded X.509 certificate and gets the URIs from the SAN extension. +func GetURINamesFromPEM(encodedCertificate string) (uris []string, err error) { + block, _ := pem.Decode([]byte(encodedCertificate)) + if block == nil { + return uris, errors.New("failed to decode certificate PEM") + } + + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return uris, errors.New("failed to parse certificate: " + err.Error()) + } + + return GetURINamesFromCertificate(cert) +} diff --git a/vendor/github.com/spiffe/go-spiffe/spiffe_test.go b/vendor/github.com/spiffe/go-spiffe/spiffe_test.go new file mode 100644 index 0000000..cc4b39c --- /dev/null +++ b/vendor/github.com/spiffe/go-spiffe/spiffe_test.go @@ -0,0 +1,44 @@ +package spiffe + +import ( + "io/ioutil" + "testing" +) + +func getCertificateFromFile(t *testing.T, certFilePath string) string { + certificateString, err := ioutil.ReadFile(certFilePath) + if err != nil { + t.Fatal(err) + } + + return string(certificateString) +} + +func TestGetURINamesFromPEM(t *testing.T) { + certPEM := getCertificateFromFile(t, "testdata/leaf.cert.pem") + + var golden = "spiffe://dev.acme.com/path/service" + + uris, err := GetURINamesFromPEM(string(certPEM)) + if err != nil { + t.Error(err) + } + + if len(uris) == 1 { + if uris[0] != golden { + t.Fatalf("Expected '%v' but got '%v'", golden, uris[0]) + } + } else { + t.Fatalf("Expected 1 URI but got '%v'", len(uris)) + } + + certPEM = getCertificateFromFile(t, "testdata/intermediate.cert.pem") + uris, err = GetURINamesFromPEM(string(certPEM)) + if err == nil { + t.Fatal("Expected to fail") + } + + if len(uris) > 0 { + t.Fatalf("Expected to have no URIs but got %v URIs", len(uris)) + } +} diff --git a/vendor/github.com/spiffe/go-spiffe/testdata/intermediate.cert.pem b/vendor/github.com/spiffe/go-spiffe/testdata/intermediate.cert.pem new file mode 100644 index 0000000..6338a1f --- /dev/null +++ b/vendor/github.com/spiffe/go-spiffe/testdata/intermediate.cert.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFiDCCA3CgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwNzELMAkGA1UEBhMCVVMx +FzAVBgNVBAoMDnRlc3QxLmFjbWUuY29tMQ8wDQYDVQQDDAZSb290Q0EwHhcNMTcw +NzE5MTY1MDIwWhcNMTcxMDI3MTY1MDIwWjA/MQswCQYDVQQGEwJVUzEXMBUGA1UE +CgwOdGVzdDEuYWNtZS5jb20xFzAVBgNVBAMMDkludGVybWVkaWFldENBMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAph7QhbUKEjMWu2R/WXIc8RR0ymCL +njJTm0D5duTe7V2hklLhCo1KnZAjvDiDX9r85UfEja5MYItHmFF4HOZjSG6nY3Yg +Mm5hdJM7Jmv9NJR8DJInROabfRcaPdugs2UQ41jCEygIoWiZ9+yVlZ21MNW0yQdI +JHUNndQfXpS7dBdKw6fUqzZgpdzo86mAapZDIPL7gXv6MW8JhbvQCm+bg7SRIJOD +t/a1T0nHPNuwxdDWjGcmJEQRknffL8pheDlW9sMAd/4BtIeaWUEb3JjdDoaJ9SWU +tCgGRZOMnmry8npJnssyoLUtIPuk8949REOUB15nT94EhTtb1BMdiuD8P8HOHWC6 +mcxpJCsKlCFmOQpES6WROqjckQJ0f/xPOdKAdI9W0Eg3TRtibV9XTfTvv8SPug9/ +6FnkdpwF5xlJ/XcuW8GtpHUZNQ0NyxjUh2rQRAbwdTMeCvhx5fPHpT2kc6PIlzLw +h4Pt0Xvc1cNt2iJOVDqs75HvvUe4RYTfdqlK5385u/s2cxQrLuB4owJrTyrqc3yM +L/0h5JXr9P+T+axrd5WQWz2ngiaimli2vxZTR++RfBnyCgQGTiY+9UUOYYgHh6hJ +CGUPY77DsKuTfIXYlra+c65FKFAcZrC4vt1CLtBqW2mBy7U868c0L9PZ1g3+WGvA +FmnZA6MgUKQE0i0CAwEAAaOBlTCBkjAdBgNVHQ4EFgQUNd238edKw2Tt8tgmXRz4 +JXhVBKAwHwYDVR0jBBgwFoAUUeBRd0yew4JtInksRTlbz71YwtEwDwYDVR0TAQH/ +BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwLwYDVR0eAQH/BCUwI6AhMA+GDS5kZXYu +YWNtZS5jb20wDoYMZGV2LmFjbWUuY29tMA0GCSqGSIb3DQEBCwUAA4ICAQBLljbI +ekm5/uhFbc/aCAlomwGkXFvyMXx7eD7Pimzn4H31nYvQ5ha2M0536JC/mH1xi7nK +OOMuSBAqjpALoRk4+3O5s6r9BN8KKhcI4jDKHqOSBe38K+Ad06B52yxYyL6YmJwk +Zlazf3KvExzUWS0t4ehNuI2HJvvwEitMpOF3hhwAYk3v/x2YBtpglH+yZC4Sfoma +ItjFWeD+DA0bVAMJiErz8Pq91avnC8SPpPXiPJVOaBhaNc8po6x6cwSqY6RnI2/K +0kUWXXH65Pz0JYru53ALzy4ouwJItcgMvzZPGVaojxEbGDrjXEkWIGresFrw1Ijr +IyrQUqWa9wW6ik+IQOb6m3FZYB8jMO7nhop9Ywm9uoSmFRg6RsP2AWHBMYP7lZqw +fx3HjrAvc3ycIZ2tSPkdLd5n8YuL31CVpa9aGwT5dYzIVd/eFUrCUIGV2j8XHgkL +gMPr8JhNPAG0B4rRpEXmK3HHybo3s0M8i80Lit98xzupNpng97xKT7jJBSinNTdt +/2uU7diHQM7aPNmNG+Wu3GXVt/MZuDMSFrh9AHl0A/Y4mNu6KKqRMJuvsfy/j1DD +fDGmAkjHrspiorTruphHk+cymJfjAGqZ0l6il4Wi5w4R5jrxnJMhkxbjr/PG5xCS ++ZKPPNZbpPIY54oQ2bHkuaQgzJ7us4ZW7gxgbw== +-----END CERTIFICATE----- diff --git a/vendor/github.com/spiffe/go-spiffe/testdata/leaf.cert.pem b/vendor/github.com/spiffe/go-spiffe/testdata/leaf.cert.pem new file mode 100644 index 0000000..cd25490 --- /dev/null +++ b/vendor/github.com/spiffe/go-spiffe/testdata/leaf.cert.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIE1DCCArygAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzELMAkGA1UEBhMCVVMx +FzAVBgNVBAoMDnRlc3QxLmFjbWUuY29tMRcwFQYDVQQDDA5JbnRlcm1lZGlhZXRD +QTAeFw0xNzA3MTkxNjUwMjBaFw0xNzA3MjkxNjUwMjBaMDUxCzAJBgNVBAYTAlVT +MRcwFQYDVQQKDA50ZXN0MS5hY21lLmNvbTENMAsGA1UEAwwEYmxvZzCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKm8P47lABp4+rz2nN+QYrxedbaFVWoF +FuoSkqcHsafMwbMrN+kI6wJVtlbwviDvxWFJ92q0H71QNFybTsmof3KUN/kYCp7P ++LKhBrN0ttWI5q6v5eDrjN0VdtVdnlZOYmJFbvETOgfK/qXKNRRM8HYW0tdqrtEw +CR5dIu53xVUSViBdwXpuy2c5W2mFn1gxTpdW+3hbZsL1pHrU9qPWLtTgl/KY8kjs +I7KW1cIcinE4SJomhB5L/4emhxKGY+kEa2+fN9IPjjvKSMOw9kiBKk1GHZcIY5EA +O3TIfUk3fysPzi5qA0su/bNtPQy1uXgXS10xUlV7pqRPvHjiNzgFkXUCAwEAAaOB +4zCB4DAJBgNVHRMEAjAAMB0GA1UdDgQWBBRVQ91jSOONzVr1VGBdJOlPN+3XxTBg +BgNVHSMEWTBXgBQ13bfx50rDZO3y2CZdHPgleFUEoKE7pDkwNzELMAkGA1UEBhMC +VVMxFzAVBgNVBAoMDnRlc3QxLmFjbWUuY29tMQ8wDQYDVQQDDAZSb290Q0GCAhAA +MA4GA1UdDwEB/wQEAwIDqDATBgNVHSUEDDAKBggrBgEFBQcDATAtBgNVHREEJjAk +hiJzcGlmZmU6Ly9kZXYuYWNtZS5jb20vcGF0aC9zZXJ2aWNlMA0GCSqGSIb3DQEB +CwUAA4ICAQBp2+rtUxt1VmNM/vi6PwoSoYzWFmQ2nc4OM7bsOG4uppU54wRYZ+T7 +c42EcrpyBgWn+rWHT1Hi6SNcmloKHydaUTZ4pq3IlKKnBNqwivU5BzIxYLDrhR/U +wd9s1tgmLvADqkQa1XjjSFn5Auoj1R640ry4qpw8IOusdm6wVhru4ssRnHX4E2uR +jQe7b3ws38aZhjtL78Ip0BB4yPxWJRp/WmEoT33QP+cZhA4IYWECxNODr6DSJeq2 +VNu/6JACGrNfM2Sjt4Wxz+nIa3cKDNCA6PR8StTUTcoQ6ZBzpn+n/Q1xSRIOJz6N +hgfkyb9O7HAMdAP+TxehjqG3gh5Ky2DgYMCIZOztVzsuOb1DGJe/kGUKeRJLl2/O +QwkctwUOcVIxckNu6OvclriFzvoXObqO77XeCI2V1Vef0wGTWlWNOdbFa4708Y7f +5UdwInYQUi87RFDnc1SDU4Jrsv4KzZiv9FCfDg8pCBIdWpWT7DAuI0d7i7PZ+iFt +ZZ6sb/YDkyiDXU4ar/dja0FDE2r7jsN9D+FfW49+iDvXr4ELQyhZpW3Zr1Ojwm58 +CJzjZwbRYiVwPBRsKmiYfO1E7esvw3CmjK5chfz8c40f6/APDro9ZmYNBRv2CnJy +t/DtcM/GpAhBbLP9Tk7kPB41v5fRIxVDo50Iz/qvkr37pQ4RsejSFg== +-----END CERTIFICATE-----