├── CODEOWNERS ├── .gitignore ├── test-certs ├── example-leaf.p12 ├── example-md5.jceks ├── example-md5.p12 ├── example-root.p12 ├── example-sha1.p12 ├── example-expired.p12 ├── example-leaf.jceks ├── example-root.jceks ├── example-sha1.jceks ├── example-bad-serial.p12 ├── example-custom-oid.p12 ├── example-expired.jceks ├── example-small-key.p12 ├── example-bad-serial.jceks ├── example-custom-oid.jceks ├── example-root-bad-ku.p12 ├── example-small-key.jceks ├── example-elliptic-sha1.jceks ├── example-elliptic-sha1.p12 ├── example-root-bad-ku.jceks ├── example-name-constraints.p12 ├── example-name-constraints.jceks ├── example-custom-oid.conf ├── README.md ├── example-elliptic-sha1.key ├── openssl.ext ├── example-small-key.csr ├── example-elliptic-sha1.csr ├── example-small-key.crt ├── example-small-key.p7b ├── example-small-key.key ├── example-custom-oid.csr ├── example-elliptic-sha1.crt ├── example-md5.csr ├── example-leaf.csr ├── example-root.csr ├── example-sha1.csr ├── example-expired.csr ├── example-bad-serial.csr ├── example-root-bad-ku.csr ├── example-elliptic-sha1.p7b ├── example-name-constraints.csr ├── example-expired.crt ├── example-custom-oid.crt ├── example-root.crt ├── example-expired.p7b ├── example-root-bad-ku.crt ├── example-custom-oid.p7b ├── example-md5.crt ├── example-leaf.crt ├── example-root.p7b ├── example-sha1.crt ├── example-bad-serial.crt ├── example-root-bad-ku.p7b ├── example-leaf.p7b ├── example-md5.p7b ├── example-sha1.p7b ├── example-bad-serial.p7b ├── example-name-constraints.crt ├── example-name-constraints.p7b ├── example-custom-oid.key └── Makefile ├── jceks ├── testdata │ ├── private-key.jceks │ ├── trusted-cert.jceks │ ├── encoder-cert-store.jceks │ ├── encoder-private-store.jceks │ ├── encoder-re-encode-private-key.jceks │ ├── encoder-re-encode-trusted-cert.jceks │ ├── private-key-ca.crt │ ├── trusted-cert.crt │ ├── private-key.crt │ ├── private-key.key │ └── generate-jceks.sh ├── README.md ├── pkcs5.go ├── utils_test.go ├── pkcs5_test.go ├── jceks_test.go ├── jceks.go ├── decoder_test.go ├── encoding.go └── modutf8.go ├── .github ├── dependabot.yml └── workflows │ ├── lint.yaml │ ├── compilecheck.yaml │ ├── release.yaml │ └── test.yaml ├── starttls ├── psql │ ├── oid │ │ ├── doc.go │ │ ├── gen.go │ │ └── types.go │ ├── ssl_renegotiation.go │ ├── README.md │ ├── ssl_windows.go │ ├── ssl_permissions.go │ ├── ssl_go1.7.go │ ├── user_posix.go │ ├── uuid.go │ ├── user_windows.go │ ├── LICENSE.md │ ├── url.go │ ├── buf.go │ ├── conn_go18.go │ └── ssl.go ├── mysql │ ├── README.md │ ├── result.go │ ├── transaction.go │ ├── AUTHORS │ ├── rows.go │ ├── const.go │ ├── buffer.go │ ├── statement.go │ ├── errors.go │ └── infile.go ├── imap.go ├── ftp.go ├── dialer.go └── ciphersuites.go ├── internal └── gen-known-logs │ ├── README.md │ └── main.go ├── CONTRIBUTING.md ├── main.go ├── .golangci.yaml ├── tests ├── dump-csr-to-text.t ├── dump-small-key-to-text.t ├── dump-leaf-to-not-verbose.t ├── dump-leaf-to-text.t ├── dump-small-key-to-json.t ├── dump-spiffe-cert-to-text.t ├── dump-name-constraints-to-text.t ├── dump-leaf-to-json.t └── dump-spiffe-cert-to-json.t ├── cli └── terminal │ ├── testing.go │ └── terminal.go ├── pkcs7 ├── pkcs7_test.go └── pkcs7.go ├── go.mod └── lib ├── oids.go ├── ct.go └── ocsp.go /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @square/idinfra-staff 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | certigo 2 | certigo.exe 3 | /gen-known-logs 4 | man/ 5 | shell/ 6 | -------------------------------------------------------------------------------- /test-certs/example-leaf.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-leaf.p12 -------------------------------------------------------------------------------- /test-certs/example-md5.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-md5.jceks -------------------------------------------------------------------------------- /test-certs/example-md5.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-md5.p12 -------------------------------------------------------------------------------- /test-certs/example-root.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-root.p12 -------------------------------------------------------------------------------- /test-certs/example-sha1.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-sha1.p12 -------------------------------------------------------------------------------- /test-certs/example-expired.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-expired.p12 -------------------------------------------------------------------------------- /test-certs/example-leaf.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-leaf.jceks -------------------------------------------------------------------------------- /test-certs/example-root.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-root.jceks -------------------------------------------------------------------------------- /test-certs/example-sha1.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-sha1.jceks -------------------------------------------------------------------------------- /jceks/testdata/private-key.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/jceks/testdata/private-key.jceks -------------------------------------------------------------------------------- /jceks/testdata/trusted-cert.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/jceks/testdata/trusted-cert.jceks -------------------------------------------------------------------------------- /test-certs/example-bad-serial.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-bad-serial.p12 -------------------------------------------------------------------------------- /test-certs/example-custom-oid.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-custom-oid.p12 -------------------------------------------------------------------------------- /test-certs/example-expired.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-expired.jceks -------------------------------------------------------------------------------- /test-certs/example-small-key.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-small-key.p12 -------------------------------------------------------------------------------- /test-certs/example-bad-serial.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-bad-serial.jceks -------------------------------------------------------------------------------- /test-certs/example-custom-oid.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-custom-oid.jceks -------------------------------------------------------------------------------- /test-certs/example-root-bad-ku.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-root-bad-ku.p12 -------------------------------------------------------------------------------- /test-certs/example-small-key.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-small-key.jceks -------------------------------------------------------------------------------- /test-certs/example-elliptic-sha1.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-elliptic-sha1.jceks -------------------------------------------------------------------------------- /test-certs/example-elliptic-sha1.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-elliptic-sha1.p12 -------------------------------------------------------------------------------- /test-certs/example-root-bad-ku.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-root-bad-ku.jceks -------------------------------------------------------------------------------- /jceks/testdata/encoder-cert-store.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/jceks/testdata/encoder-cert-store.jceks -------------------------------------------------------------------------------- /test-certs/example-name-constraints.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-name-constraints.p12 -------------------------------------------------------------------------------- /jceks/testdata/encoder-private-store.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/jceks/testdata/encoder-private-store.jceks -------------------------------------------------------------------------------- /test-certs/example-name-constraints.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/test-certs/example-name-constraints.jceks -------------------------------------------------------------------------------- /jceks/testdata/encoder-re-encode-private-key.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/jceks/testdata/encoder-re-encode-private-key.jceks -------------------------------------------------------------------------------- /jceks/testdata/encoder-re-encode-trusted-cert.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/master/jceks/testdata/encoder-re-encode-trusted-cert.jceks -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /starttls/psql/oid/doc.go: -------------------------------------------------------------------------------- 1 | // Package oid contains OID constants 2 | // as defined by the Postgres server. 3 | package oid 4 | 5 | // Oid is a Postgres Object ID. 6 | type Oid uint32 7 | -------------------------------------------------------------------------------- /starttls/psql/ssl_renegotiation.go: -------------------------------------------------------------------------------- 1 | // +build !go1.7 2 | 3 | package pq 4 | 5 | import "crypto/tls" 6 | 7 | // Renegotiation is not supported by crypto/tls until Go 1.7. 8 | func sslRenegotiation(*tls.Config) {} 9 | -------------------------------------------------------------------------------- /starttls/psql/README.md: -------------------------------------------------------------------------------- 1 | This is a forked version of [lib/pq](https://github.com/lib/pq), modified to 2 | allow access to the underlying TLS connection context so that Certigo can dump 3 | TLS certificates from PostgreSQL database servers. 4 | -------------------------------------------------------------------------------- /test-certs/example-custom-oid.conf: -------------------------------------------------------------------------------- 1 | oid_section = OIDs 2 | 3 | [ req ] 4 | distinguished_name = dn 5 | prompt = no 6 | 7 | [ OIDs ] 8 | CustomTestOID=1.3.6.1.4.1.36914 9 | 10 | [ dn ] 11 | CN = example-custom-oids 12 | CustomTestOID = example 13 | -------------------------------------------------------------------------------- /starttls/mysql/README.md: -------------------------------------------------------------------------------- 1 | This is a forked version of [go-sql-driver/mysql](https://github.com/go-sql-driver/mysql), modified to 2 | allow access to the underlying TLS connection context so that Certigo can dump TLS certificates from 3 | MySQL database servers. 4 | -------------------------------------------------------------------------------- /starttls/psql/ssl_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package pq 4 | 5 | import "os" 6 | 7 | // sslCertificatePermissions checks the permissions on user-supplied certificate 8 | // files. In libpq, this is a no-op on Windows. 9 | func sslCertificatePermissions(cert, key os.FileInfo) {} 10 | -------------------------------------------------------------------------------- /test-certs/README.md: -------------------------------------------------------------------------------- 1 | Test certs 2 | ========== 3 | 4 | The certificates and keys contained in this directory have been generated for 5 | test/development purposes only. Do not use these files in production 6 | deployments! You can regenerate them anytime by running `make all`. The 7 | password for the PKCS12 and JCEKS keystores is always "password". 8 | -------------------------------------------------------------------------------- /jceks/README.md: -------------------------------------------------------------------------------- 1 | # JCEKS 2 | 3 | Package jceks reads and writes JCEKS (Java Cryptogaphy Extension Key Store) files containing private keys and certificates. 4 | This module implements only a fraction of the JCEKS cryptographic protocols. 5 | In particular, it implements the SHA1 signature verification of the keystore and the PBEWithMD5AndDES3CBC cipher for encrypting private keys. 6 | -------------------------------------------------------------------------------- /internal/gen-known-logs/README.md: -------------------------------------------------------------------------------- 1 | # gen-known-logs 2 | 3 | A tool for generating the list of CT logs that certigo uses for printing operator information and log URLs for SCTs. 4 | 5 | Logs are fetched parsed from https://www.gstatic.com/ct/log_list/v2/all_logs_list.json, documented [here](https://github.com/google/certificate-transparency-community-site/blob/master/docs/google/known-logs.md). 6 | 7 | ## Usage 8 | 9 | Run `go generate ./...` to regenerate known logs. -------------------------------------------------------------------------------- /starttls/psql/ssl_permissions.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package pq 4 | 5 | import "os" 6 | 7 | // sslCertificatePermissions checks the permissions on user-supplied certificate 8 | // files. The key file should have very little access. 9 | // 10 | // libpq does not check key file permissions on Windows. 11 | func sslCertificatePermissions(cert, key os.FileInfo) { 12 | kmode := key.Mode() 13 | if kmode != kmode&0600 { 14 | panic(ErrSSLKeyHasWorldPermissions) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /starttls/psql/ssl_go1.7.go: -------------------------------------------------------------------------------- 1 | // +build go1.7 2 | 3 | package pq 4 | 5 | import "crypto/tls" 6 | 7 | // Accept renegotiation requests initiated by the backend. 8 | // 9 | // Renegotiation was deprecated then removed from PostgreSQL 9.5, but 10 | // the default configuration of older versions has it enabled. Redshift 11 | // also initiates renegotiations and cannot be reconfigured. 12 | func sslRenegotiation(conf *tls.Config) { 13 | conf.Renegotiation = tls.RenegotiateFreelyAsClient 14 | } 15 | -------------------------------------------------------------------------------- /test-certs/example-elliptic-sha1.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PARAMETERS----- 2 | BgUrgQQAIw== 3 | -----END EC PARAMETERS----- 4 | -----BEGIN EC PRIVATE KEY----- 5 | MIHcAgEBBEIBc4MT0rgcDEaimeP7Bxlhp3hAI2/KySFbOu6cg40taVXhXnaDAPqV 6 | tKj3kru+1DhvxBCrUewzKo5u9g+OC7xdu8qgBwYFK4EEACOhgYkDgYYABAEQWNjT 7 | 8/MemDSwPVJD6T0qDTPibCzuoIsk7iMEctY9jzfeG5j0WXL33xTB6NIwW6H/7hBG 8 | syZtFgKI5nKqhA+nGgDLvkHGKz3boBwhztZb+YsGebCcrMEPKsoGq6DFgYO0Gl4w 9 | LNaeDp3Lod+wsUY+9JrHtGBD7fp3gQ+QTPvGQ3IRVw== 10 | -----END EC PRIVATE KEY----- 11 | -------------------------------------------------------------------------------- /starttls/psql/user_posix.go: -------------------------------------------------------------------------------- 1 | // Package pq is a pure Go Postgres driver for the database/sql package. 2 | 3 | // +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris rumprun 4 | 5 | package pq 6 | 7 | import ( 8 | "os" 9 | "os/user" 10 | ) 11 | 12 | func userCurrent() (string, error) { 13 | u, err := user.Current() 14 | if err == nil { 15 | return u.Username, nil 16 | } 17 | 18 | name := os.Getenv("USER") 19 | if name != "" { 20 | return name, nil 21 | } 22 | 23 | return "", ErrCouldNotDetectUsername 24 | } 25 | -------------------------------------------------------------------------------- /test-certs/openssl.ext: -------------------------------------------------------------------------------- 1 | [root] 2 | keyUsage=critical, keyCertSign 3 | basicConstraints=critical, CA:TRUE, pathlen:0 4 | [leaf] 5 | extendedKeyUsage = clientAuth, serverAuth 6 | subjectAltName = IP:127.0.0.1,IP:::1,DNS:localhost 7 | [root_bad] 8 | keyUsage=critical, cRLSign 9 | basicConstraints=critical, CA:TRUE, pathlen:0 10 | [root_with_constraints] 11 | keyUsage=critical, keyCertSign 12 | basicConstraints=critical, CA:TRUE, pathlen:0 13 | nameConstraints=permitted;DNS:.example.com,permitted;IP:192.168.0.0/255.255.0.0,permitted;email:.example.com,excluded;DNS:.example.org,excluded;IP:10.10.0.0/255.255.0.0,excluded;email:.example.org 14 | -------------------------------------------------------------------------------- /starttls/psql/uuid.go: -------------------------------------------------------------------------------- 1 | package pq 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | ) 7 | 8 | // decodeUUIDBinary interprets the binary format of a uuid, returning it in text format. 9 | func decodeUUIDBinary(src []byte) ([]byte, error) { 10 | if len(src) != 16 { 11 | return nil, fmt.Errorf("pq: unable to decode uuid; bad length: %d", len(src)) 12 | } 13 | 14 | dst := make([]byte, 36) 15 | dst[8], dst[13], dst[18], dst[23] = '-', '-', '-', '-' 16 | hex.Encode(dst[0:], src[0:4]) 17 | hex.Encode(dst[9:], src[4:6]) 18 | hex.Encode(dst[14:], src[6:8]) 19 | hex.Encode(dst[19:], src[8:10]) 20 | hex.Encode(dst[24:], src[10:16]) 21 | 22 | return dst, nil 23 | } 24 | -------------------------------------------------------------------------------- /test-certs/example-small-key.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIBmjCCAQMCAQAwWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQK 3 | EwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRowGAYDVQQDExFleGFtcGxlLXNt 4 | YWxsLWtleTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAlzyCIeP1T87k1rHV 5 | MtbaGXIWpK/VQuvuXwig+e3ct1ajA4bw0BAInXZ37FEGGSCUix0k/CjH2NltGREt 6 | wbahE0k5oTkVbA5XS4xkNs0M0poAFN5OiFKEAqZ014hqhvKnEUQ2oTe9SVORWw49 7 | mLNg36AIEE2Fu2KQb/VT90cwwD0CAwEAAaAAMA0GCSqGSIb3DQEBBQUAA4GBAIC/ 8 | L8gtyUkyoiZMjlYbnRD14IOWoQsViDOWEeGv/SXC/9D6JOaSCXyi4sweL0/gKKSr 9 | imikLHDKessAEklXUPGpPt/TAaUN29ocAnJ07YeAK4rUuShkjQa34sKz9P+wFIFm 10 | T3/IL/0zhT5SKKzbL9GzCIq4Oqxbe9Mi9xKtNRnU 11 | -----END CERTIFICATE REQUEST----- 12 | -------------------------------------------------------------------------------- /starttls/mysql/result.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | type mysqlResult struct { 12 | affectedRows int64 13 | insertId int64 14 | } 15 | 16 | func (res *mysqlResult) LastInsertId() (int64, error) { 17 | return res.insertId, nil 18 | } 19 | 20 | func (res *mysqlResult) RowsAffected() (int64, error) { 21 | return res.affectedRows, nil 22 | } 23 | -------------------------------------------------------------------------------- /test-certs/example-elliptic-sha1.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIBoTCCAQMCAQAwXjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQK 3 | EwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMR4wHAYDVQQDExVleGFtcGxlLWVs 4 | bGlwdGljLXNoYTEwgZswEAYHKoZIzj0CAQYFK4EEACMDgYYABAEQWNjT8/MemDSw 5 | PVJD6T0qDTPibCzuoIsk7iMEctY9jzfeG5j0WXL33xTB6NIwW6H/7hBGsyZtFgKI 6 | 5nKqhA+nGgDLvkHGKz3boBwhztZb+YsGebCcrMEPKsoGq6DFgYO0Gl4wLNaeDp3L 7 | od+wsUY+9JrHtGBD7fp3gQ+QTPvGQ3IRV6AAMAkGByqGSM49BAEDgYwAMIGIAkIA 8 | 57b3rNqca+NzuU1bX/vWV5bdFFN11YTmwjoWp8MHVd5AgZlVjYvVPVY531K6BS0c 9 | HRB//5mAe99/8IfH7jsKPjcCQgFyEFjtXJmUkG3hieCT7rjYloFweBjkZbp9u5S5 10 | J06NKwXNJO2kyhZYWBPQ/sTx+p6cPP1gk3GxTWIGUlg/W9siZg== 11 | -----END CERTIFICATE REQUEST----- 12 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | We appreciate your desire to contribute code to this repo. You may do so 5 | through GitHub by forking the repository and sending a pull request. 6 | 7 | When submitting code, please make every effort to follow existing conventions 8 | and style in order to keep the code as readable as possible. Please also make 9 | sure all tests pass by running `go test ./...`, and format your code with `go fmt`. 10 | We also recommend using `golint`. 11 | 12 | Before your code can be accepted into the project you must also sign the 13 | Individual Contributor License Agreement. We use [cla-assistant.io][1] and you 14 | will be prompted to sign once a pull request is opened. 15 | 16 | [1]: https://cla-assistant.io/ 17 | -------------------------------------------------------------------------------- /starttls/mysql/transaction.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | type mysqlTx struct { 12 | mc *mysqlConn 13 | } 14 | 15 | func (tx *mysqlTx) Commit() (err error) { 16 | if tx.mc == nil || tx.mc.netConn == nil { 17 | return ErrInvalidConn 18 | } 19 | err = tx.mc.exec("COMMIT") 20 | tx.mc = nil 21 | return 22 | } 23 | 24 | func (tx *mysqlTx) Rollback() (err error) { 25 | if tx.mc == nil || tx.mc.netConn == nil { 26 | return ErrInvalidConn 27 | } 28 | err = tx.mc.exec("ROLLBACK") 29 | tx.mc = nil 30 | return 31 | } 32 | -------------------------------------------------------------------------------- /test-certs/example-small-key.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICKzCCAZQCCQDHlr/u+lfb8jANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJV 3 | UzELMAkGA1UECBMCQ0ExEDAOBgNVBAoTB2NlcnRpZ28xEDAOBgNVBAsTB2V4YW1w 4 | bGUxGjAYBgNVBAMTEWV4YW1wbGUtc21hbGwta2V5MB4XDTE2MDYxMDIyMTQxMloX 5 | DTIzMDQxNTIyMTQxMlowWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYD 6 | VQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRowGAYDVQQDExFleGFtcGxl 7 | LXNtYWxsLWtleTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAlzyCIeP1T87k 8 | 1rHVMtbaGXIWpK/VQuvuXwig+e3ct1ajA4bw0BAInXZ37FEGGSCUix0k/CjH2Nlt 9 | GREtwbahE0k5oTkVbA5XS4xkNs0M0poAFN5OiFKEAqZ014hqhvKnEUQ2oTe9SVOR 10 | Ww49mLNg36AIEE2Fu2KQb/VT90cwwD0CAwEAATANBgkqhkiG9w0BAQsFAAOBgQBV 11 | sJ4Vb2L1ywLVeAxNqY0PZqS7a8Q2GLhNr5V+3hOoWn7bwqQ7L06UJGSrcLOPZeIH 12 | IWM20aOFHSTWbocd4f+m6s3llyXwBBlK2BPZbWv0OeAHgjN9AVav4flAZ4oD2GxA 13 | aJkGAXmR9QzZNJLai5mv3L/B/p/NxeU3UGfaySxVvw== 14 | -----END CERTIFICATE----- 15 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Lint 3 | 4 | on: 5 | pull_request: 6 | branches: [master] 7 | 8 | jobs: 9 | golangci: 10 | name: Lint 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: read 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4 17 | - name: Setup Go environment 18 | uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # ratchet:actions/setup-go@v5 19 | with: 20 | go-version: 'stable' 21 | check-latest: true 22 | - name: Golangci-lint 23 | uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # ratchet:golangci/golangci-lint-action@v8 24 | with: 25 | version: v2.3.0 26 | install-mode: goinstall 27 | only-new-issues: true 28 | -------------------------------------------------------------------------------- /test-certs/example-small-key.p7b: -------------------------------------------------------------------------------- 1 | -----BEGIN PKCS7----- 2 | MIICXAYJKoZIhvcNAQcCoIICTTCCAkkCAQExADALBgkqhkiG9w0BBwGgggIvMIIC 3 | KzCCAZQCCQDHlr/u+lfb8jANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzEL 4 | MAkGA1UECBMCQ0ExEDAOBgNVBAoTB2NlcnRpZ28xEDAOBgNVBAsTB2V4YW1wbGUx 5 | GjAYBgNVBAMTEWV4YW1wbGUtc21hbGwta2V5MB4XDTE2MDYxMDIyMTQxMloXDTIz 6 | MDQxNTIyMTQxMlowWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQK 7 | EwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRowGAYDVQQDExFleGFtcGxlLXNt 8 | YWxsLWtleTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAlzyCIeP1T87k1rHV 9 | MtbaGXIWpK/VQuvuXwig+e3ct1ajA4bw0BAInXZ37FEGGSCUix0k/CjH2NltGREt 10 | wbahE0k5oTkVbA5XS4xkNs0M0poAFN5OiFKEAqZ014hqhvKnEUQ2oTe9SVORWw49 11 | mLNg36AIEE2Fu2KQb/VT90cwwD0CAwEAATANBgkqhkiG9w0BAQsFAAOBgQBVsJ4V 12 | b2L1ywLVeAxNqY0PZqS7a8Q2GLhNr5V+3hOoWn7bwqQ7L06UJGSrcLOPZeIHIWM2 13 | 0aOFHSTWbocd4f+m6s3llyXwBBlK2BPZbWv0OeAHgjN9AVav4flAZ4oD2GxAaJkG 14 | AXmR9QzZNJLai5mv3L/B/p/NxeU3UGfaySxVv6EAMQA= 15 | -----END PKCS7----- 16 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //go:debug x509negativeserial=1 17 | package main 18 | 19 | import ( 20 | "os" 21 | 22 | "github.com/square/certigo/cli" 23 | "github.com/square/certigo/cli/terminal" 24 | ) 25 | 26 | func main() { 27 | os.Exit(cli.Run(os.Args[1:], terminal.OpenTTY())) 28 | } 29 | -------------------------------------------------------------------------------- /test-certs/example-small-key.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICWwIBAAKBgQCXPIIh4/VPzuTWsdUy1toZchakr9VC6+5fCKD57dy3VqMDhvDQ 3 | EAiddnfsUQYZIJSLHST8KMfY2W0ZES3BtqETSTmhORVsDldLjGQ2zQzSmgAU3k6I 4 | UoQCpnTXiGqG8qcRRDahN71JU5FbDj2Ys2DfoAgQTYW7YpBv9VP3RzDAPQIDAQAB 5 | AoGADClLdZo1IcPLVxBZyorIYv9vnrKc/ZKnSnERMMfV1dBbr27scm63oqHiJtil 6 | GuQpzb38OWK2H1Psl/VvroIXFaxJg6g952POXQBTuXMUTCPz2C8I7XRMVV7y3QtO 7 | T1nBztm0HF2EJyNHcRTjNSf8Od5rc0/hmk4jjB7LUfmPswECQQDH7kpfHwgjAuB/ 8 | SNw+VTlFK02II+/cI3VAhxzP5ghQIGOni08v2x9SB8Cd7/1v6hc7G7fNPDbu1hgu 9 | uKyHYZnhAkEAwaZHH9ZY9Hlk/n+gkPbJaQo04hxjKaLTxbX2qAhlwqCvlCuPWld+ 10 | dc1mJ2x/zujZK6K3WwhqVIx5RvlMpkYJ3QJAJCKQdJLCQLmzY64CiI2UtUi3UaTV 11 | JF+QQKJq9bRoyjqgaFbIcCjVh1j5WlJW5xRTMI648LIyZ4ZwhnlATZO5YQJAaOAJ 12 | ovduI7De/mnWZswQ1l9dtptTJQh3EptyoxwxKE/n5yIUOAQsi9Yanf1H1qIKg3RT 13 | Quzy0SlmwQZg+afyzQJAZlphYDuAvZj4EBVJjs4WMwsaZE3bFangnpWyAEHWrYrT 14 | ybk1V44+/Usb1Kh41u0YFg6fJ/p9af7CM5wg+uJxSg== 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /starttls/psql/user_windows.go: -------------------------------------------------------------------------------- 1 | // Package pq is a pure Go Postgres driver for the database/sql package. 2 | package pq 3 | 4 | import ( 5 | "path/filepath" 6 | "syscall" 7 | ) 8 | 9 | // Perform Windows user name lookup identically to libpq. 10 | // 11 | // The PostgreSQL code makes use of the legacy Win32 function 12 | // GetUserName, and that function has not been imported into stock Go. 13 | // GetUserNameEx is available though, the difference being that a 14 | // wider range of names are available. To get the output to be the 15 | // same as GetUserName, only the base (or last) component of the 16 | // result is returned. 17 | func userCurrent() (string, error) { 18 | pw_name := make([]uint16, 128) 19 | pwname_size := uint32(len(pw_name)) - 1 20 | err := syscall.GetUserNameEx(syscall.NameSamCompatible, &pw_name[0], &pwname_size) 21 | if err != nil { 22 | return "", ErrCouldNotDetectUsername 23 | } 24 | s := syscall.UTF16ToString(pw_name) 25 | u := filepath.Base(s) 26 | return u, nil 27 | } 28 | -------------------------------------------------------------------------------- /test-certs/example-custom-oid.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICejCCAWICAQAwNTEcMBoGA1UEAwwTZXhhbXBsZS1jdXN0b20tb2lkczEVMBMG 3 | CCsGAQQBgqAyDAdleGFtcGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC 4 | AQEAxyNyxFGSp5VDHOHMpOB23b1eeKFGs1GexrajYpVkZA5xq8kaSiyMPXEb38jy 5 | bh2vTiH/YxzTqDpUfrnBZYnlQpx4GSzTT9+0Vwb0jmKitd/lZBA8zvfL3Ni2Ym0T 6 | pZQGHp/QyaVOvvqDiYjbrN+WJay5usHUxFDxFMP1NjyWCT9QKZPrdivKtgFMQzCC 7 | oBkic33mKvSfi+3nd57ol83BJgjIvEZFkVg4R4/fSGWe3DW836rWTJOxj/FIDgGc 8 | Py1eWInQBHmF77CYTGuuVMMrGnV+brc4M4FlBUnQv8sy8S6XZBrfG1Z2npYHkvKC 9 | ZM4LUOmKmpGGFACAlvmrBolIYwIDAQABoAAwDQYJKoZIhvcNAQEFBQADggEBAFqr 10 | qQK9/gLAxBxUfny7cCN+NFBb11HJx8HerLKqBGN4VOh27SkJGg6H5ozj/qwhElVw 11 | SQUX33TigC6roiPDK3+KvL9fxjUY60xnLRXKciMm/1jqeYjht33hQMZAi4OwT+dG 12 | FFVys2zlSFXxqX79+6mkKUIRurymnJWjOHNWOhrDckShDjf5N+A3lUNWgwu7M0cX 13 | T5Bv/6AFV4vuMJALv4JTBC60bT+cfXmYxCox+5rZ+bT+6uOFWu+0H4CPXfP9OU5B 14 | /aA20r2bN16TftaLPY9HSJNPXqlZHX196e6+oqd16Rr6AyYAvGlKmpJgpm5OS17r 15 | 7yMFrPWSWlsOmIhxhXs= 16 | -----END CERTIFICATE REQUEST----- 17 | -------------------------------------------------------------------------------- /test-certs/example-elliptic-sha1.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICiDCCAeqgAwIBAgIJAJ/xjMI6FKOAMAkGByqGSM49BAEwXjELMAkGA1UEBhMC 3 | VVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFt 4 | cGxlMR4wHAYDVQQDExVleGFtcGxlLWVsbGlwdGljLXNoYTEwHhcNMTYwNjIyMDAy 5 | MzIwWhcNMjMwNDI3MDAyMzIwWjBeMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex 6 | EDAOBgNVBAoTB2NlcnRpZ28xEDAOBgNVBAsTB2V4YW1wbGUxHjAcBgNVBAMTFWV4 7 | YW1wbGUtZWxsaXB0aWMtc2hhMTCBmzAQBgcqhkjOPQIBBgUrgQQAIwOBhgAEARBY 8 | 2NPz8x6YNLA9UkPpPSoNM+JsLO6giyTuIwRy1j2PN94bmPRZcvffFMHo0jBbof/u 9 | EEazJm0WAojmcqqED6caAMu+QcYrPdugHCHO1lv5iwZ5sJyswQ8qygaroMWBg7Qa 10 | XjAs1p4Oncuh37CxRj70mse0YEPt+neBD5BM+8ZDchFXo08wTTAdBgNVHSUEFjAU 11 | BggrBgEFBQcDAgYIKwYBBQUHAwEwLAYDVR0RBCUwI4cEfwAAAYcQAAAAAAAAAAAA 12 | AAAAAAAAAYIJbG9jYWxob3N0MAkGByqGSM49BAEDgYwAMIGIAkIByNx56XB0CyNR 13 | 3vKIRdn9uoo8Q9PqBlO8ZtldeF+MqI/UvcnbrVxn7d5+lNj6K2YY2GzwV+IdxRyP 14 | JSsHT03pww0CQgE5ssxuHZXCw1zLFXPilEHKpx1B3umRwR9q0lVc63d9zFT/Dz5L 15 | QqF02WtegKS3Kr6M7R8fn2QvsdzOPqlcN57E2Q== 16 | -----END CERTIFICATE----- 17 | -------------------------------------------------------------------------------- /test-certs/example-md5.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICmTCCAYECAQAwVDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQK 3 | EwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRQwEgYDVQQDEwtleGFtcGxlLW1k 4 | NTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKUBQrGJ6f6HcOmPRLzR 5 | es9cZqYkE4iaQSOQwBIYUag7hE0baYknd+Ch5cCKa4hnHMCrPP7LkS6qR4JmCNqD 6 | eONv8ia6B/ejNFpURZ960991W4dLaaNu8uX7BcO0JUB30BcmbWkWxcpaFodlsNF6 7 | R8OS75Pambdmx2LLIBK5W9+INc6mIPdfe/86CUv9XgjxYB6PyfZnw0qIUEvBtfpU 8 | eIrs3Nxb98SSb2ydjJhYwor//iLY5SalIQdERPOpFr6X+UBfIseZkTdGU/UzMkWh 9 | aCXcoyyw9i3tx/H38/B2wdIYZanfwjXf4QY1fTJhTfYrMFdzC+YCQWQFgu10ZWWa 10 | WiUCAwEAAaAAMA0GCSqGSIb3DQEBBQUAA4IBAQCI52QJ9JZchuAFmWR1rIzMZ8I6 11 | 9yrkh74IMhpDDVVjy5VEbCWIv3k+dOyctSLG6uzl/n0beyp+i8Hk3tGV760upEWV 12 | 3QnvbbO++UoiVv7nb0axDxE6JuJ3D6R0XrtdF2ganhhDyTWZ7k3WKHtRONxp0eHX 13 | SUetvSUgpvmWtB+i551BXUu/c15l2TY/c46vavgAc2Iio7HFbew8YsQUfU4Nz3Wf 14 | Z420Jot4DemLAkRRDswLgZMPV5xHr8r5sEfms7T8ymeFZvzx2BIpPDV4UkMONf4f 15 | QhuCdv+Xeg+7jadhkJxohripthoHsehVeUNefqt2XHrGUeax0HjGAAIwD2G7 16 | -----END CERTIFICATE REQUEST----- 17 | -------------------------------------------------------------------------------- /test-certs/example-leaf.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICmjCCAYICAQAwVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQK 3 | EwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRUwEwYDVQQDEwxleGFtcGxlLWxl 4 | YWYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7stSvfQyGuHw3v34f 5 | isqIdDXberrFoFk9ht/WdXgYzX2uLNKdsR/J5sbWSl8K/5djpzj31eIzqU69w8v7 6 | SChM5x9bouDsABHz3kZucx5cSafEgJojysBkcrq3VY+aJanzbL+qErYX+lhRpPcZ 7 | K6JMWIwar8Y3B2la4yWwieecw2/WfEVvG0M/DOYKnR8QHFsfl3US1dnBM84czKPy 8 | t9r40gDk2XiH/lGts5a94rAGvbr8IMCtq0mA5aH3Fx3mDSi3+4MZwygCAHrF5O5i 9 | SV9rEI+m2+7j2S+jHDUnvV+nqcpb9m6ENECnYX8FD2KcqlOjTmw8smDy09N2Np6i 10 | 464lAgMBAAGgADANBgkqhkiG9w0BAQUFAAOCAQEAZW13CST8BtPCINS0CiIv9BMv 11 | zXpkCRz3riPvrPkllnOY3Dp0NQzQkdj3aE4at5GSN9fOTWCQ0tGnjOLAZ8tqHcyg 12 | FLgU3MjDcsRvyeQ8mYpCqeUbwq/nHIs33jM/x087lTP7aNXGH4sncxZdIv71+sqF 13 | f4WnumxsJUARaeb0AnUZmtAC/OR+9vpiUw+wMMhMbDNCboKYANqnFhWkTKp5/85f 14 | eC21haSG55pT7bGvlG9WNawgXJ3WX48yw29dSyDKd/buVM5Andrp7hYVuC57wz0u 15 | wng/cxCCQrENS4qSvxOgFiLK2j1LHccMuChPFFGyOyXqBNs9pr8F4/2qPJ7tOw== 16 | -----END CERTIFICATE REQUEST----- 17 | -------------------------------------------------------------------------------- /test-certs/example-root.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICmjCCAYICAQAwVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQK 3 | EwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRUwEwYDVQQDEwxleGFtcGxlLXJv 4 | b3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDKOEoSiNjMQ8/zUFcQ 5 | W89LWw+UeTXKGwNDSpGjyi8jBKZ1lWPbnMmrjI6DZ9ReevHHzqBdKZt+9NFPFEz7 6 | djDMRByIuJhRvzhfFBflaIdSeNk2+NpUaFuUUUd6IIePu0AdRveJ8ZGHXRwCeEDI 7 | VCZS4oBYPHOhX/zMWDg8vSO4pSxTjGc7I8fHxaUSkVzUBbeO9T/1eFk0m2uxs3Uz 8 | iUck2X/8YqRd+p/EaBED78nXvKRALAguKAzqxIgk3ccPK0SVQFNFq+eV1/qo8cou 9 | eQuqMpCAvwVkfpVKhneyC2NlMrfzlcZZbfG/irlSjQn5+ExZX4Isy1pCUbOiVfSr 10 | sCdtAgMBAAGgADANBgkqhkiG9w0BAQUFAAOCAQEAoDcBLidVU6KA9IlID32P3uox 11 | fjKsbJee6mswMBqkFKqgdv/vHMiXAttiVxB+Y5AQnkb+kMCl8KwVaXgH145drahz 12 | 0aTH6Rxqrf+1OfStQ+Y1CZgRL26vpafvd3xFE7151upO+dUraiYt9736umoStuqX 13 | WuqV9EZ5RmvhqEW6cIa6zG5KVlHgDs72jC1f+7nAsj3V3EBqwf/NtOMz+whFo3LB 14 | DJU4djTjLiROa/bWI1ZvhKjFf6EWQnZIeGhZLyS8Y+0qoiI1ojhrOYYAdOOHGTV5 15 | RJqViMG2o7YxaMYA/QCaYVmkiTlfwR9fZrZOG4lHZ6PyTLtwkHYXkmCgUW237w== 16 | -----END CERTIFICATE REQUEST----- 17 | -------------------------------------------------------------------------------- /test-certs/example-sha1.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICmjCCAYICAQAwVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQK 3 | EwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRUwEwYDVQQDEwxleGFtcGxlLXNo 4 | YTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCUuAbvyfPJsBEr41hy 5 | eAX26jhZJWVbioCveouRbgfO9JQOdHxKRNJtWiI2+rVwUfIRxF9qO/wsNeRDHjsb 6 | W5KD4prsp8RNLZJqVKm171XwCCUSDeiHTUJfTzsMiV2PwwbzQOK41m0uywtrhEUL 7 | cW9+Z+UZ7wnE6+NlU9aLNGEZ94hh3BsnKip/pGHGsIh14vaXE4M+OTJvXkUs/6/d 8 | L2yBdiZiw9bqv1GIU3vliI5h28tjB118duwf7ZMqxoRQ32wsUmskNMN/S0OLoS/9 9 | BTNyGH2vG/juZnt//Wh35563cun2Qp0va8WzNTRrqRtULfn/+5CwaswnutervIu9 10 | 9xnxAgMBAAGgADANBgkqhkiG9w0BAQUFAAOCAQEAECEYs5iOXzghMNgRB0bCgCKA 11 | Gw8hfiDCZ1qJLei+ozK8lndbWmXnqDBKPUQnNrLcMHRIrqL1h1nS2wDXJjRn8Tdk 12 | CyvGGWpTfN2weOtcxoOs/kQLN5AJWfOhmPmBoImAt9CJw5zRL1d7CRU/+DoZY3Fr 13 | FTZM8hHPghL5O838535sqerwIGFtkX6LkFG/gkz6JzZHI1fusEx73FpgNj3UaBZ4 14 | zOlEkbtOAABq8RVGNXZ5DJQLCTsu0p9w6jsgcYs3PYNyFoW1RMUcTvDEWCExLLr4 15 | m0syZsTfgBNuhaCUNr97BNHR0t1Uy1AnSACZpYdwi254gp0nL5673/CYbZT38A== 16 | -----END CERTIFICATE REQUEST----- 17 | -------------------------------------------------------------------------------- /test-certs/example-expired.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICnTCCAYUCAQAwWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQK 3 | EwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRgwFgYDVQQDEw9leGFtcGxlLWV4 4 | cGlyZWQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzoljseb80Cwfe 5 | e4w45IGejpabCxNgTDeGTh7KmtaIj82miJqJlUIZqFPw0AqosNbm32+ArPpzh9+L 6 | n9pDaxyJyGrdBLukQsBiVLhv57Ld0DqGPrYw5NM7gB3lsVttFkb4x2bYDK+Xp5Xp 7 | xq1CEy/G3toihj35uh3IhgLXNlWrBDmw5P4I8sOfEr4a+GjIPaMv9fBmmVaIhUuh 8 | fp4Skoij9C0HqZJTbXoX8+4DOX8Emid35HMX2ZzEmN1lhmWHBPz6jyhcQwji8R/f 9 | LN37MH+aO7EMlHaLDdtIv7NmvYZX8Kwwsb+Oa9uki7Ij+nl5PLRqXP6Ic6LJ4I32 10 | mAPqPKifAgMBAAGgADANBgkqhkiG9w0BAQUFAAOCAQEAUVa413jOpJ4iCvMLoYFy 11 | UnGGnZGAFN6DwrjzV1WJzeBq8XEdOB6qFmWRXjCisd8yHBLF/iP2TXo2XXCUzvF0 12 | CMczIp83xd6IoUwNqLaSSvDlhb1EyDd07fAtkf+gFI7KSQlWFSSYJIKEc9JtjNGc 13 | QSjoeEN6QJ/wCHDVIXAHrEoy4Po+bdjJdwfRXavdaCR2SYe78Q/ORSkuDP7F0jGw 14 | duGn87c8Gq+givSLK0njX6rn1Ts5YdGAf94RdeHttkKwktJJglpr3ySczVh2zFFz 15 | hwLv4os9hHDHUX2f0EMugbKOBFozaiMLXNiHRPV/3MlnoiI64oTrZ8W6ReCtl/F1 16 | Qw== 17 | -----END CERTIFICATE REQUEST----- 18 | -------------------------------------------------------------------------------- /test-certs/example-bad-serial.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICoDCCAYgCAQAwWzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQK 3 | EwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRswGQYDVQQDExJleGFtcGxlLWJh 4 | ZC1zZXJpYWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5HI9+T9WO 5 | ge5aftXhpLK7NdyUcA10hae7FH+ZbTuMOgeWO7x2oSIwWXUMzvAc9IKTib+9g0o3 6 | yDnJGsNMuYbYtmLeBtG/7TPLX+AsYfvkP7ieU4AH4cCQ60cEWfF7BxIoyYbYikc7 7 | u+vYC2ciTutFNJaQAhMwl4jI70vPKL9G8aaMGgJ/gNm1h7uaH5gLRcbzUiWZHgKX 8 | Jd0Enw7RB14gMQTR6AViyS77inYeC3ihtibxbw1tMyyjFoRaG8DZKdIzH57ZRbYK 9 | j62xX4GN8LAuo9c6v5rAow+TbafnoS/dobqHqDNc4fL9g5UZinfEgVjrJDACkgIQ 10 | m0Ye1VhnT4HlAgMBAAGgADANBgkqhkiG9w0BAQUFAAOCAQEABFYpzaNEUu+bRMVl 11 | 1OlikjOX0S92HGO+qXp/GBNUhMIWH9A/w6j4bRfMuAPzpOKXWRoastH1rixVtGG8 12 | zeko+sn1k7QyXFhk+VN8PGUymdpFcV0VnpmomkcEg6FGHkERiaTuuspYvvKkFZDc 13 | /P7J4YBk/iET6o9F64p9+LGxMDNvAgqtOhrUW+7IDLoigwL3cFZBXGem8BSSn2ot 14 | hkyjNbIJPgqJs/XiYF4wxxNmL3xCTSDxbAByPRZY9UZt4yPKlBFvsuOoOWRb+33a 15 | uvIAkiE8WoR9qArx+iBs8RLaqd27K/PLQMKSbSyY4ZJAQoUg0nTP8a18zRX9JZoV 16 | utwczA== 17 | -----END CERTIFICATE REQUEST----- 18 | -------------------------------------------------------------------------------- /test-certs/example-root-bad-ku.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICoTCCAYkCAQAwXDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQK 3 | EwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRwwGgYDVQQDExNleGFtcGxlLXJv 4 | b3QtYmFkLWt1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsj6lSucM 5 | IMGPdniZrtp0oPz+2fDep9wZeP8u/zCZo5LZE4C0OsQ3SugC3XMGODfGXOUd1lkx 6 | uHsyFcEa/+6+CXidd04cs2OfACz94zgpDLC3JomYd1bGgVqlqd+ucBlLGn7Yt5s+ 7 | zPYocWiRBkuNnJbGBGjalq/pjSTjBG8QBwpAAqitQbSueEu9Q1kmVNDw439BVQZp 8 | ORNrqJl5x6ONsbIwF0uIw+1bPvZagglu3IO8BKIgi3O08xe2+SQ58/LcO00hg2rn 9 | qU6uEwuEXmB8AjJzCBqMSYSZkvMJxiQVgDgBXkvRXL9AJAq1a1w5dkPusq/4Wrby 10 | +L4fMIBetjBNdwIDAQABoAAwDQYJKoZIhvcNAQEFBQADggEBADC1M/iHiNa8GQx0 11 | pqdxmLpzjTxdQNtM/F84r5bP1JUsFqXzak5/9mFpllqslykSYyY+RLhzDFJy6Dtd 12 | 8RsUTTHVY/sUP7mouuh98uPZPUa0a6iEUBy4GqbTsHwXp3H/33yVdKXaTOLP4KT/ 13 | y34k4BAYHn3E+JcRGMrvAh6tyot3iEYU9IGdbjrALo39qicPAHJWvN+Jzm6laUXH 14 | /9QtiDLfC5Q+4Sz986bSVCVMTYbZURA0tned4yY+gG7AQ4ZiuxyS5NEhs3471ez2 15 | XQ5xc98MpkNGmlVxULmu6T7n1UaysXybeeLUuHV1hp781mk87JERQMDN4cA4hV8w 16 | iVkmbZ4= 17 | -----END CERTIFICATE REQUEST----- 18 | -------------------------------------------------------------------------------- /test-certs/example-elliptic-sha1.p7b: -------------------------------------------------------------------------------- 1 | -----BEGIN PKCS7----- 2 | MIICuQYJKoZIhvcNAQcCoIICqjCCAqYCAQExADALBgkqhkiG9w0BBwGgggKMMIIC 3 | iDCCAeqgAwIBAgIJAJ/xjMI6FKOAMAkGByqGSM49BAEwXjELMAkGA1UEBhMCVVMx 4 | CzAJBgNVBAgTAkNBMRAwDgYDVQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxl 5 | MR4wHAYDVQQDExVleGFtcGxlLWVsbGlwdGljLXNoYTEwHhcNMTYwNjIyMDAyMzIw 6 | WhcNMjMwNDI3MDAyMzIwWjBeMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEDAO 7 | BgNVBAoTB2NlcnRpZ28xEDAOBgNVBAsTB2V4YW1wbGUxHjAcBgNVBAMTFWV4YW1w 8 | bGUtZWxsaXB0aWMtc2hhMTCBmzAQBgcqhkjOPQIBBgUrgQQAIwOBhgAEARBY2NPz 9 | 8x6YNLA9UkPpPSoNM+JsLO6giyTuIwRy1j2PN94bmPRZcvffFMHo0jBbof/uEEaz 10 | Jm0WAojmcqqED6caAMu+QcYrPdugHCHO1lv5iwZ5sJyswQ8qygaroMWBg7QaXjAs 11 | 1p4Oncuh37CxRj70mse0YEPt+neBD5BM+8ZDchFXo08wTTAdBgNVHSUEFjAUBggr 12 | BgEFBQcDAgYIKwYBBQUHAwEwLAYDVR0RBCUwI4cEfwAAAYcQAAAAAAAAAAAAAAAA 13 | AAAAAYIJbG9jYWxob3N0MAkGByqGSM49BAEDgYwAMIGIAkIByNx56XB0CyNR3vKI 14 | Rdn9uoo8Q9PqBlO8ZtldeF+MqI/UvcnbrVxn7d5+lNj6K2YY2GzwV+IdxRyPJSsH 15 | T03pww0CQgE5ssxuHZXCw1zLFXPilEHKpx1B3umRwR9q0lVc63d9zFT/Dz5LQqF0 16 | 2WtegKS3Kr6M7R8fn2QvsdzOPqlcN57E2aEAMQA= 17 | -----END PKCS7----- 18 | -------------------------------------------------------------------------------- /test-certs/example-name-constraints.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICpjCCAY4CAQAwYTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQK 3 | EwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMSEwHwYDVQQDExhleGFtcGxlLW5h 4 | bWUtY29uc3RyYWludHMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDk 5 | rrryvC1a63Vc2yCBb0SliieU6TnqZAQ5rJzAa4zf7uq9obwLuGfZV5j/7nYl04M2 6 | gPiz4Rk8uia14tIqBjFjB5DH7czuaPiGKgMSKMXt9lPZxu3ZnRLRmF+I/zlIjxqL 7 | U9bxem7Qs7RBoncbbd65gy8PPe2J09rJdrcj5V/hD2jZpNWtrYA6ANGsMSxLchtp 8 | n3LKyDy/bW2HIhXmqVIq/2j2p2ljKf6E63L4S7e7FW8EMGiW9y+DLO5U7qSZUiqg 9 | AiXHjTKWtYgbnlqboPtZRV5quN2PE0KD3FOLhAQX5MKS3P48+cjeswmruNVc8qjd 10 | Ci1NLsRCTHukM/WxAC35AgMBAAGgADANBgkqhkiG9w0BAQUFAAOCAQEAf486fgU4 11 | srGDTypLMY/rBBf/Y8Dhp+O/bEeHEDbSCBM4k8/WwsQvgZqwkvzQ6xW8HAXBWDe6 12 | r0swDFc4wkKPpwQBhAu6JT8FxozCfbsGT9ITBESWZnphs5bXJmhcXFOjNa78jSPX 13 | uygXlQZB+vKmpURZYehg/sLBFTUslOtrM/PTC40KZfLP6CgRiMASQbZyKcKudHJi 14 | sSiyK3FLCeMoIEDiDAT7CdTH6VKRwgIH9S/D2ZtuNAm0rS2HjRjH3HOYPJy5S2Gl 15 | V8k1LPiCcMc2eDhSkbESvn4MLORHGQdm+bonduG2xn+Mm1WZGF5sVcJ4u13oG06S 16 | WcVAIB7TSGAoDw== 17 | -----END CERTIFICATE REQUEST----- 18 | -------------------------------------------------------------------------------- /starttls/psql/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2013, 'pq' Contributors 2 | Portions Copyright (C) 2011 Blake Mizerany 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /.github/workflows/compilecheck.yaml: -------------------------------------------------------------------------------- 1 | name: compilecheck.yaml 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | build: 11 | name: Compile Check 12 | strategy: 13 | matrix: 14 | version: ['stable'] 15 | target: 16 | - {os: 'darwin', platform: 'macos-latest', arch: 'amd64'} 17 | - {os: 'linux', platform: 'ubuntu-latest', arch: 'amd64'} 18 | - {os: 'windows', platform: 'windows-latest', arch: 'amd64'} 19 | runs-on: ${{ matrix.target.platform }} 20 | permissions: 21 | contents: write 22 | steps: 23 | - name: Setup Go environment 24 | uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # ratchet:actions/setup-go@v5 25 | with: 26 | go-version: ${{ matrix.version }} 27 | check-latest: true 28 | id: go 29 | - name: Checkout 30 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4 31 | - name: Build binary 32 | run: | 33 | go env -w CGO_ENABLED=0 34 | go build -o certigo . 35 | -------------------------------------------------------------------------------- /test-certs/example-expired.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDLDCCAhQCCQCa74bQsAj2/jANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJV 3 | UzELMAkGA1UECBMCQ0ExEDAOBgNVBAoTB2NlcnRpZ28xEDAOBgNVBAsTB2V4YW1w 4 | bGUxGDAWBgNVBAMTD2V4YW1wbGUtZXhwaXJlZDAeFw0xNjA2MTAyMjE0MTJaFw0x 5 | NjA2MTEyMjE0MTJaMFgxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UE 6 | ChMHY2VydGlnbzEQMA4GA1UECxMHZXhhbXBsZTEYMBYGA1UEAxMPZXhhbXBsZS1l 7 | eHBpcmVkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs6JY7Hm/NAsH 8 | 3nuMOOSBno6WmwsTYEw3hk4eyprWiI/NpoiaiZVCGahT8NAKqLDW5t9vgKz6c4ff 9 | i5/aQ2scichq3QS7pELAYlS4b+ey3dA6hj62MOTTO4Ad5bFbbRZG+Mdm2Ayvl6eV 10 | 6catQhMvxt7aIoY9+bodyIYC1zZVqwQ5sOT+CPLDnxK+GvhoyD2jL/XwZplWiIVL 11 | oX6eEpKIo/QtB6mSU216F/PuAzl/BJond+RzF9mcxJjdZYZlhwT8+o8oXEMI4vEf 12 | 3yzd+zB/mjuxDJR2iw3bSL+zZr2GV/CsMLG/jmvbpIuyI/p5eTy0alz+iHOiyeCN 13 | 9pgD6jyonwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAMUuv/zVYniJ94GdOVcNJ/ 14 | bL3CxR5lo6YB04S425qsVrmOex3IQBL1fUduKSSxh5nF+6nzhRzRrDzp07f9pWHL 15 | ZHt6rruVhE1Eqt7TKKCtZg0d85lmx5WddL+yWc5cI1UtCohB9+iZDPUBUR9RcszQ 16 | dGD9PmxnPc9soEcQw/3iNffhMMpLRhPaRW9qtJU8wk16DZunWR8E0Oeq42jVTnb4 17 | ZiD1Idajj0tj/rT5/M1K/ZLEiOzXVpo/+l/+hoXw9eVnRa2nBwjoiZ9cMuGKUpHm 18 | YSv7SyFevNwDwcxcAq6uVitKi0YCqHiNZ7Ye3/BGRDUFpK2IASUo8YbXYNyA/6nu 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /jceks/testdata/private-key-ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDPTCCAiWgAwIBAgIUNV3pM2Ajd3ecZzBIA5+R6/XbqocwDQYJKoZIhvcNAQEL 3 | BQAwOzEQMA4GA1UEAwwHVGVzdCBDQTEaMBgGA1UECgwRVGVzdCBPcmdhbml6YXRp 4 | b24xCzAJBgNVBAYTAlVTMB4XDTI1MDgwMTIyMDgwM1oXDTI2MDgwMTIyMDgwM1ow 5 | OzEQMA4GA1UEAwwHVGVzdCBDQTEaMBgGA1UECgwRVGVzdCBPcmdhbml6YXRpb24x 6 | CzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzx1Y 7 | wJKfigwLvoRCdZuuvoBGDaYjcEpEQtYQF6Uz2CL8Dh/Mm4y9ursJfoQkYvzVeQe7 8 | 6DNnq5gXmqVhfj/umrersbXQlxJId9G1oF/85R+MMMVvtc5m3ifXRUP4hKomSMXW 9 | hiUMfkkcDhH5j6b8XWaBtamgLHzXk7AcLgZRod+uDsGorQJW4ASrTk5+NYkQ1DOY 10 | 9Vm1rEUskyd4YxQnKJIQif5IKlhQpSUrnu1LujulIo0F6hBa9jx8QklAif6XPatc 11 | dgy/P3WGOY7fBvUtFjqdN4IbY0rXYD2wCze8EyoEFenYJg3NklnyE93vGLgA6wTe 12 | YzxtgD1+jNLOKm8P/wIDAQABozkwNzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAd 13 | BgNVHQ4EFgQU+x7IVFNBsBWkTXLcQYYRTfSpa3MwDQYJKoZIhvcNAQELBQADggEB 14 | AHB0u04FhbZOtdbpmeQDtoMwqDCGNctP6mzqlMzLatjs/vAYvx0cbW2edLjXtBAQ 15 | oQi2NlsJh4Yp1gvRhr3ewhT4amyuR6j7tDyH6OiXRpKvY6R4TAzc9yew096HkU4l 16 | EfuhpSYZ2UeSuro9sDg2gw4F0kWwwcox7OddvVOBjyxauN5+C4Vpe2PTzKXRdjXS 17 | +9c5vcGrccstPfVDuI5/8XwfxDlK9qhcvVNQD/ZySxFmppkuyUaO0/PfiRQTC3Nn 18 | rxalbh53EPk/Hno4w/RkAhex5W5lMbi8jCAhYP2x56uJcBxZtQdltXeozO19f3l1 19 | ZejXhjF6zzbywxJfI2Nqn08= 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /test-certs/example-custom-oid.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDPDCCAiSgAwIBAgIJAMnEiF0KGVsZMA0GCSqGSIb3DQEBCwUAMDUxHDAaBgNV 3 | BAMME2V4YW1wbGUtY3VzdG9tLW9pZHMxFTATBggrBgEEAYKgMgwHZXhhbXBsZTAe 4 | Fw0xNzA4MTgxOTQyMDZaFw0yNDA2MjIxOTQyMDZaMDUxHDAaBgNVBAMME2V4YW1w 5 | bGUtY3VzdG9tLW9pZHMxFTATBggrBgEEAYKgMgwHZXhhbXBsZTCCASIwDQYJKoZI 6 | hvcNAQEBBQADggEPADCCAQoCggEBAMcjcsRRkqeVQxzhzKTgdt29XnihRrNRnsa2 7 | o2KVZGQOcavJGkosjD1xG9/I8m4dr04h/2Mc06g6VH65wWWJ5UKceBks00/ftFcG 8 | 9I5iorXf5WQQPM73y9zYtmJtE6WUBh6f0MmlTr76g4mI26zfliWsubrB1MRQ8RTD 9 | 9TY8lgk/UCmT63YryrYBTEMwgqAZInN95ir0n4vt53ee6JfNwSYIyLxGRZFYOEeP 10 | 30hlntw1vN+q1kyTsY/xSA4BnD8tXliJ0AR5he+wmExrrlTDKxp1fm63ODOBZQVJ 11 | 0L/LMvEul2Qa3xtWdp6WB5LygmTOC1DpipqRhhQAgJb5qwaJSGMCAwEAAaNPME0w 12 | HQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMCwGA1UdEQQlMCOHBH8AAAGH 13 | EAAAAAAAAAAAAAAAAAAAAAGCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEA 14 | Tglf3JyqYQXGA7cftxoNh8ZHbKlGwZ/V7qGPunhY+3lUdcU9nMrGJkHfhtHjbxwy 15 | Mp8Xin+oJWxvQC74MhwN4eYmbgxoeIhnWpwycuLC748/dEF3G+lbzPoBDGC7oaLd 16 | Bdy4ChAAPGu47fQrkxCJP9TTYWrTcrgo7rioB+7v7Hu68nW3qp5hlcBOpI5f8SIm 17 | 2RKJMZDkavUrwQx/wRWqG5lrn7mRVNNJ1hO//NrfrcobtGdKCSH9uoNTCYvBtcAT 18 | j+MGqfv1mmYaTzS3RGBd3w14bWxR9qBuFB5GccgPMzW6hMaIhGM5+qjdQ0GnOgnz 19 | n2g4qtGeN+uglDfulWZ7lA== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /jceks/testdata/trusted-cert.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDQTCCAimgAwIBAgIUYyE1tXkBVbP+bQHd1LRcx9AKEyEwDQYJKoZIhvcNAQEL 3 | BQAwPTESMBAGA1UEAwwJVGVzdCBVc2VyMRowGAYDVQQKDBFUZXN0IE9yZ2FuaXph 4 | dGlvbjELMAkGA1UEBhMCVVMwHhcNMjUwODAxMjIwODAzWhcNMjYwODAxMjIwODAz 5 | WjA9MRIwEAYDVQQDDAlUZXN0IFVzZXIxGjAYBgNVBAoMEVRlc3QgT3JnYW5pemF0 6 | aW9uMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB 7 | ALSuOz/t1AQ/OuB1zdgIjcdTETDw6m1RKe8fC/a53DJ8dtaosgJLWjc94yhG3RNz 8 | +E3QOtnc3m+ct0bQkGuzPAPPax93xRVAK2aUDbexenSKtMYNHCUbIXMgin2xr14R 9 | //bFG2zVyn/lZQwDPeb5l72y9jK9ezTHIlkmmSexE1lRqxRpbjWjpin4MB/onCu/ 10 | KjwcikFrllQ7EB2A109hqSeoZjeVrl75gnxwbMaMVi6EjJhxyAS3kIvPWsrIJkVy 11 | cEzARBb2gF4eOgfIrc7MjNi2N0bBt7Ps/mlKyvBIm9E3XUUMLdnrq50kpeYNklZD 12 | N0Rdossc/1wK3i/BHMprh/cCAwEAAaM5MDcwCQYDVR0TBAIwADALBgNVHQ8EBAMC 13 | BeAwHQYDVR0OBBYEFMhaAGHG0+LuezKyEPdkhDg6iyYsMA0GCSqGSIb3DQEBCwUA 14 | A4IBAQA8jTCQ4xAP9QZowY2EVVlE9sd9J7XcWjdB2698WBi98DDgK1aH2vVcuseQ 15 | v/Oi1djb+3J2zAThLnOCZvS6YC/MGwbcXibi0RFm+KaPfU4gejoHHJa3S4bU2JZu 16 | 11L+8delsttiJOiy9G+rHP1kINR/0ox8tbOzQryn+gVASwG76f93kzLe6EDUJaD1 17 | r9vS1WM3433lgY9Uhe3m9hGquBOL5pj4042g3tv4jKldYdN9CNC9RoIFu7HD3lpV 18 | In6zeRDU64eUZzXCD61G0jbT/ikEbhc9WWPK5211blHt8iVd09pMuhdxoyiVutGH 19 | xWJnJl77tsKEXJcBw8FNfVBOIUjA 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /jceks/testdata/private-key.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDSDCCAjCgAwIBAgIUZtp1JR91xLftP0ho218v1CbSVKMwDQYJKoZIhvcNAQEL 3 | BQAwOzEQMA4GA1UEAwwHVGVzdCBDQTEaMBgGA1UECgwRVGVzdCBPcmdhbml6YXRp 4 | b24xCzAJBgNVBAYTAlVTMB4XDTI1MDgwMTIyMDgwM1oXDTI2MDgwMTIyMDgwM1ow 5 | PTESMBAGA1UEAwwJVGVzdCBVc2VyMRowGAYDVQQKDBFUZXN0IE9yZ2FuaXphdGlv 6 | bjELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCm 7 | H1a2ZlglMV9PJn0VpVSo9GoIDlncCpia/r5+NdYyglTXxytlT6kgfD7MM5xxKLVL 8 | bQGsEyPT2qyfJyAtTGh7v3orWN5e7k+0s8c+wo6Q3FuhXFlfA44fZY3bUVqKBpM5 9 | sGijRFPrByY74e+cFm+p1ipe/HG+CYRMJgvAYj6LhvovDLN3xzB/Q5vr2x9FOGBN 10 | 8wVeH7Pxu29+aSXltWzpvgclYZnOgMqhYxjQ45/FGBcO5lM9ReUcvOx23bb8DaT2 11 | CBAIZMkabFnrlijRxypVO5E5v8GnOOzzQEcrdYYi33d3W90s0YOG9bS7sVeUnfl6 12 | dikwhCblXXirKcK9BSnXAgMBAAGjQjBAMB0GA1UdDgQWBBRdq/i4SMFlmRgA3rl6 13 | E7bx8j9HLTAfBgNVHSMEGDAWgBT7HshUU0GwFaRNctxBhhFN9KlrczANBgkqhkiG 14 | 9w0BAQsFAAOCAQEAY2SYBN5t2h1CtDQ4MASazZ/FDdf7as3oLXH1RGf8TGDa+blf 15 | kBELLkC9UaAKQpdeCBpxdKsV2PmpR4njiBcwI6U7Y1+k0Xbcc+0LSdeUu5cioaVZ 16 | J83XOTCG1ZHstAm0Gll7ubiPLR9TIAZvs4mbslk8JvO70JZrCayaYVD7iVhrcSlp 17 | Oyu7VxEywNtSvvoTOAf5Kwu2AhjzTL0EWatQVpY4rNLaZVSjXkUbDFHOeUlpCPf8 18 | 72gAZxXjQ7buW/kSlG63ETvVss1CDC0Dco6viOVWm9Uy46T3kupJ55OAwNt9rNPz 19 | 5zcnAXPw+A8BmbUn8BhYXaaPByh1E6Mk3dC7bA== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /test-certs/example-root.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDUzCCAjugAwIBAgIJAKg+LQlirffwMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNV 3 | BAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMH 4 | ZXhhbXBsZTEVMBMGA1UEAxMMZXhhbXBsZS1yb290MB4XDTE2MDYxMDIyMTQxMVoX 5 | DTIzMDQxNTIyMTQxMVowVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYD 6 | VQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRUwEwYDVQQDEwxleGFtcGxl 7 | LXJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDKOEoSiNjMQ8/z 8 | UFcQW89LWw+UeTXKGwNDSpGjyi8jBKZ1lWPbnMmrjI6DZ9ReevHHzqBdKZt+9NFP 9 | FEz7djDMRByIuJhRvzhfFBflaIdSeNk2+NpUaFuUUUd6IIePu0AdRveJ8ZGHXRwC 10 | eEDIVCZS4oBYPHOhX/zMWDg8vSO4pSxTjGc7I8fHxaUSkVzUBbeO9T/1eFk0m2ux 11 | s3UziUck2X/8YqRd+p/EaBED78nXvKRALAguKAzqxIgk3ccPK0SVQFNFq+eV1/qo 12 | 8coueQuqMpCAvwVkfpVKhneyC2NlMrfzlcZZbfG/irlSjQn5+ExZX4Isy1pCUbOi 13 | VfSrsCdtAgMBAAGjJjAkMA4GA1UdDwEB/wQEAwICBDASBgNVHRMBAf8ECDAGAQH/ 14 | AgEAMA0GCSqGSIb3DQEBCwUAA4IBAQCLEJU65vTU+oLbNHLOCR6fALrbjK7xsi6S 15 | FDpSXBMm74MWsy3myDBmXpOcN8hCYgsgivUXTQz9ynXP/pzOj4b83zzlaOfPtLTA 16 | mMhKWVV4Q85mrDQz+HzG4lKXM78eTsD8PyrocA/tSE7mVEJ0Jal4E2KI/Z9/fqpY 17 | FLB6LFlx5n83ehXM/egA0l4OeCC9nBKCeNUN3sIQO85lljyzAJdtWnsdoWogJs6q 18 | jcV8n2U5xjZxN5ZFdclYLjq6g2cjEXXMQxb8b7ZhHjLWFdjHP85UvXHK3DpK3JmU 19 | g8bYS7t1DJffDQNjawhlsMycKZN+r0ND0Um4m7AjGqxbKT/M2yKF 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /test-certs/example-expired.p7b: -------------------------------------------------------------------------------- 1 | -----BEGIN PKCS7----- 2 | MIIDXQYJKoZIhvcNAQcCoIIDTjCCA0oCAQExADALBgkqhkiG9w0BBwGgggMwMIID 3 | LDCCAhQCCQCa74bQsAj2/jANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJVUzEL 4 | MAkGA1UECBMCQ0ExEDAOBgNVBAoTB2NlcnRpZ28xEDAOBgNVBAsTB2V4YW1wbGUx 5 | GDAWBgNVBAMTD2V4YW1wbGUtZXhwaXJlZDAeFw0xNjA2MTAyMjE0MTJaFw0xNjA2 6 | MTEyMjE0MTJaMFgxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMH 7 | Y2VydGlnbzEQMA4GA1UECxMHZXhhbXBsZTEYMBYGA1UEAxMPZXhhbXBsZS1leHBp 8 | cmVkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs6JY7Hm/NAsH3nuM 9 | OOSBno6WmwsTYEw3hk4eyprWiI/NpoiaiZVCGahT8NAKqLDW5t9vgKz6c4ffi5/a 10 | Q2scichq3QS7pELAYlS4b+ey3dA6hj62MOTTO4Ad5bFbbRZG+Mdm2Ayvl6eV6cat 11 | QhMvxt7aIoY9+bodyIYC1zZVqwQ5sOT+CPLDnxK+GvhoyD2jL/XwZplWiIVLoX6e 12 | EpKIo/QtB6mSU216F/PuAzl/BJond+RzF9mcxJjdZYZlhwT8+o8oXEMI4vEf3yzd 13 | +zB/mjuxDJR2iw3bSL+zZr2GV/CsMLG/jmvbpIuyI/p5eTy0alz+iHOiyeCN9pgD 14 | 6jyonwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAMUuv/zVYniJ94GdOVcNJ/bL3C 15 | xR5lo6YB04S425qsVrmOex3IQBL1fUduKSSxh5nF+6nzhRzRrDzp07f9pWHLZHt6 16 | rruVhE1Eqt7TKKCtZg0d85lmx5WddL+yWc5cI1UtCohB9+iZDPUBUR9RcszQdGD9 17 | PmxnPc9soEcQw/3iNffhMMpLRhPaRW9qtJU8wk16DZunWR8E0Oeq42jVTnb4ZiD1 18 | Idajj0tj/rT5/M1K/ZLEiOzXVpo/+l/+hoXw9eVnRa2nBwjoiZ9cMuGKUpHmYSv7 19 | SyFevNwDwcxcAq6uVitKi0YCqHiNZ7Ye3/BGRDUFpK2IASUo8YbXYNyA/6nuoQAx 20 | AA== 21 | -----END PKCS7----- 22 | -------------------------------------------------------------------------------- /test-certs/example-root-bad-ku.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDYTCCAkmgAwIBAgIJAOX7/hy/dHNrMA0GCSqGSIb3DQEBCwUAMFwxCzAJBgNV 3 | BAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMH 4 | ZXhhbXBsZTEcMBoGA1UEAxMTZXhhbXBsZS1yb290LWJhZC1rdTAeFw0xNjA2MTAy 5 | MjE0MTJaFw0yMzA0MTUyMjE0MTJaMFwxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJD 6 | QTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMHZXhhbXBsZTEcMBoGA1UEAxMT 7 | ZXhhbXBsZS1yb290LWJhZC1rdTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC 8 | ggEBALI+pUrnDCDBj3Z4ma7adKD8/tnw3qfcGXj/Lv8wmaOS2ROAtDrEN0roAt1z 9 | Bjg3xlzlHdZZMbh7MhXBGv/uvgl4nXdOHLNjnwAs/eM4KQywtyaJmHdWxoFapanf 10 | rnAZSxp+2LebPsz2KHFokQZLjZyWxgRo2pav6Y0k4wRvEAcKQAKorUG0rnhLvUNZ 11 | JlTQ8ON/QVUGaTkTa6iZecejjbGyMBdLiMPtWz72WoIJbtyDvASiIItztPMXtvkk 12 | OfPy3DtNIYNq56lOrhMLhF5gfAIycwgajEmEmZLzCcYkFYA4AV5L0Vy/QCQKtWtc 13 | OXZD7rKv+Fq28vi+HzCAXrYwTXcCAwEAAaMmMCQwDgYDVR0PAQH/BAQDAgECMBIG 14 | A1UdEwEB/wQIMAYBAf8CAQAwDQYJKoZIhvcNAQELBQADggEBABfPQM3XOudPMhEr 15 | k7pvh6nTX5ojmC3DSAv4Ideulqj1q5DvMrcYhP0nX9Fq83zKjk+29VdvFDN3BpLT 16 | 6X1LxcK7dbhGLjPAj+gsdHdiMOjbwhCIjNFbx1VH6w3mG1Kb8AypTOMdZk0v4e4u 17 | +SCSrlhsCVGliFwWFCLmrMImnr6GgFYAmoFQ0wOYrgAW4j/Mj5P9pl7bUtX4Lyui 18 | +vt0Nai617RBzyBIubupPRIrWYAoyrRsstp6EL3D1PXF5QVy3L1DiM+fc/+TI174 19 | HLyyLfdtUClUtvdNYgosB5BAYqSMlxtSM50fvw8+/KG+xnd4UE6ruF0m+Ml9RB2p 20 | 9XHTpLI= 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /test-certs/example-custom-oid.p7b: -------------------------------------------------------------------------------- 1 | -----BEGIN PKCS7----- 2 | MIIDbQYJKoZIhvcNAQcCoIIDXjCCA1oCAQExADALBgkqhkiG9w0BBwGgggNAMIID 3 | PDCCAiSgAwIBAgIJAMnEiF0KGVsZMA0GCSqGSIb3DQEBCwUAMDUxHDAaBgNVBAMM 4 | E2V4YW1wbGUtY3VzdG9tLW9pZHMxFTATBggrBgEEAYKgMgwHZXhhbXBsZTAeFw0x 5 | NzA4MTgxOTQyMDZaFw0yNDA2MjIxOTQyMDZaMDUxHDAaBgNVBAMME2V4YW1wbGUt 6 | Y3VzdG9tLW9pZHMxFTATBggrBgEEAYKgMgwHZXhhbXBsZTCCASIwDQYJKoZIhvcN 7 | AQEBBQADggEPADCCAQoCggEBAMcjcsRRkqeVQxzhzKTgdt29XnihRrNRnsa2o2KV 8 | ZGQOcavJGkosjD1xG9/I8m4dr04h/2Mc06g6VH65wWWJ5UKceBks00/ftFcG9I5i 9 | orXf5WQQPM73y9zYtmJtE6WUBh6f0MmlTr76g4mI26zfliWsubrB1MRQ8RTD9TY8 10 | lgk/UCmT63YryrYBTEMwgqAZInN95ir0n4vt53ee6JfNwSYIyLxGRZFYOEeP30hl 11 | ntw1vN+q1kyTsY/xSA4BnD8tXliJ0AR5he+wmExrrlTDKxp1fm63ODOBZQVJ0L/L 12 | MvEul2Qa3xtWdp6WB5LygmTOC1DpipqRhhQAgJb5qwaJSGMCAwEAAaNPME0wHQYD 13 | VR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMCwGA1UdEQQlMCOHBH8AAAGHEAAA 14 | AAAAAAAAAAAAAAAAAAGCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEATglf 15 | 3JyqYQXGA7cftxoNh8ZHbKlGwZ/V7qGPunhY+3lUdcU9nMrGJkHfhtHjbxwyMp8X 16 | in+oJWxvQC74MhwN4eYmbgxoeIhnWpwycuLC748/dEF3G+lbzPoBDGC7oaLdBdy4 17 | ChAAPGu47fQrkxCJP9TTYWrTcrgo7rioB+7v7Hu68nW3qp5hlcBOpI5f8SIm2RKJ 18 | MZDkavUrwQx/wRWqG5lrn7mRVNNJ1hO//NrfrcobtGdKCSH9uoNTCYvBtcATj+MG 19 | qfv1mmYaTzS3RGBd3w14bWxR9qBuFB5GccgPMzW6hMaIhGM5+qjdQ0GnOgnzn2g4 20 | qtGeN+uglDfulWZ7lKEAMQA= 21 | -----END PKCS7----- 22 | -------------------------------------------------------------------------------- /test-certs/example-md5.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDejCCAmKgAwIBAgIJAIx/wkxu2k9LMA0GCSqGSIb3DQEBBAUAMFQxCzAJBgNV 3 | BAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMH 4 | ZXhhbXBsZTEUMBIGA1UEAxMLZXhhbXBsZS1tZDUwHhcNMTYwNjEwMjIxNDEyWhcN 5 | MjMwNDE1MjIxNDEyWjBUMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEDAOBgNV 6 | BAoTB2NlcnRpZ28xEDAOBgNVBAsTB2V4YW1wbGUxFDASBgNVBAMTC2V4YW1wbGUt 7 | bWQ1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApQFCsYnp/odw6Y9E 8 | vNF6z1xmpiQTiJpBI5DAEhhRqDuETRtpiSd34KHlwIpriGccwKs8/suRLqpHgmYI 9 | 2oN442/yJroH96M0WlRFn3rT33Vbh0tpo27y5fsFw7QlQHfQFyZtaRbFyloWh2Ww 10 | 0XpHw5Lvk9qZt2bHYssgErlb34g1zqYg9197/zoJS/1eCPFgHo/J9mfDSohQS8G1 11 | +lR4iuzc3Fv3xJJvbJ2MmFjCiv/+ItjlJqUhB0RE86kWvpf5QF8ix5mRN0ZT9TMy 12 | RaFoJdyjLLD2Le3H8ffz8HbB0hhlqd/CNd/hBjV9MmFN9iswV3ML5gJBZAWC7XRl 13 | ZZpaJQIDAQABo08wTTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwLAYD 14 | VR0RBCUwI4cEfwAAAYcQAAAAAAAAAAAAAAAAAAAAAYIJbG9jYWxob3N0MA0GCSqG 15 | SIb3DQEBBAUAA4IBAQBYKtQ+QvaGZvDwJ1enDvI95/OQXn3Up8/7I6CAV9wbFq7e 16 | a5gIp8tPSdu2qzf0jEp3qadWZUTqL/2vffXrsy2simjCEQQ8tgkDJHhjQhOgSmxm 17 | IV80F6mpfSeLtEbNcpobDe9PlNhXCf5mCSWC4N7Us29hMaCYyPocT50UMp9g6PrT 18 | JNDjuh2AovI38QUgDNk0ZnjfMNuFERae/gN8X9Kin0kx5dncsuF4cvaGuOv/C90h 19 | XIga1dTuOXQtac48f4TUc8mTq52mSwDceIePYEK1+5UebvhWbwJz4oxkmVFmLhTe 20 | 8qY6j9Lv+cc5dr3nNMI2CoJj2h3E5G6I371GC0Vv 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /test-certs/example-leaf.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDfDCCAmSgAwIBAgIJANWAkzF7PA8/MA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNV 3 | BAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMH 4 | ZXhhbXBsZTEVMBMGA1UEAxMMZXhhbXBsZS1sZWFmMB4XDTE2MDYxMDIyMTQxMVoX 5 | DTIzMDQxNTIyMTQxMVowVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYD 6 | VQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRUwEwYDVQQDEwxleGFtcGxl 7 | LWxlYWYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7stSvfQyGuHw3 8 | v34fisqIdDXberrFoFk9ht/WdXgYzX2uLNKdsR/J5sbWSl8K/5djpzj31eIzqU69 9 | w8v7SChM5x9bouDsABHz3kZucx5cSafEgJojysBkcrq3VY+aJanzbL+qErYX+lhR 10 | pPcZK6JMWIwar8Y3B2la4yWwieecw2/WfEVvG0M/DOYKnR8QHFsfl3US1dnBM84c 11 | zKPyt9r40gDk2XiH/lGts5a94rAGvbr8IMCtq0mA5aH3Fx3mDSi3+4MZwygCAHrF 12 | 5O5iSV9rEI+m2+7j2S+jHDUnvV+nqcpb9m6ENECnYX8FD2KcqlOjTmw8smDy09N2 13 | Np6i464lAgMBAAGjTzBNMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAs 14 | BgNVHREEJTAjhwR/AAABhxAAAAAAAAAAAAAAAAAAAAABgglsb2NhbGhvc3QwDQYJ 15 | KoZIhvcNAQELBQADggEBAGM4aa/qrURUweZBIwZYv8O9b2+r4l0HjGAh982/B9sM 16 | lM05kojyDCUGvj86z18Lm8mKr4/y+i0nJ+vDIksEvfDuzw5ALAXGcBzPJKtICUf7 17 | LstA/n9NNpshWz0kld9ylnB5mbUzSFDncVyeXkEf5sGQXdIIZT9ChRBoiloSaa7d 18 | vBVCcsX1LGP2LWqKtD+7nUnw5qCwtyAVT8pthEUxFTpywoiJS5ZdzeEx8MNGvUeL 19 | Fj2kleqPF78EioEQlSOxViCuctEtnQuPcDLHNFr10byTZY9roObiqdsJLMVvb2Xl 20 | iJjAqaPa9AkYwGE6xHw2ispwg64Rse0+AtKups19WIU= 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /test-certs/example-root.p7b: -------------------------------------------------------------------------------- 1 | -----BEGIN PKCS7----- 2 | MIIDhAYJKoZIhvcNAQcCoIIDdTCCA3ECAQExADALBgkqhkiG9w0BBwGgggNXMIID 3 | UzCCAjugAwIBAgIJAKg+LQlirffwMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNVBAYT 4 | AlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMHZXhh 5 | bXBsZTEVMBMGA1UEAxMMZXhhbXBsZS1yb290MB4XDTE2MDYxMDIyMTQxMVoXDTIz 6 | MDQxNTIyMTQxMVowVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQK 7 | EwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRUwEwYDVQQDEwxleGFtcGxlLXJv 8 | b3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDKOEoSiNjMQ8/zUFcQ 9 | W89LWw+UeTXKGwNDSpGjyi8jBKZ1lWPbnMmrjI6DZ9ReevHHzqBdKZt+9NFPFEz7 10 | djDMRByIuJhRvzhfFBflaIdSeNk2+NpUaFuUUUd6IIePu0AdRveJ8ZGHXRwCeEDI 11 | VCZS4oBYPHOhX/zMWDg8vSO4pSxTjGc7I8fHxaUSkVzUBbeO9T/1eFk0m2uxs3Uz 12 | iUck2X/8YqRd+p/EaBED78nXvKRALAguKAzqxIgk3ccPK0SVQFNFq+eV1/qo8cou 13 | eQuqMpCAvwVkfpVKhneyC2NlMrfzlcZZbfG/irlSjQn5+ExZX4Isy1pCUbOiVfSr 14 | sCdtAgMBAAGjJjAkMA4GA1UdDwEB/wQEAwICBDASBgNVHRMBAf8ECDAGAQH/AgEA 15 | MA0GCSqGSIb3DQEBCwUAA4IBAQCLEJU65vTU+oLbNHLOCR6fALrbjK7xsi6SFDpS 16 | XBMm74MWsy3myDBmXpOcN8hCYgsgivUXTQz9ynXP/pzOj4b83zzlaOfPtLTAmMhK 17 | WVV4Q85mrDQz+HzG4lKXM78eTsD8PyrocA/tSE7mVEJ0Jal4E2KI/Z9/fqpYFLB6 18 | LFlx5n83ehXM/egA0l4OeCC9nBKCeNUN3sIQO85lljyzAJdtWnsdoWogJs6qjcV8 19 | n2U5xjZxN5ZFdclYLjq6g2cjEXXMQxb8b7ZhHjLWFdjHP85UvXHK3DpK3JmUg8bY 20 | S7t1DJffDQNjawhlsMycKZN+r0ND0Um4m7AjGqxbKT/M2yKFoQAxAA== 21 | -----END PKCS7----- 22 | -------------------------------------------------------------------------------- /test-certs/example-sha1.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDfDCCAmSgAwIBAgIJAKmeSDe4ln5zMA0GCSqGSIb3DQEBBQUAMFUxCzAJBgNV 3 | BAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMH 4 | ZXhhbXBsZTEVMBMGA1UEAxMMZXhhbXBsZS1zaGExMB4XDTE2MDYxMDIyMTQxMVoX 5 | DTIzMDQxNTIyMTQxMVowVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYD 6 | VQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRUwEwYDVQQDEwxleGFtcGxl 7 | LXNoYTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCUuAbvyfPJsBEr 8 | 41hyeAX26jhZJWVbioCveouRbgfO9JQOdHxKRNJtWiI2+rVwUfIRxF9qO/wsNeRD 9 | HjsbW5KD4prsp8RNLZJqVKm171XwCCUSDeiHTUJfTzsMiV2PwwbzQOK41m0uywtr 10 | hEULcW9+Z+UZ7wnE6+NlU9aLNGEZ94hh3BsnKip/pGHGsIh14vaXE4M+OTJvXkUs 11 | /6/dL2yBdiZiw9bqv1GIU3vliI5h28tjB118duwf7ZMqxoRQ32wsUmskNMN/S0OL 12 | oS/9BTNyGH2vG/juZnt//Wh35563cun2Qp0va8WzNTRrqRtULfn/+5Cwaswnuter 13 | vIu99xnxAgMBAAGjTzBNMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAs 14 | BgNVHREEJTAjhwR/AAABhxAAAAAAAAAAAAAAAAAAAAABgglsb2NhbGhvc3QwDQYJ 15 | KoZIhvcNAQEFBQADggEBADZJcu8Rb/ehLSlv5cf0D7zlAAyGtnfE0yB8rNi8T1RZ 16 | aoKO9mbg3YoStT0mmqZB/K3v0SAaFMe3UFZK870MrjL5VLjj/+X/djW+ENiXpCCp 17 | Ao/oK9WYG3EPDmz6AtSermlAJ1Ae7kaKbJY9SwZwM4QMz9xMR8tNKuo+i/NiMHtm 18 | KgkNPMxNjzdOFWQ8rBMfV2ZYWP8Z6R97VuCMGDnA1Hy6/95IPzvh7KfgIy+0jua9 19 | Fo8aq8RPkpEgqkvhciFzn2lw2AroQLUfAxQ7nSF35aCbDCnN0bK4sd9Y0zRU53/f 20 | clEIdVP9l8nN4DBMRvwCSIQbHaDxM/6PYVTDDdpWqnA= 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /test-certs/example-bad-serial.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDgDCCAmigAwIBAgIBADANBgkqhkiG9w0BAQsFADBbMQswCQYDVQQGEwJVUzEL 3 | MAkGA1UECBMCQ0ExEDAOBgNVBAoTB2NlcnRpZ28xEDAOBgNVBAsTB2V4YW1wbGUx 4 | GzAZBgNVBAMTEmV4YW1wbGUtYmFkLXNlcmlhbDAeFw0xNjA2MTAyMjE0MTJaFw0y 5 | MzA0MTUyMjE0MTJaMFsxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UE 6 | ChMHY2VydGlnbzEQMA4GA1UECxMHZXhhbXBsZTEbMBkGA1UEAxMSZXhhbXBsZS1i 7 | YWQtc2VyaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuRyPfk/V 8 | joHuWn7V4aSyuzXclHANdIWnuxR/mW07jDoHlju8dqEiMFl1DM7wHPSCk4m/vYNK 9 | N8g5yRrDTLmG2LZi3gbRv+0zy1/gLGH75D+4nlOAB+HAkOtHBFnxewcSKMmG2IpH 10 | O7vr2AtnIk7rRTSWkAITMJeIyO9Lzyi/RvGmjBoCf4DZtYe7mh+YC0XG81IlmR4C 11 | lyXdBJ8O0QdeIDEE0egFYsku+4p2Hgt4obYm8W8NbTMsoxaEWhvA2SnSMx+e2UW2 12 | Co+tsV+BjfCwLqPXOr+awKMPk22n56Ev3aG6h6gzXOHy/YOVGYp3xIFY6yQwApIC 13 | EJtGHtVYZ0+B5QIDAQABo08wTTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH 14 | AwEwLAYDVR0RBCUwI4cEfwAAAYcQAAAAAAAAAAAAAAAAAAAAAYIJbG9jYWxob3N0 15 | MA0GCSqGSIb3DQEBCwUAA4IBAQB6zkdwhzIOelObaUFZFiZ9eFqaZdKr74CWvTBc 16 | K7mmryX0CdXDJS+iKgwpT+gZP36x4nahh0YEaU0XDjm8Zb8GiYeDJKei1vsSRbGC 17 | 62J8n6ZQVcxcMYX0wuiwfXgtxlt9Xpdgf52S+PA9X293siCAlaIDfNUD386kBtDa 18 | U90Q82HfLXmaMTfb1z+6blEnxvOGOHe0XaRIJRV6kUP3YvBbC+jW/5fj89lr35UB 19 | 6sI4pLvbGdV88E3Rw3TAsFKIJa8nOrzLvnICBdwfN76JyovH5t3n3txp2RIdGilw 20 | JWDAZZJA838IXbIrUbNiH3ZugzA7KJjyn/hA2igKYszJ9lNG 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /test-certs/example-root-bad-ku.p7b: -------------------------------------------------------------------------------- 1 | -----BEGIN PKCS7----- 2 | MIIDkgYJKoZIhvcNAQcCoIIDgzCCA38CAQExADALBgkqhkiG9w0BBwGgggNlMIID 3 | YTCCAkmgAwIBAgIJAOX7/hy/dHNrMA0GCSqGSIb3DQEBCwUAMFwxCzAJBgNVBAYT 4 | AlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMHZXhh 5 | bXBsZTEcMBoGA1UEAxMTZXhhbXBsZS1yb290LWJhZC1rdTAeFw0xNjA2MTAyMjE0 6 | MTJaFw0yMzA0MTUyMjE0MTJaMFwxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEQ 7 | MA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMHZXhhbXBsZTEcMBoGA1UEAxMTZXhh 8 | bXBsZS1yb290LWJhZC1rdTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB 9 | ALI+pUrnDCDBj3Z4ma7adKD8/tnw3qfcGXj/Lv8wmaOS2ROAtDrEN0roAt1zBjg3 10 | xlzlHdZZMbh7MhXBGv/uvgl4nXdOHLNjnwAs/eM4KQywtyaJmHdWxoFapanfrnAZ 11 | Sxp+2LebPsz2KHFokQZLjZyWxgRo2pav6Y0k4wRvEAcKQAKorUG0rnhLvUNZJlTQ 12 | 8ON/QVUGaTkTa6iZecejjbGyMBdLiMPtWz72WoIJbtyDvASiIItztPMXtvkkOfPy 13 | 3DtNIYNq56lOrhMLhF5gfAIycwgajEmEmZLzCcYkFYA4AV5L0Vy/QCQKtWtcOXZD 14 | 7rKv+Fq28vi+HzCAXrYwTXcCAwEAAaMmMCQwDgYDVR0PAQH/BAQDAgECMBIGA1Ud 15 | EwEB/wQIMAYBAf8CAQAwDQYJKoZIhvcNAQELBQADggEBABfPQM3XOudPMhErk7pv 16 | h6nTX5ojmC3DSAv4Ideulqj1q5DvMrcYhP0nX9Fq83zKjk+29VdvFDN3BpLT6X1L 17 | xcK7dbhGLjPAj+gsdHdiMOjbwhCIjNFbx1VH6w3mG1Kb8AypTOMdZk0v4e4u+SCS 18 | rlhsCVGliFwWFCLmrMImnr6GgFYAmoFQ0wOYrgAW4j/Mj5P9pl7bUtX4Lyui+vt0 19 | Nai617RBzyBIubupPRIrWYAoyrRsstp6EL3D1PXF5QVy3L1DiM+fc/+TI174HLyy 20 | LfdtUClUtvdNYgosB5BAYqSMlxtSM50fvw8+/KG+xnd4UE6ruF0m+Ml9RB2p9XHT 21 | pLKhADEA 22 | -----END PKCS7----- 23 | -------------------------------------------------------------------------------- /.golangci.yaml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | linters: 3 | default: none 4 | enable: 5 | - errcheck 6 | - goheader 7 | - govet 8 | - ineffassign 9 | - staticcheck 10 | - unused 11 | settings: 12 | goheader: 13 | template: |- 14 | Copyright {{ MOD_YEAR }} Block, Inc. 15 | SPDX-License-Identifier: Apache-2.0 16 | 17 | Licensed under the Apache License, Version 2.0 (the "License"); 18 | you may not use this file except in compliance with the License. 19 | You may obtain a copy of the License at 20 | 21 | http://www.apache.org/licenses/LICENSE-2.0 22 | 23 | Unless required by applicable law or agreed to in writing, software 24 | distributed under the License is distributed on an "AS IS" BASIS, 25 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 26 | See the License for the specific language governing permissions and 27 | limitations under the License. 28 | exclusions: 29 | generated: lax 30 | presets: 31 | - comments 32 | - common-false-positives 33 | - legacy 34 | - std-error-handling 35 | paths: 36 | - starttls/ldap/ 37 | - starttls/mysql/ 38 | - starttls/psql/ 39 | formatters: 40 | exclusions: 41 | generated: lax 42 | paths: 43 | - starttls/ldap/ 44 | - starttls/mysql/ 45 | - starttls/psql/ 46 | -------------------------------------------------------------------------------- /tests/dump-csr-to-text.t: -------------------------------------------------------------------------------- 1 | Set up test data. 2 | 3 | $ cat > example-leaf.csr < -----BEGIN CERTIFICATE REQUEST----- 5 | > MIICmjCCAYICAQAwVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQK 6 | > EwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRUwEwYDVQQDEwxleGFtcGxlLWxl 7 | > YWYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7stSvfQyGuHw3v34f 8 | > isqIdDXberrFoFk9ht/WdXgYzX2uLNKdsR/J5sbWSl8K/5djpzj31eIzqU69w8v7 9 | > SChM5x9bouDsABHz3kZucx5cSafEgJojysBkcrq3VY+aJanzbL+qErYX+lhRpPcZ 10 | > K6JMWIwar8Y3B2la4yWwieecw2/WfEVvG0M/DOYKnR8QHFsfl3US1dnBM84czKPy 11 | > t9r40gDk2XiH/lGts5a94rAGvbr8IMCtq0mA5aH3Fx3mDSi3+4MZwygCAHrF5O5i 12 | > SV9rEI+m2+7j2S+jHDUnvV+nqcpb9m6ENECnYX8FD2KcqlOjTmw8smDy09N2Np6i 13 | > 464lAgMBAAGgADANBgkqhkiG9w0BAQUFAAOCAQEAZW13CST8BtPCINS0CiIv9BMv 14 | > zXpkCRz3riPvrPkllnOY3Dp0NQzQkdj3aE4at5GSN9fOTWCQ0tGnjOLAZ8tqHcyg 15 | > FLgU3MjDcsRvyeQ8mYpCqeUbwq/nHIs33jM/x087lTP7aNXGH4sncxZdIv71+sqF 16 | > f4WnumxsJUARaeb0AnUZmtAC/OR+9vpiUw+wMMhMbDNCboKYANqnFhWkTKp5/85f 17 | > eC21haSG55pT7bGvlG9WNawgXJ3WX48yw29dSyDKd/buVM5Andrp7hYVuC57wz0u 18 | > wng/cxCCQrENS4qSvxOgFiLK2j1LHccMuChPFFGyOyXqBNs9pr8F4/2qPJ7tOw== 19 | > -----END CERTIFICATE REQUEST----- 20 | > EOF 21 | 22 | Dump an example certificate request (example-leaf.csr) 23 | 24 | $ certigo --verbose dump example-leaf.csr 25 | warning: certificate requests are not supported 26 | warning: no certificates found in input 27 | 28 | -------------------------------------------------------------------------------- /test-certs/example-leaf.p7b: -------------------------------------------------------------------------------- 1 | -----BEGIN PKCS7----- 2 | MIIDrQYJKoZIhvcNAQcCoIIDnjCCA5oCAQExADALBgkqhkiG9w0BBwGgggOAMIID 3 | fDCCAmSgAwIBAgIJANWAkzF7PA8/MA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNVBAYT 4 | AlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMHZXhh 5 | bXBsZTEVMBMGA1UEAxMMZXhhbXBsZS1sZWFmMB4XDTE2MDYxMDIyMTQxMVoXDTIz 6 | MDQxNTIyMTQxMVowVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQK 7 | EwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRUwEwYDVQQDEwxleGFtcGxlLWxl 8 | YWYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7stSvfQyGuHw3v34f 9 | isqIdDXberrFoFk9ht/WdXgYzX2uLNKdsR/J5sbWSl8K/5djpzj31eIzqU69w8v7 10 | SChM5x9bouDsABHz3kZucx5cSafEgJojysBkcrq3VY+aJanzbL+qErYX+lhRpPcZ 11 | K6JMWIwar8Y3B2la4yWwieecw2/WfEVvG0M/DOYKnR8QHFsfl3US1dnBM84czKPy 12 | t9r40gDk2XiH/lGts5a94rAGvbr8IMCtq0mA5aH3Fx3mDSi3+4MZwygCAHrF5O5i 13 | SV9rEI+m2+7j2S+jHDUnvV+nqcpb9m6ENECnYX8FD2KcqlOjTmw8smDy09N2Np6i 14 | 464lAgMBAAGjTzBNMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAsBgNV 15 | HREEJTAjhwR/AAABhxAAAAAAAAAAAAAAAAAAAAABgglsb2NhbGhvc3QwDQYJKoZI 16 | hvcNAQELBQADggEBAGM4aa/qrURUweZBIwZYv8O9b2+r4l0HjGAh982/B9sMlM05 17 | kojyDCUGvj86z18Lm8mKr4/y+i0nJ+vDIksEvfDuzw5ALAXGcBzPJKtICUf7LstA 18 | /n9NNpshWz0kld9ylnB5mbUzSFDncVyeXkEf5sGQXdIIZT9ChRBoiloSaa7dvBVC 19 | csX1LGP2LWqKtD+7nUnw5qCwtyAVT8pthEUxFTpywoiJS5ZdzeEx8MNGvUeLFj2k 20 | leqPF78EioEQlSOxViCuctEtnQuPcDLHNFr10byTZY9roObiqdsJLMVvb2XliJjA 21 | qaPa9AkYwGE6xHw2ispwg64Rse0+AtKups19WIWhADEA 22 | -----END PKCS7----- 23 | -------------------------------------------------------------------------------- /test-certs/example-md5.p7b: -------------------------------------------------------------------------------- 1 | -----BEGIN PKCS7----- 2 | MIIDqwYJKoZIhvcNAQcCoIIDnDCCA5gCAQExADALBgkqhkiG9w0BBwGgggN+MIID 3 | ejCCAmKgAwIBAgIJAIx/wkxu2k9LMA0GCSqGSIb3DQEBBAUAMFQxCzAJBgNVBAYT 4 | AlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMHZXhh 5 | bXBsZTEUMBIGA1UEAxMLZXhhbXBsZS1tZDUwHhcNMTYwNjEwMjIxNDEyWhcNMjMw 6 | NDE1MjIxNDEyWjBUMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEDAOBgNVBAoT 7 | B2NlcnRpZ28xEDAOBgNVBAsTB2V4YW1wbGUxFDASBgNVBAMTC2V4YW1wbGUtbWQ1 8 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApQFCsYnp/odw6Y9EvNF6 9 | z1xmpiQTiJpBI5DAEhhRqDuETRtpiSd34KHlwIpriGccwKs8/suRLqpHgmYI2oN4 10 | 42/yJroH96M0WlRFn3rT33Vbh0tpo27y5fsFw7QlQHfQFyZtaRbFyloWh2Ww0XpH 11 | w5Lvk9qZt2bHYssgErlb34g1zqYg9197/zoJS/1eCPFgHo/J9mfDSohQS8G1+lR4 12 | iuzc3Fv3xJJvbJ2MmFjCiv/+ItjlJqUhB0RE86kWvpf5QF8ix5mRN0ZT9TMyRaFo 13 | JdyjLLD2Le3H8ffz8HbB0hhlqd/CNd/hBjV9MmFN9iswV3ML5gJBZAWC7XRlZZpa 14 | JQIDAQABo08wTTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwLAYDVR0R 15 | BCUwI4cEfwAAAYcQAAAAAAAAAAAAAAAAAAAAAYIJbG9jYWxob3N0MA0GCSqGSIb3 16 | DQEBBAUAA4IBAQBYKtQ+QvaGZvDwJ1enDvI95/OQXn3Up8/7I6CAV9wbFq7ea5gI 17 | p8tPSdu2qzf0jEp3qadWZUTqL/2vffXrsy2simjCEQQ8tgkDJHhjQhOgSmxmIV80 18 | F6mpfSeLtEbNcpobDe9PlNhXCf5mCSWC4N7Us29hMaCYyPocT50UMp9g6PrTJNDj 19 | uh2AovI38QUgDNk0ZnjfMNuFERae/gN8X9Kin0kx5dncsuF4cvaGuOv/C90hXIga 20 | 1dTuOXQtac48f4TUc8mTq52mSwDceIePYEK1+5UebvhWbwJz4oxkmVFmLhTe8qY6 21 | j9Lv+cc5dr3nNMI2CoJj2h3E5G6I371GC0VvoQAxAA== 22 | -----END PKCS7----- 23 | -------------------------------------------------------------------------------- /test-certs/example-sha1.p7b: -------------------------------------------------------------------------------- 1 | -----BEGIN PKCS7----- 2 | MIIDrQYJKoZIhvcNAQcCoIIDnjCCA5oCAQExADALBgkqhkiG9w0BBwGgggOAMIID 3 | fDCCAmSgAwIBAgIJAKmeSDe4ln5zMA0GCSqGSIb3DQEBBQUAMFUxCzAJBgNVBAYT 4 | AlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMHZXhh 5 | bXBsZTEVMBMGA1UEAxMMZXhhbXBsZS1zaGExMB4XDTE2MDYxMDIyMTQxMVoXDTIz 6 | MDQxNTIyMTQxMVowVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQK 7 | EwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRUwEwYDVQQDEwxleGFtcGxlLXNo 8 | YTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCUuAbvyfPJsBEr41hy 9 | eAX26jhZJWVbioCveouRbgfO9JQOdHxKRNJtWiI2+rVwUfIRxF9qO/wsNeRDHjsb 10 | W5KD4prsp8RNLZJqVKm171XwCCUSDeiHTUJfTzsMiV2PwwbzQOK41m0uywtrhEUL 11 | cW9+Z+UZ7wnE6+NlU9aLNGEZ94hh3BsnKip/pGHGsIh14vaXE4M+OTJvXkUs/6/d 12 | L2yBdiZiw9bqv1GIU3vliI5h28tjB118duwf7ZMqxoRQ32wsUmskNMN/S0OLoS/9 13 | BTNyGH2vG/juZnt//Wh35563cun2Qp0va8WzNTRrqRtULfn/+5CwaswnutervIu9 14 | 9xnxAgMBAAGjTzBNMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAsBgNV 15 | HREEJTAjhwR/AAABhxAAAAAAAAAAAAAAAAAAAAABgglsb2NhbGhvc3QwDQYJKoZI 16 | hvcNAQEFBQADggEBADZJcu8Rb/ehLSlv5cf0D7zlAAyGtnfE0yB8rNi8T1RZaoKO 17 | 9mbg3YoStT0mmqZB/K3v0SAaFMe3UFZK870MrjL5VLjj/+X/djW+ENiXpCCpAo/o 18 | K9WYG3EPDmz6AtSermlAJ1Ae7kaKbJY9SwZwM4QMz9xMR8tNKuo+i/NiMHtmKgkN 19 | PMxNjzdOFWQ8rBMfV2ZYWP8Z6R97VuCMGDnA1Hy6/95IPzvh7KfgIy+0jua9Fo8a 20 | q8RPkpEgqkvhciFzn2lw2AroQLUfAxQ7nSF35aCbDCnN0bK4sd9Y0zRU53/fclEI 21 | dVP9l8nN4DBMRvwCSIQbHaDxM/6PYVTDDdpWqnChADEA 22 | -----END PKCS7----- 23 | -------------------------------------------------------------------------------- /test-certs/example-bad-serial.p7b: -------------------------------------------------------------------------------- 1 | -----BEGIN PKCS7----- 2 | MIIDsQYJKoZIhvcNAQcCoIIDojCCA54CAQExADALBgkqhkiG9w0BBwGgggOEMIID 3 | gDCCAmigAwIBAgIBADANBgkqhkiG9w0BAQsFADBbMQswCQYDVQQGEwJVUzELMAkG 4 | A1UECBMCQ0ExEDAOBgNVBAoTB2NlcnRpZ28xEDAOBgNVBAsTB2V4YW1wbGUxGzAZ 5 | BgNVBAMTEmV4YW1wbGUtYmFkLXNlcmlhbDAeFw0xNjA2MTAyMjE0MTJaFw0yMzA0 6 | MTUyMjE0MTJaMFsxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMH 7 | Y2VydGlnbzEQMA4GA1UECxMHZXhhbXBsZTEbMBkGA1UEAxMSZXhhbXBsZS1iYWQt 8 | c2VyaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuRyPfk/VjoHu 9 | Wn7V4aSyuzXclHANdIWnuxR/mW07jDoHlju8dqEiMFl1DM7wHPSCk4m/vYNKN8g5 10 | yRrDTLmG2LZi3gbRv+0zy1/gLGH75D+4nlOAB+HAkOtHBFnxewcSKMmG2IpHO7vr 11 | 2AtnIk7rRTSWkAITMJeIyO9Lzyi/RvGmjBoCf4DZtYe7mh+YC0XG81IlmR4ClyXd 12 | BJ8O0QdeIDEE0egFYsku+4p2Hgt4obYm8W8NbTMsoxaEWhvA2SnSMx+e2UW2Co+t 13 | sV+BjfCwLqPXOr+awKMPk22n56Ev3aG6h6gzXOHy/YOVGYp3xIFY6yQwApICEJtG 14 | HtVYZ0+B5QIDAQABo08wTTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEw 15 | LAYDVR0RBCUwI4cEfwAAAYcQAAAAAAAAAAAAAAAAAAAAAYIJbG9jYWxob3N0MA0G 16 | CSqGSIb3DQEBCwUAA4IBAQB6zkdwhzIOelObaUFZFiZ9eFqaZdKr74CWvTBcK7mm 17 | ryX0CdXDJS+iKgwpT+gZP36x4nahh0YEaU0XDjm8Zb8GiYeDJKei1vsSRbGC62J8 18 | n6ZQVcxcMYX0wuiwfXgtxlt9Xpdgf52S+PA9X293siCAlaIDfNUD386kBtDaU90Q 19 | 82HfLXmaMTfb1z+6blEnxvOGOHe0XaRIJRV6kUP3YvBbC+jW/5fj89lr35UB6sI4 20 | pLvbGdV88E3Rw3TAsFKIJa8nOrzLvnICBdwfN76JyovH5t3n3txp2RIdGilwJWDA 21 | ZZJA838IXbIrUbNiH3ZugzA7KJjyn/hA2igKYszJ9lNGoQAxAA== 22 | -----END PKCS7----- 23 | -------------------------------------------------------------------------------- /jceks/pkcs5.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package jceks 17 | 18 | import ( 19 | "bytes" 20 | "crypto/des" 21 | "crypto/subtle" 22 | ) 23 | 24 | func pkcs5Pad(ciphertext []byte) []byte { 25 | pad := byte(des.BlockSize - len(ciphertext)%des.BlockSize) 26 | 27 | return append(ciphertext, bytes.Repeat([]byte{pad}, int(pad))...) 28 | } 29 | 30 | func pkcs5Unpad(ciphertext []byte) ([]byte, error) { 31 | if len(ciphertext) < des.BlockSize { 32 | return nil, ErrInvalidCiphertext 33 | } 34 | 35 | pad := ciphertext[len(ciphertext)-1] 36 | if pad > des.BlockSize { 37 | return nil, ErrInvalidCiphertext 38 | } 39 | if subtle.ConstantTimeCompare(ciphertext[len(ciphertext)-int(pad):], bytes.Repeat([]byte{pad}, int(pad))) != 1 { 40 | return nil, ErrInvalidCiphertext 41 | } 42 | 43 | return ciphertext[:len(ciphertext)-int(pad)], nil 44 | } 45 | -------------------------------------------------------------------------------- /test-certs/example-name-constraints.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID1DCCArygAwIBAgIJAL8xOGAOaSzBMA0GCSqGSIb3DQEBCwUAMGExCzAJBgNV 3 | BAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMH 4 | ZXhhbXBsZTEhMB8GA1UEAxMYZXhhbXBsZS1uYW1lLWNvbnN0cmFpbnRzMB4XDTE3 5 | MDgxODE5NDg1MFoXDTI0MDYyMjE5NDg1MFowYTELMAkGA1UEBhMCVVMxCzAJBgNV 6 | BAgTAkNBMRAwDgYDVQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMSEwHwYD 7 | VQQDExhleGFtcGxlLW5hbWUtY29uc3RyYWludHMwggEiMA0GCSqGSIb3DQEBAQUA 8 | A4IBDwAwggEKAoIBAQDkrrryvC1a63Vc2yCBb0SliieU6TnqZAQ5rJzAa4zf7uq9 9 | obwLuGfZV5j/7nYl04M2gPiz4Rk8uia14tIqBjFjB5DH7czuaPiGKgMSKMXt9lPZ 10 | xu3ZnRLRmF+I/zlIjxqLU9bxem7Qs7RBoncbbd65gy8PPe2J09rJdrcj5V/hD2jZ 11 | pNWtrYA6ANGsMSxLchtpn3LKyDy/bW2HIhXmqVIq/2j2p2ljKf6E63L4S7e7FW8E 12 | MGiW9y+DLO5U7qSZUiqgAiXHjTKWtYgbnlqboPtZRV5quN2PE0KD3FOLhAQX5MKS 13 | 3P48+cjeswmruNVc8qjdCi1NLsRCTHukM/WxAC35AgMBAAGjgY4wgYswDgYDVR0P 14 | AQH/BAQDAgIEMBIGA1UdEwEB/wQIMAYBAf8CAQAwZQYDVR0eBF4wXKAsMA6CDC5l 15 | eGFtcGxlLmNvbTAKhwjAqAAA//8AADAOgQwuZXhhbXBsZS5jb22hLDAOggwuZXhh 16 | bXBsZS5vcmcwCocICgoAAP//AAAwDoEMLmV4YW1wbGUub3JnMA0GCSqGSIb3DQEB 17 | CwUAA4IBAQBDxhzTZc0fB7B2EvHmzz45J3xgk/aQksKFXeWxyX7UdgSY+gzABRMk 18 | txHCMZtdTJxi4BACpRlO4QMbJXSiofhGJQb+ThSvM1CQv+W5X4j7/40RBfRnWbQP 19 | ZGfxSyGhLYAIzUsHi6X7DbJmzV2EixPbYKCJ+5Vlf7IH0aDexzf4xKLQdy+cWy1g 20 | Y4bHiiQbVqfTjlzdsMuiFqoZe3/NhOP93XSMZErx6pJ/lrWiO+6ytM3gdwh1y0DE 21 | tFmaBKzI+2uQwt6DHQuPmOSEmI7NcrVS2WhPVPa7fIY/ExUy7jqIr2qM59eSl7OC 22 | AhzgLtgqfwLgbj5PWZENYlNRXmZGi4GD 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /test-certs/example-name-constraints.p7b: -------------------------------------------------------------------------------- 1 | -----BEGIN PKCS7----- 2 | MIIEBQYJKoZIhvcNAQcCoIID9jCCA/ICAQExADALBgkqhkiG9w0BBwGgggPYMIID 3 | 1DCCArygAwIBAgIJAL8xOGAOaSzBMA0GCSqGSIb3DQEBCwUAMGExCzAJBgNVBAYT 4 | AlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMHZXhh 5 | bXBsZTEhMB8GA1UEAxMYZXhhbXBsZS1uYW1lLWNvbnN0cmFpbnRzMB4XDTE3MDgx 6 | ODE5NDg1MFoXDTI0MDYyMjE5NDg1MFowYTELMAkGA1UEBhMCVVMxCzAJBgNVBAgT 7 | AkNBMRAwDgYDVQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMSEwHwYDVQQD 8 | ExhleGFtcGxlLW5hbWUtY29uc3RyYWludHMwggEiMA0GCSqGSIb3DQEBAQUAA4IB 9 | DwAwggEKAoIBAQDkrrryvC1a63Vc2yCBb0SliieU6TnqZAQ5rJzAa4zf7uq9obwL 10 | uGfZV5j/7nYl04M2gPiz4Rk8uia14tIqBjFjB5DH7czuaPiGKgMSKMXt9lPZxu3Z 11 | nRLRmF+I/zlIjxqLU9bxem7Qs7RBoncbbd65gy8PPe2J09rJdrcj5V/hD2jZpNWt 12 | rYA6ANGsMSxLchtpn3LKyDy/bW2HIhXmqVIq/2j2p2ljKf6E63L4S7e7FW8EMGiW 13 | 9y+DLO5U7qSZUiqgAiXHjTKWtYgbnlqboPtZRV5quN2PE0KD3FOLhAQX5MKS3P48 14 | +cjeswmruNVc8qjdCi1NLsRCTHukM/WxAC35AgMBAAGjgY4wgYswDgYDVR0PAQH/ 15 | BAQDAgIEMBIGA1UdEwEB/wQIMAYBAf8CAQAwZQYDVR0eBF4wXKAsMA6CDC5leGFt 16 | cGxlLmNvbTAKhwjAqAAA//8AADAOgQwuZXhhbXBsZS5jb22hLDAOggwuZXhhbXBs 17 | ZS5vcmcwCocICgoAAP//AAAwDoEMLmV4YW1wbGUub3JnMA0GCSqGSIb3DQEBCwUA 18 | A4IBAQBDxhzTZc0fB7B2EvHmzz45J3xgk/aQksKFXeWxyX7UdgSY+gzABRMktxHC 19 | MZtdTJxi4BACpRlO4QMbJXSiofhGJQb+ThSvM1CQv+W5X4j7/40RBfRnWbQPZGfx 20 | SyGhLYAIzUsHi6X7DbJmzV2EixPbYKCJ+5Vlf7IH0aDexzf4xKLQdy+cWy1gY4bH 21 | iiQbVqfTjlzdsMuiFqoZe3/NhOP93XSMZErx6pJ/lrWiO+6ytM3gdwh1y0DEtFma 22 | BKzI+2uQwt6DHQuPmOSEmI7NcrVS2WhPVPa7fIY/ExUy7jqIr2qM59eSl7OCAhzg 23 | LtgqfwLgbj5PWZENYlNRXmZGi4GDoQAxAA== 24 | -----END PKCS7----- 25 | -------------------------------------------------------------------------------- /cli/terminal/testing.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package terminal 17 | 18 | import ( 19 | "bytes" 20 | "io" 21 | 22 | "github.com/mattn/go-colorable" 23 | ) 24 | 25 | // TestTerminal just collects input into buffers 26 | // That can be used to check output in tests 27 | type TestTerminal struct { 28 | OutputBuf bytes.Buffer 29 | ErrorBuf bytes.Buffer 30 | Password string 31 | Width int 32 | } 33 | 34 | var _ Terminal = &TestTerminal{} 35 | 36 | func (t *TestTerminal) Output() io.Writer { 37 | return colorable.NewNonColorable(&t.OutputBuf) 38 | } 39 | 40 | func (t *TestTerminal) Error() io.Writer { 41 | return &t.ErrorBuf 42 | } 43 | 44 | func (t *TestTerminal) SetDefaultPassword(password string) { 45 | t.Password = password 46 | } 47 | 48 | func (t *TestTerminal) ReadPassword(prompt string) string { 49 | return t.Password 50 | } 51 | 52 | func (t TestTerminal) DetermineWidth() int { 53 | return t.Width 54 | } 55 | -------------------------------------------------------------------------------- /starttls/psql/oid/gen.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | // Generate the table of OID values 4 | // Run with 'go run gen.go'. 5 | package main 6 | 7 | import ( 8 | "database/sql" 9 | "fmt" 10 | "log" 11 | "os" 12 | "os/exec" 13 | 14 | _ "github.com/lib/pq" 15 | ) 16 | 17 | func main() { 18 | datname := os.Getenv("PGDATABASE") 19 | sslmode := os.Getenv("PGSSLMODE") 20 | 21 | if datname == "" { 22 | os.Setenv("PGDATABASE", "pqgotest") 23 | } 24 | 25 | if sslmode == "" { 26 | os.Setenv("PGSSLMODE", "disable") 27 | } 28 | 29 | db, err := sql.Open("postgres", "") 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | cmd := exec.Command("gofmt") 34 | cmd.Stderr = os.Stderr 35 | w, err := cmd.StdinPipe() 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | f, err := os.Create("types.go") 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | cmd.Stdout = f 44 | err = cmd.Start() 45 | if err != nil { 46 | log.Fatal(err) 47 | } 48 | fmt.Fprintln(w, "// generated by 'go run gen.go'; do not edit") 49 | fmt.Fprintln(w, "\npackage oid") 50 | fmt.Fprintln(w, "const (") 51 | rows, err := db.Query(` 52 | SELECT typname, oid 53 | FROM pg_type WHERE oid < 10000 54 | ORDER BY oid; 55 | `) 56 | if err != nil { 57 | log.Fatal(err) 58 | } 59 | var name string 60 | var oid int 61 | for rows.Next() { 62 | err = rows.Scan(&name, &oid) 63 | if err != nil { 64 | log.Fatal(err) 65 | } 66 | fmt.Fprintf(w, "T_%s Oid = %d\n", name, oid) 67 | } 68 | if err = rows.Err(); err != nil { 69 | log.Fatal(err) 70 | } 71 | fmt.Fprintln(w, ")") 72 | w.Close() 73 | cmd.Wait() 74 | } 75 | -------------------------------------------------------------------------------- /test-certs/example-custom-oid.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAxyNyxFGSp5VDHOHMpOB23b1eeKFGs1GexrajYpVkZA5xq8ka 3 | SiyMPXEb38jybh2vTiH/YxzTqDpUfrnBZYnlQpx4GSzTT9+0Vwb0jmKitd/lZBA8 4 | zvfL3Ni2Ym0TpZQGHp/QyaVOvvqDiYjbrN+WJay5usHUxFDxFMP1NjyWCT9QKZPr 5 | divKtgFMQzCCoBkic33mKvSfi+3nd57ol83BJgjIvEZFkVg4R4/fSGWe3DW836rW 6 | TJOxj/FIDgGcPy1eWInQBHmF77CYTGuuVMMrGnV+brc4M4FlBUnQv8sy8S6XZBrf 7 | G1Z2npYHkvKCZM4LUOmKmpGGFACAlvmrBolIYwIDAQABAoIBAFbQmmkB0viiNtbx 8 | jYDXppp2wye9z4+/ZoPk/Yet7007iszEaGSr1Ru4ItnFxgLBiDp80dTIrXFqtIlm 9 | u+xjWIJYqnb3Nsxcj0ddbeJhF+RITr334jqshsCp7aEtQmJuDNju/VvbaGAT4KJl 10 | +rb75n5YNcQ4WmSXkPJnl5Bki1hN75zjlnVlD+FV+/j0hz2Kg7mAQkJqbLn7Cd/4 11 | 398qYABak0aAA2ZH2DeybZsZGstka2yIypQsJVQNYvxWcxCNweWMOljRPfxX50Oa 12 | ZIuuO70VQlXmIZYHruvqpImEOSWQtiVm2lTR+jVR6wof/oWOWkYqefoAQdLJy1ud 13 | FgEFUXkCgYEA7sSMOVM6pvz+ALz3aKFCqUYe+yA3HBkCUDF8ESVNnl9OMQB5mXRg 14 | J6kxr8z/Pu8P8J9Np87hBZPSJe7rFavfhjgxJ1rnrg73Btq4+TZ85qby3H57Ybnm 15 | SVohAWOQfEaPt9wD4AIy8p/tKBaWoNnBwuYBux52WX0iO2nZIgTA+W0CgYEA1YK2 16 | k55Rfo6WfUWCK3CyR+6kGfphxPY3GWkuckTh3M8cQvFzQEZ6p4eBPBxxZ9VtogXR 17 | CP5AXzHZYNhH9s9djmiZh/m0MHGKILQD3feD0dtypySQ0gCh+NOCxAProB2gDK5C 18 | kCOJ5x45nujiPcz8mqq/qvWBQjEaOunL8ivNdw8CgYBS8Ek5PHT6PpVI0lsqqCGM 19 | xUPLvcQG6ZUJIDdViVyt77MsiFq0+FzSSg5tX8smJEqoLd3+tXkqw89Sx+w3Ke3K 20 | JMjGP+iMaLxcUCvCva+jO0jPWMwpBTc+MaWXYut0j9oqZq2d6YWjC/SIYVhPZXTN 21 | 67GcOxenPDVg6AK5s2cI/QKBgQCD5C8smUv2Rdaoo9t5QjCfRseQVJ84sG/w/ox2 22 | gh6T7Eyk/coYFnGGM6qdm2Rio/EJeVZbm9Pf+kcyD+jEiexdIDhXSuUIV9rfwNPy 23 | tgd0B3+XyHq9fy6PKvlIZLOVQTexHaR4rbtuTbmCdJHtY0dXs8qarPWgEf0GD46w 24 | lmZh+QKBgQDPPfnORduMYFTLmcqHBDIyK4X4Y4bZfsEJkoyRbPRP2YSTuliFzuUe 25 | qBDOPcy9JcDg+jAmTmPeaEKK3Ad516QpS4uk7u0n5AE3UTdiC7ICs4+VmGWOO2T4 26 | mQP1CdSxBEsYap5ZT1szVwRllInSHLqCeKDSI6LD+0AzA2biBaRmaw== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /jceks/testdata/private-key.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCmH1a2ZlglMV9P 3 | Jn0VpVSo9GoIDlncCpia/r5+NdYyglTXxytlT6kgfD7MM5xxKLVLbQGsEyPT2qyf 4 | JyAtTGh7v3orWN5e7k+0s8c+wo6Q3FuhXFlfA44fZY3bUVqKBpM5sGijRFPrByY7 5 | 4e+cFm+p1ipe/HG+CYRMJgvAYj6LhvovDLN3xzB/Q5vr2x9FOGBN8wVeH7Pxu29+ 6 | aSXltWzpvgclYZnOgMqhYxjQ45/FGBcO5lM9ReUcvOx23bb8DaT2CBAIZMkabFnr 7 | lijRxypVO5E5v8GnOOzzQEcrdYYi33d3W90s0YOG9bS7sVeUnfl6dikwhCblXXir 8 | KcK9BSnXAgMBAAECggEACjqYLBQ/NAMUDrUC/+5o0rWodb7uEsBNFA/AvdeMAE4w 9 | JTbS829yE/L/XUy5D4RL+k0uMvp2uuVjIOWkc8WZq80AtWL8ExvyqODNBI58TGuV 10 | JM/Uto03sIBs7LOksvNwS1DgEI8oT6qzOv3qTOuNjDqiUnj4emG7O+keN7lhuohh 11 | ERCHw9jzfBg/sHDQS30gT4omk67+UMjaKoQRprA9lkgvJyk2J1PFOb6kuICt/+LX 12 | O9AQlGEixE6RtKNjPboqA1nNIOEG8pgUJWK+Cemwfc59DNMKhhNmnrHQwKjU2QiN 13 | OLV5lzq9Hh7wz5/47dz43qyYiZ8HOt8UeqKeKsMz+QKBgQDgPMJoFDu3VT8YA+BS 14 | we4QXWTbEcMXeU40r0cSw65Zguogfot78osyRebjTcPX3Q0YVv1p6i+3E7wdfKju 15 | PoIJfYXQICnCa9y7CRJC/5FWPHwSu5AovI/ddNFWFAZvPnbw3NGOy94OnBejZdmO 16 | ufx9tdJgGOcaS16zDFqBytJkjQKBgQC9pzraBolYl4ROkvPp3aAeis8Edhsxg67H 17 | D6XdNnyy5CIoFfHofGM7kJdyBfj2cnaeBZ2l/PFDEZWIeKVh7/DLrUAt/+vdosKA 18 | jp5TH04tC0zh5Nyfe0Ed4C1hoHZXCYUYVLaxtEaTyGqm4zkgrRaR574WHkbVPR05 19 | waGXKQyY8wKBgQDURpCfaLATYT7mZB0DGxVcJp2oiUsPuMVhOKDm0ZoGoNk1Q7aV 20 | 8sj1IZluixqgCmIwLYGet5TOEn+dxnpBd6kwhMSdfyUpKWT/JppEFJ4obf/i1+6j 21 | scXblo/plfF1dEOyxsgXD0W6vOffxjZLWCP/eZWbZVe526ndhA9WGYRMrQKBgQC0 22 | 90iB2DDgVxvJehHCe1aZLiyhfi0ujw+MJJNJRP+yF1u8UkxVYbOyl6LbC3Xne4tR 23 | 99vZEkYIkFPWOADfNfvX7cZK3mPrRa5Ay1WpzTpkYt8GwcH/b7XP0ySafmnNXWkh 24 | sVvury2VQGz8N99AHclmifHo2xUNzlX/0ZLmUSYTHwKBgFzWENt+IWn4lVStAeMv 25 | fxvIXrDGvkv7QbA8ZzxoIlR1eb1yboojkndpQjNUYG7BUfGvVtOvUJIECXr4LYRk 26 | VGHoDVPtKG69kAdvdSUcNcdl0GT6pxW4BuhRRoGaqlNbX1xAnptq/ixvHdJ2GPmv 27 | j2Q+WrIx2dhqrzlGjqxzC+/J 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /tests/dump-small-key-to-text.t: -------------------------------------------------------------------------------- 1 | Set up test data. 2 | 3 | $ cat > example-small-key.crt < -----BEGIN CERTIFICATE----- 5 | > MIICKzCCAZQCCQDHlr/u+lfb8jANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJV 6 | > UzELMAkGA1UECBMCQ0ExEDAOBgNVBAoTB2NlcnRpZ28xEDAOBgNVBAsTB2V4YW1w 7 | > bGUxGjAYBgNVBAMTEWV4YW1wbGUtc21hbGwta2V5MB4XDTE2MDYxMDIyMTQxMloX 8 | > DTIzMDQxNTIyMTQxMlowWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYD 9 | > VQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRowGAYDVQQDExFleGFtcGxl 10 | > LXNtYWxsLWtleTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAlzyCIeP1T87k 11 | > 1rHVMtbaGXIWpK/VQuvuXwig+e3ct1ajA4bw0BAInXZ37FEGGSCUix0k/CjH2Nlt 12 | > GREtwbahE0k5oTkVbA5XS4xkNs0M0poAFN5OiFKEAqZ014hqhvKnEUQ2oTe9SVOR 13 | > Ww49mLNg36AIEE2Fu2KQb/VT90cwwD0CAwEAATANBgkqhkiG9w0BAQsFAAOBgQBV 14 | > sJ4Vb2L1ywLVeAxNqY0PZqS7a8Q2GLhNr5V+3hOoWn7bwqQ7L06UJGSrcLOPZeIH 15 | > IWM20aOFHSTWbocd4f+m6s3llyXwBBlK2BPZbWv0OeAHgjN9AVav4flAZ4oD2GxA 16 | > aJkGAXmR9QzZNJLai5mv3L/B/p/NxeU3UGfaySxVvw== 17 | > -----END CERTIFICATE----- 18 | > EOF 19 | 20 | Dump an example certificate (example-leaf.crt) 21 | 22 | $ certigo --verbose dump example-small-key.crt 23 | ** CERTIFICATE 1 ** 24 | Input Format: PEM 25 | Serial: 14381893493177441266 26 | Valid: 2016-06-10 22:14 UTC to 2023-04-15 22:14 UTC 27 | Signature: SHA256-RSA (self-signed) 28 | Subject Info: 29 | \tCountry: US (esc) 30 | \tProvince: CA (esc) 31 | \tOrganization: certigo (esc) 32 | \tOrganizational Unit: example (esc) 33 | \tCommonName: example-small-key (esc) 34 | Issuer Info: 35 | \tCountry: US (esc) 36 | \tProvince: CA (esc) 37 | \tOrganization: certigo (esc) 38 | \tOrganizational Unit: example (esc) 39 | \tCommonName: example-small-key (esc) 40 | Lints: 41 | \tERROR: [RFC5280] CAs must include keyIdentifer field of AKI in all non-self-issued certificates (esc) 42 | \tWARN: [RFC5280] Sub certificates SHOULD include Subject Key Identifier in end entity certs (esc) 43 | -------------------------------------------------------------------------------- /starttls/imap.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package starttls 17 | 18 | import ( 19 | "bufio" 20 | "crypto/tls" 21 | "fmt" 22 | "net" 23 | ) 24 | 25 | func dumpTLSConnStateFromIMAP(dialer Dialer, address string, config *tls.Config) (*tls.ConnectionState, error) { 26 | c, err := dialer.Dial("tcp", address) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | conn := c.(*net.TCPConn) 32 | status, err := readIMAP(conn) 33 | if err != nil { 34 | return nil, err 35 | } 36 | if status != "OK" { 37 | return nil, fmt.Errorf("IMAP server responded with %s, was expecting OK", status) 38 | } 39 | 40 | fmt.Fprintf(conn, "1 STARTTLS\r\n") 41 | status, err = readIMAP(conn) 42 | if err != nil { 43 | return nil, err 44 | } 45 | if status != "OK" { 46 | return nil, fmt.Errorf("IMAP server responded with %s, was expecting OK", status) 47 | } 48 | 49 | tlsConn := tls.Client(conn, config) 50 | err = tlsConn.Handshake() 51 | if err != nil { 52 | return nil, err 53 | } 54 | 55 | state := tlsConn.ConnectionState() 56 | return &state, nil 57 | } 58 | 59 | func readIMAP(conn *net.TCPConn) (string, error) { 60 | reader := bufio.NewReader(conn) 61 | response, err := reader.ReadString('\n') 62 | if err != nil { 63 | return "", err 64 | } 65 | return response[2:4], nil 66 | } 67 | -------------------------------------------------------------------------------- /pkcs7/pkcs7_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package pkcs7 17 | 18 | import ( 19 | "encoding/base64" 20 | "testing" 21 | ) 22 | 23 | var testBlock, _ = base64.StdEncoding.DecodeString(` 24 | MIICXAYJKoZIhvcNAQcCoIICTTCCAkkCAQExADALBgkqhkiG9w0BBwGgggIvMIIC 25 | KzCCAZQCCQDHlr/u+lfb8jANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzEL 26 | MAkGA1UECBMCQ0ExEDAOBgNVBAoTB2NlcnRpZ28xEDAOBgNVBAsTB2V4YW1wbGUx 27 | GjAYBgNVBAMTEWV4YW1wbGUtc21hbGwta2V5MB4XDTE2MDYxMDIyMTQxMloXDTIz 28 | MDQxNTIyMTQxMlowWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQK 29 | EwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRowGAYDVQQDExFleGFtcGxlLXNt 30 | YWxsLWtleTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAlzyCIeP1T87k1rHV 31 | MtbaGXIWpK/VQuvuXwig+e3ct1ajA4bw0BAInXZ37FEGGSCUix0k/CjH2NltGREt 32 | wbahE0k5oTkVbA5XS4xkNs0M0poAFN5OiFKEAqZ014hqhvKnEUQ2oTe9SVORWw49 33 | mLNg36AIEE2Fu2KQb/VT90cwwD0CAwEAATANBgkqhkiG9w0BAQsFAAOBgQBVsJ4V 34 | b2L1ywLVeAxNqY0PZqS7a8Q2GLhNr5V+3hOoWn7bwqQ7L06UJGSrcLOPZeIHIWM2 35 | 0aOFHSTWbocd4f+m6s3llyXwBBlK2BPZbWv0OeAHgjN9AVav4flAZ4oD2GxAaJkG 36 | AXmR9QzZNJLai5mv3L/B/p/NxeU3UGfaySxVv6EAMQA=`) 37 | 38 | func TestExtract(t *testing.T) { 39 | certs, err := ExtractCertificates(testBlock) 40 | if err != nil { 41 | t.Fatal(err) 42 | } 43 | if len(certs) != 1 { 44 | t.Fatalf("expected 1 certs, but found %d", len(certs)) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/square/certigo 2 | 3 | go 1.24.4 4 | 5 | require ( 6 | github.com/Masterminds/sprig/v3 v3.3.0 7 | github.com/alecthomas/kingpin/v2 v2.4.0 8 | github.com/fatih/color v1.18.0 9 | github.com/go-ldap/ldap/v3 v3.4.11 10 | github.com/google/certificate-transparency-go v1.3.2 11 | github.com/mattn/go-colorable v0.1.14 12 | github.com/mwitkow/go-http-dialer v0.0.0-20161116154839-378f744fb2b8 13 | github.com/stretchr/testify v1.10.0 14 | github.com/zmap/zcrypto v0.0.0-20240803002437-3a861682ac77 15 | github.com/zmap/zlint/v3 v3.6.5 16 | golang.org/x/crypto v0.40.0 17 | software.sslmate.com/src/go-pkcs12 v0.6.0 18 | ) 19 | 20 | require ( 21 | dario.cat/mergo v1.0.1 // indirect 22 | github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect 23 | github.com/Masterminds/goutils v1.1.1 // indirect 24 | github.com/Masterminds/semver/v3 v3.3.0 // indirect 25 | github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect 26 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 27 | github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect 28 | github.com/google/uuid v1.6.0 // indirect 29 | github.com/huandu/xstrings v1.5.0 // indirect 30 | github.com/mattn/go-isatty v0.0.20 // indirect 31 | github.com/mitchellh/copystructure v1.2.0 // indirect 32 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 33 | github.com/pelletier/go-toml v1.9.5 // indirect 34 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 35 | github.com/shopspring/decimal v1.4.0 // indirect 36 | github.com/spf13/cast v1.7.0 // indirect 37 | github.com/weppos/publicsuffix-go v0.40.3-0.20250708083804-25ff8f86d8b3 // indirect 38 | github.com/xhit/go-str2duration/v2 v2.1.0 // indirect 39 | golang.org/x/net v0.41.0 // indirect 40 | golang.org/x/sys v0.34.0 // indirect 41 | golang.org/x/term v0.33.0 // indirect 42 | golang.org/x/text v0.27.0 // indirect 43 | gopkg.in/yaml.v3 v3.0.1 // indirect 44 | ) 45 | -------------------------------------------------------------------------------- /starttls/psql/url.go: -------------------------------------------------------------------------------- 1 | package pq 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | nurl "net/url" 7 | "sort" 8 | "strings" 9 | ) 10 | 11 | // ParseURL no longer needs to be used by clients of this library since supplying a URL as a 12 | // connection string to sql.Open() is now supported: 13 | // 14 | // sql.Open("postgres", "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full") 15 | // 16 | // It remains exported here for backwards-compatibility. 17 | // 18 | // ParseURL converts a url to a connection string for driver.Open. 19 | // Example: 20 | // 21 | // "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full" 22 | // 23 | // converts to: 24 | // 25 | // "user=bob password=secret host=1.2.3.4 port=5432 dbname=mydb sslmode=verify-full" 26 | // 27 | // A minimal example: 28 | // 29 | // "postgres://" 30 | // 31 | // This will be blank, causing driver.Open to use all of the defaults 32 | func ParseURL(url string) (string, error) { 33 | u, err := nurl.Parse(url) 34 | if err != nil { 35 | return "", err 36 | } 37 | 38 | if u.Scheme != "postgres" && u.Scheme != "postgresql" { 39 | return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme) 40 | } 41 | 42 | var kvs []string 43 | escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`) 44 | accrue := func(k, v string) { 45 | if v != "" { 46 | kvs = append(kvs, k+"="+escaper.Replace(v)) 47 | } 48 | } 49 | 50 | if u.User != nil { 51 | v := u.User.Username() 52 | accrue("user", v) 53 | 54 | v, _ = u.User.Password() 55 | accrue("password", v) 56 | } 57 | 58 | if host, port, err := net.SplitHostPort(u.Host); err != nil { 59 | accrue("host", u.Host) 60 | } else { 61 | accrue("host", host) 62 | accrue("port", port) 63 | } 64 | 65 | if u.Path != "" { 66 | accrue("dbname", u.Path[1:]) 67 | } 68 | 69 | q := u.Query() 70 | for k := range q { 71 | accrue(k, q.Get(k)) 72 | } 73 | 74 | sort.Strings(kvs) // Makes testing easier (not a performance concern) 75 | return strings.Join(kvs, " "), nil 76 | } 77 | -------------------------------------------------------------------------------- /jceks/testdata/generate-jceks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if ! [ -d testdata ]; then 3 | echo "Must be called in parent directory of testdata" >&2 4 | exit 1 5 | fi 6 | 7 | cd testdata 8 | 9 | rm -f "private-key-ca.key" "private-key-ca.crt" "private-key.key" "private-key.csr" "private-key-ca.srl" \ 10 | "private-key.crt" "private-key.p12" "private-key.jceks" 11 | openssl req -x509 -nodes -days 365 -newkey rsa:2048 -subj "/CN=Test CA/O=Test Organization/C=US" -extensions v3_req \ 12 | -keyout "private-key-ca.key" -out "private-key-ca.crt" 13 | openssl genrsa -out "private-key.key" 2048 14 | openssl req -new -key "private-key.key" \ 15 | -subj "/CN=Test User/O=Test Organization/C=US" -subj "/CN=Test User/O=Test Organization/C=US" -extensions v3_req \ 16 | -out "private-key.csr" 17 | openssl x509 -req -in "private-key.csr" -CA "private-key-ca.crt" -CAkey "private-key-ca.key" \ 18 | -CAcreateserial -out "private-key.crt" -days 365 19 | openssl pkcs12 -export -in "private-key.crt" -certfile "private-key-ca.crt" -inkey "private-key.key" \ 20 | -name "private-key-some-alias" -out "private-key.p12" -passout "pass:store-password" 21 | keytool -importkeystore -alias "private-key-some-alias" \ 22 | -srckeystore "private-key.p12" -srcstoretype PKCS12 -srcstorepass "store-password" \ 23 | -destkeystore "private-key.jceks" -storetype JCEKS -deststorepass "store-password" -destkeypass "key-password" 24 | rm -f "private-key-ca.key" "private-key.csr" "private-key-ca.srl" "private-key.p12" 25 | 26 | rm -f "trusted-cert.key" "trusted-cert.crt" "trusted-cert.jceks" 27 | openssl req -x509 -nodes -days 365 -newkey rsa:2048 -subj "/CN=Test User/O=Test Organization/C=US" -extensions v3_req \ 28 | -keyout "trusted-cert.key" -out "trusted-cert.crt" 29 | keytool -importcert -noprompt -alias "trusted-cert-some-alias" \ 30 | -file "trusted-cert.crt" \ 31 | -destkeystore "trusted-cert.jceks" -storetype JCEKS -deststorepass "store-password" 32 | rm -f "trusted-cert.key" 33 | 34 | go test github.com/square/certigo/jceks -jceks.write-reencoded=true 35 | -------------------------------------------------------------------------------- /starttls/psql/buf.go: -------------------------------------------------------------------------------- 1 | package pq 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | 7 | "github.com/square/certigo/starttls/psql/oid" 8 | ) 9 | 10 | type readBuf []byte 11 | 12 | func (b *readBuf) int32() (n int) { 13 | n = int(int32(binary.BigEndian.Uint32(*b))) 14 | *b = (*b)[4:] 15 | return 16 | } 17 | 18 | func (b *readBuf) oid() (n oid.Oid) { 19 | n = oid.Oid(binary.BigEndian.Uint32(*b)) 20 | *b = (*b)[4:] 21 | return 22 | } 23 | 24 | // N.B: this is actually an unsigned 16-bit integer, unlike int32 25 | func (b *readBuf) int16() (n int) { 26 | n = int(binary.BigEndian.Uint16(*b)) 27 | *b = (*b)[2:] 28 | return 29 | } 30 | 31 | func (b *readBuf) string() string { 32 | i := bytes.IndexByte(*b, 0) 33 | if i < 0 { 34 | errorf("invalid message format; expected string terminator") 35 | } 36 | s := (*b)[:i] 37 | *b = (*b)[i+1:] 38 | return string(s) 39 | } 40 | 41 | func (b *readBuf) next(n int) (v []byte) { 42 | v = (*b)[:n] 43 | *b = (*b)[n:] 44 | return 45 | } 46 | 47 | func (b *readBuf) byte() byte { 48 | return b.next(1)[0] 49 | } 50 | 51 | type writeBuf struct { 52 | buf []byte 53 | pos int 54 | } 55 | 56 | func (b *writeBuf) int32(n int) { 57 | x := make([]byte, 4) 58 | binary.BigEndian.PutUint32(x, uint32(n)) 59 | b.buf = append(b.buf, x...) 60 | } 61 | 62 | func (b *writeBuf) int16(n int) { 63 | x := make([]byte, 2) 64 | binary.BigEndian.PutUint16(x, uint16(n)) 65 | b.buf = append(b.buf, x...) 66 | } 67 | 68 | func (b *writeBuf) string(s string) { 69 | b.buf = append(b.buf, (s + "\000")...) 70 | } 71 | 72 | func (b *writeBuf) byte(c byte) { 73 | b.buf = append(b.buf, c) 74 | } 75 | 76 | func (b *writeBuf) bytes(v []byte) { 77 | b.buf = append(b.buf, v...) 78 | } 79 | 80 | func (b *writeBuf) wrap() []byte { 81 | p := b.buf[b.pos:] 82 | binary.BigEndian.PutUint32(p, uint32(len(p))) 83 | return b.buf 84 | } 85 | 86 | func (b *writeBuf) next(c byte) { 87 | p := b.buf[b.pos:] 88 | binary.BigEndian.PutUint32(p, uint32(len(p))) 89 | b.pos = len(b.buf) + 1 90 | b.buf = append(b.buf, c, 0, 0, 0, 0) 91 | } 92 | -------------------------------------------------------------------------------- /starttls/ftp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package starttls 17 | 18 | import ( 19 | "bufio" 20 | "crypto/tls" 21 | "fmt" 22 | "net" 23 | "strconv" 24 | ) 25 | 26 | func dumpTLSConnStateFromFTP(dialer Dialer, address string, config *tls.Config) (*tls.ConnectionState, error) { 27 | c, err := dialer.Dial("tcp", address) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | conn := c.(*net.TCPConn) 33 | status, err := readFTP(conn) 34 | if err != nil { 35 | return nil, err 36 | } 37 | if status != 220 { 38 | return nil, fmt.Errorf("FTP server responded with status %d, was expecting 220", status) 39 | } 40 | 41 | fmt.Fprintf(conn, "AUTH TLS\r\n") 42 | status, err = readFTP(conn) 43 | if err != nil { 44 | return nil, err 45 | } 46 | if status != 234 { 47 | return nil, fmt.Errorf("FTP server responded with status %d, was expecting 234", status) 48 | } 49 | 50 | tlsConn := tls.Client(conn, config) 51 | err = tlsConn.Handshake() 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | state := tlsConn.ConnectionState() 57 | return &state, nil 58 | } 59 | 60 | func readFTP(conn *net.TCPConn) (int, error) { 61 | reader := bufio.NewReader(conn) 62 | response, err := reader.ReadString('\n') 63 | if err != nil { 64 | return 0, err 65 | } 66 | if len(response) <= 3 { 67 | return 0, fmt.Errorf("Error parsing ftp protocol: Status code too short: '%s'", response) 68 | } 69 | return strconv.Atoi(response[:3]) 70 | } 71 | -------------------------------------------------------------------------------- /starttls/mysql/AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of Go-MySQL-Driver authors for copyright purposes. 2 | 3 | # If you are submitting a patch, please add your name or the name of the 4 | # organization which holds the copyright to this list in alphabetical order. 5 | 6 | # Names should be added to this file as 7 | # Name 8 | # The email address is not required for organizations. 9 | # Please keep the list sorted. 10 | 11 | 12 | # Individual Persons 13 | 14 | Aaron Hopkins 15 | Arne Hormann 16 | Carlos Nieto 17 | Chris Moos 18 | Daniel Nichter 19 | Daniël van Eeden 20 | DisposaBoy 21 | Egor Smolyakov 22 | Frederick Mayle 23 | Gustavo Kristic 24 | Hanno Braun 25 | Henri Yandell 26 | Hirotaka Yamamoto 27 | INADA Naoki 28 | James Harr 29 | Jian Zhen 30 | Joshua Prunier 31 | Julien Lefevre 32 | Julien Schmidt 33 | Kamil Dziedzic 34 | Kevin Malachowski 35 | Lennart Rudolph 36 | Leonardo YongUk Kim 37 | Luca Looz 38 | Lucas Liu 39 | Luke Scott 40 | Michael Woolnough 41 | Nicola Peduzzi 42 | Olivier Mengué 43 | Paul Bonser 44 | Runrioter Wung 45 | Soroush Pour 46 | Stan Putrya 47 | Stanley Gunawan 48 | Xiangyu Hu 49 | Xiaobing Jiang 50 | Xiuming Chen 51 | Zhenye Xie 52 | 53 | # Organizations 54 | 55 | Barracuda Networks, Inc. 56 | Google Inc. 57 | Stripe Inc. 58 | -------------------------------------------------------------------------------- /tests/dump-leaf-to-not-verbose.t: -------------------------------------------------------------------------------- 1 | Set up test data. 2 | 3 | $ cat > example-leaf.crt < -----BEGIN CERTIFICATE----- 5 | > MIIDfDCCAmSgAwIBAgIJANWAkzF7PA8/MA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNV 6 | > BAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMH 7 | > ZXhhbXBsZTEVMBMGA1UEAxMMZXhhbXBsZS1sZWFmMB4XDTE2MDYxMDIyMTQxMVoX 8 | > DTIzMDQxNTIyMTQxMVowVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYD 9 | > VQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRUwEwYDVQQDEwxleGFtcGxl 10 | > LWxlYWYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7stSvfQyGuHw3 11 | > v34fisqIdDXberrFoFk9ht/WdXgYzX2uLNKdsR/J5sbWSl8K/5djpzj31eIzqU69 12 | > w8v7SChM5x9bouDsABHz3kZucx5cSafEgJojysBkcrq3VY+aJanzbL+qErYX+lhR 13 | > pPcZK6JMWIwar8Y3B2la4yWwieecw2/WfEVvG0M/DOYKnR8QHFsfl3US1dnBM84c 14 | > zKPyt9r40gDk2XiH/lGts5a94rAGvbr8IMCtq0mA5aH3Fx3mDSi3+4MZwygCAHrF 15 | > 5O5iSV9rEI+m2+7j2S+jHDUnvV+nqcpb9m6ENECnYX8FD2KcqlOjTmw8smDy09N2 16 | > Np6i464lAgMBAAGjTzBNMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAs 17 | > BgNVHREEJTAjhwR/AAABhxAAAAAAAAAAAAAAAAAAAAABgglsb2NhbGhvc3QwDQYJ 18 | > KoZIhvcNAQELBQADggEBAGM4aa/qrURUweZBIwZYv8O9b2+r4l0HjGAh982/B9sM 19 | > lM05kojyDCUGvj86z18Lm8mKr4/y+i0nJ+vDIksEvfDuzw5ALAXGcBzPJKtICUf7 20 | > LstA/n9NNpshWz0kld9ylnB5mbUzSFDncVyeXkEf5sGQXdIIZT9ChRBoiloSaa7d 21 | > vBVCcsX1LGP2LWqKtD+7nUnw5qCwtyAVT8pthEUxFTpywoiJS5ZdzeEx8MNGvUeL 22 | > Fj2kleqPF78EioEQlSOxViCuctEtnQuPcDLHNFr10byTZY9roObiqdsJLMVvb2Xl 23 | > iJjAqaPa9AkYwGE6xHw2ispwg64Rse0+AtKups19WIU= 24 | > -----END CERTIFICATE----- 25 | > EOF 26 | 27 | Dump an example certificate (example-leaf.crt) 28 | 29 | $ certigo dump example-leaf.crt 30 | ** CERTIFICATE 1 ** 31 | Input Format: PEM 32 | Valid: 2016-06-10 22:14 UTC to 2023-04-15 22:14 UTC 33 | Subject: 34 | \tC=US, ST=CA, O=certigo, OU=example, CN=example-leaf (esc) 35 | Issuer: 36 | \tC=US, ST=CA, O=certigo, OU=example, CN=example-leaf (esc) 37 | DNS Names: 38 | \tlocalhost (esc) 39 | IP Addresses: 40 | \t127.0.0.1, ::1 (esc) 41 | Lints: 42 | \tERROR: [RFC5280] CAs must include keyIdentifer field of AKI in all non-self-issued certificates (esc) 43 | \tWARN: [RFC5280] Sub certificates SHOULD include Subject Key Identifier in end entity certs (esc) 44 | 45 | -------------------------------------------------------------------------------- /starttls/psql/conn_go18.go: -------------------------------------------------------------------------------- 1 | // +build go1.8 2 | 3 | package pq 4 | 5 | import ( 6 | "context" 7 | "database/sql/driver" 8 | "errors" 9 | ) 10 | 11 | // Implement the "QueryerContext" interface 12 | func (cn *conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { 13 | list := make([]driver.Value, len(args)) 14 | for i, nv := range args { 15 | list[i] = nv.Value 16 | } 17 | var closed chan<- struct{} 18 | if ctx.Done() != nil { 19 | closed = watchCancel(ctx, cn.cancel) 20 | } 21 | r, err := cn.query(query, list) 22 | if err != nil { 23 | return nil, err 24 | } 25 | r.closed = closed 26 | return r, nil 27 | } 28 | 29 | // Implement the "ExecerContext" interface 30 | func (cn *conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { 31 | list := make([]driver.Value, len(args)) 32 | for i, nv := range args { 33 | list[i] = nv.Value 34 | } 35 | 36 | if ctx.Done() != nil { 37 | closed := watchCancel(ctx, cn.cancel) 38 | defer close(closed) 39 | } 40 | 41 | return cn.Exec(query, list) 42 | } 43 | 44 | // Implement the "ConnBeginTx" interface 45 | func (cn *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { 46 | if opts.Isolation != 0 { 47 | return nil, errors.New("isolation levels not supported") 48 | } 49 | if opts.ReadOnly { 50 | return nil, errors.New("read-only transactions not supported") 51 | } 52 | tx, err := cn.Begin() 53 | if err != nil { 54 | return nil, err 55 | } 56 | if ctx.Done() != nil { 57 | cn.txnClosed = watchCancel(ctx, cn.cancel) 58 | } 59 | return tx, nil 60 | } 61 | 62 | func watchCancel(ctx context.Context, cancel func()) chan<- struct{} { 63 | closed := make(chan struct{}) 64 | go func() { 65 | select { 66 | case <-ctx.Done(): 67 | cancel() 68 | case <-closed: 69 | } 70 | }() 71 | return closed 72 | } 73 | 74 | func (cn *conn) cancel() { 75 | var err error 76 | can := &conn{} 77 | can.c, err = dial(cn.dialer, cn.opts) 78 | if err != nil { 79 | return 80 | } 81 | can.ssl(cn.opts) 82 | 83 | defer can.errRecover(&err) 84 | 85 | w := can.writeBuf(0) 86 | w.int32(80877102) // cancel request code 87 | w.int32(cn.processID) 88 | w.int32(cn.secretKey) 89 | 90 | can.sendStartupPacket(w) 91 | _ = can.c.Close() 92 | } 93 | -------------------------------------------------------------------------------- /jceks/utils_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package jceks 17 | 18 | import ( 19 | "crypto/rsa" 20 | "crypto/x509" 21 | "encoding/pem" 22 | "fmt" 23 | "os" 24 | "strings" 25 | ) 26 | 27 | // LoadPEMKey extracts a private key from a PEM file. 28 | func LoadPEMKey(filename string) (*rsa.PrivateKey, error) { 29 | keyPEMBlock, err := os.ReadFile(filename) 30 | if err != nil { 31 | return nil, fmt.Errorf("unable to read private key from %q: %w", filename, err) 32 | } 33 | 34 | var keyDERBlock *pem.Block 35 | for { 36 | keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock) 37 | if keyDERBlock == nil { 38 | return nil, fmt.Errorf("failed to parse key PEM data") 39 | } 40 | if keyDERBlock.Type == "PRIVATE KEY" || 41 | strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") { 42 | break 43 | } 44 | } 45 | 46 | key, err := x509.ParsePKCS8PrivateKey(keyDERBlock.Bytes) 47 | if err != nil { 48 | return nil, fmt.Errorf("failed to parse PKCS8 private key: %s", err) 49 | } 50 | 51 | rsaKey, ok := key.(*rsa.PrivateKey) 52 | if !ok { 53 | return nil, fmt.Errorf("PKCS8 private key is not RSA") 54 | } 55 | 56 | return rsaKey, nil 57 | } 58 | 59 | // LoadPEMCert extracts a certificate from a PEM file. 60 | func LoadPEMCert(filename string) (*x509.Certificate, error) { 61 | certPEMBlock, err := os.ReadFile(filename) 62 | if err != nil { 63 | return nil, fmt.Errorf("unable to read private key from %q: %w", filename, err) 64 | } 65 | 66 | var certDERBlock *pem.Block 67 | for { 68 | certDERBlock, certPEMBlock = pem.Decode(certPEMBlock) 69 | if certDERBlock == nil { 70 | return nil, fmt.Errorf("failed to parse certificate PEM data") 71 | } 72 | if certDERBlock.Type == "CERTIFICATE" { 73 | break 74 | } 75 | } 76 | 77 | return x509.ParseCertificate(certDERBlock.Bytes) 78 | } 79 | -------------------------------------------------------------------------------- /tests/dump-leaf-to-text.t: -------------------------------------------------------------------------------- 1 | Set up test data. 2 | 3 | $ cat > example-leaf.crt < -----BEGIN CERTIFICATE----- 5 | > MIIDfDCCAmSgAwIBAgIJANWAkzF7PA8/MA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNV 6 | > BAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMH 7 | > ZXhhbXBsZTEVMBMGA1UEAxMMZXhhbXBsZS1sZWFmMB4XDTE2MDYxMDIyMTQxMVoX 8 | > DTIzMDQxNTIyMTQxMVowVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYD 9 | > VQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRUwEwYDVQQDEwxleGFtcGxl 10 | > LWxlYWYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7stSvfQyGuHw3 11 | > v34fisqIdDXberrFoFk9ht/WdXgYzX2uLNKdsR/J5sbWSl8K/5djpzj31eIzqU69 12 | > w8v7SChM5x9bouDsABHz3kZucx5cSafEgJojysBkcrq3VY+aJanzbL+qErYX+lhR 13 | > pPcZK6JMWIwar8Y3B2la4yWwieecw2/WfEVvG0M/DOYKnR8QHFsfl3US1dnBM84c 14 | > zKPyt9r40gDk2XiH/lGts5a94rAGvbr8IMCtq0mA5aH3Fx3mDSi3+4MZwygCAHrF 15 | > 5O5iSV9rEI+m2+7j2S+jHDUnvV+nqcpb9m6ENECnYX8FD2KcqlOjTmw8smDy09N2 16 | > Np6i464lAgMBAAGjTzBNMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAs 17 | > BgNVHREEJTAjhwR/AAABhxAAAAAAAAAAAAAAAAAAAAABgglsb2NhbGhvc3QwDQYJ 18 | > KoZIhvcNAQELBQADggEBAGM4aa/qrURUweZBIwZYv8O9b2+r4l0HjGAh982/B9sM 19 | > lM05kojyDCUGvj86z18Lm8mKr4/y+i0nJ+vDIksEvfDuzw5ALAXGcBzPJKtICUf7 20 | > LstA/n9NNpshWz0kld9ylnB5mbUzSFDncVyeXkEf5sGQXdIIZT9ChRBoiloSaa7d 21 | > vBVCcsX1LGP2LWqKtD+7nUnw5qCwtyAVT8pthEUxFTpywoiJS5ZdzeEx8MNGvUeL 22 | > Fj2kleqPF78EioEQlSOxViCuctEtnQuPcDLHNFr10byTZY9roObiqdsJLMVvb2Xl 23 | > iJjAqaPa9AkYwGE6xHw2ispwg64Rse0+AtKups19WIU= 24 | > -----END CERTIFICATE----- 25 | > EOF 26 | 27 | Dump an example certificate (example-leaf.crt) 28 | 29 | $ certigo --verbose dump example-leaf.crt 30 | ** CERTIFICATE 1 ** 31 | Input Format: PEM 32 | Serial: 15384458167827828543 33 | Valid: 2016-06-10 22:14 UTC to 2023-04-15 22:14 UTC 34 | Signature: SHA256-RSA 35 | Subject Info: 36 | \tCountry: US (esc) 37 | \tProvince: CA (esc) 38 | \tOrganization: certigo (esc) 39 | \tOrganizational Unit: example (esc) 40 | \tCommonName: example-leaf (esc) 41 | Issuer Info: 42 | \tCountry: US (esc) 43 | \tProvince: CA (esc) 44 | \tOrganization: certigo (esc) 45 | \tOrganizational Unit: example (esc) 46 | \tCommonName: example-leaf (esc) 47 | Extended Key Usage: 48 | \tClient Auth (esc) 49 | \tServer Auth (esc) 50 | DNS Names: 51 | \tlocalhost (esc) 52 | IP Addresses: 53 | \t127.0.0.1, ::1 (esc) 54 | Lints: 55 | \tERROR: [RFC5280] CAs must include keyIdentifer field of AKI in all non-self-issued certificates (esc) 56 | \tWARN: [RFC5280] Sub certificates SHOULD include Subject Key Identifier in end entity certs (esc) 57 | -------------------------------------------------------------------------------- /tests/dump-small-key-to-json.t: -------------------------------------------------------------------------------- 1 | Set up test data. 2 | 3 | $ cat > example-small-key.crt < -----BEGIN CERTIFICATE----- 5 | > MIICKzCCAZQCCQDHlr/u+lfb8jANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJV 6 | > UzELMAkGA1UECBMCQ0ExEDAOBgNVBAoTB2NlcnRpZ28xEDAOBgNVBAsTB2V4YW1w 7 | > bGUxGjAYBgNVBAMTEWV4YW1wbGUtc21hbGwta2V5MB4XDTE2MDYxMDIyMTQxMloX 8 | > DTIzMDQxNTIyMTQxMlowWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYD 9 | > VQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRowGAYDVQQDExFleGFtcGxl 10 | > LXNtYWxsLWtleTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAlzyCIeP1T87k 11 | > 1rHVMtbaGXIWpK/VQuvuXwig+e3ct1ajA4bw0BAInXZ37FEGGSCUix0k/CjH2Nlt 12 | > GREtwbahE0k5oTkVbA5XS4xkNs0M0poAFN5OiFKEAqZ014hqhvKnEUQ2oTe9SVOR 13 | > Ww49mLNg36AIEE2Fu2KQb/VT90cwwD0CAwEAATANBgkqhkiG9w0BAQsFAAOBgQBV 14 | > sJ4Vb2L1ywLVeAxNqY0PZqS7a8Q2GLhNr5V+3hOoWn7bwqQ7L06UJGSrcLOPZeIH 15 | > IWM20aOFHSTWbocd4f+m6s3llyXwBBlK2BPZbWv0OeAHgjN9AVav4flAZ4oD2GxA 16 | > aJkGAXmR9QzZNJLai5mv3L/B/p/NxeU3UGfaySxVvw== 17 | > -----END CERTIFICATE----- 18 | > EOF 19 | 20 | Dump an example certificate (example-leaf.crt) to JSON output 21 | 22 | $ certigo dump --json example-small-key.crt 23 | {"certificates":[{"serial":"14381893493177441266","not_before":"2016-06-10T22:14:12Z","not_after":"2023-04-15T22:14:12Z","signature_algorithm":"SHA256-RSA","is_self_signed":true,"subject":{"common_name":"example-small-key","country":["US"],"organization":["certigo"],"organizational_unit":["example"],"province":["CA"]},"issuer":{"common_name":"example-small-key","country":["US"],"organization":["certigo"],"organizational_unit":["example"],"province":["CA"]},"lints":["ERROR: [RFC5280] CAs must include keyIdentifer field of AKI in all non-self-issued certificates","WARN: [RFC5280] Sub certificates SHOULD include Subject Key Identifier in end entity certs"],"pem":"-----BEGIN CERTIFICATE-----\nMIICKzCCAZQCCQDHlr/u+lfb8jANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJV\nUzELMAkGA1UECBMCQ0ExEDAOBgNVBAoTB2NlcnRpZ28xEDAOBgNVBAsTB2V4YW1w\nbGUxGjAYBgNVBAMTEWV4YW1wbGUtc21hbGwta2V5MB4XDTE2MDYxMDIyMTQxMloX\nDTIzMDQxNTIyMTQxMlowWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYD\nVQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRowGAYDVQQDExFleGFtcGxl\nLXNtYWxsLWtleTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAlzyCIeP1T87k\n1rHVMtbaGXIWpK/VQuvuXwig+e3ct1ajA4bw0BAInXZ37FEGGSCUix0k/CjH2Nlt\nGREtwbahE0k5oTkVbA5XS4xkNs0M0poAFN5OiFKEAqZ014hqhvKnEUQ2oTe9SVOR\nWw49mLNg36AIEE2Fu2KQb/VT90cwwD0CAwEAATANBgkqhkiG9w0BAQsFAAOBgQBV\nsJ4Vb2L1ywLVeAxNqY0PZqS7a8Q2GLhNr5V+3hOoWn7bwqQ7L06UJGSrcLOPZeIH\nIWM20aOFHSTWbocd4f+m6s3llyXwBBlK2BPZbWv0OeAHgjN9AVav4flAZ4oD2GxA\naJkGAXmR9QzZNJLai5mv3L/B/p/NxeU3UGfaySxVvw==\n-----END CERTIFICATE-----\n"}]} 24 | -------------------------------------------------------------------------------- /internal/gen-known-logs/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package main 17 | 18 | import ( 19 | "encoding/json" 20 | "flag" 21 | "fmt" 22 | "log" 23 | "net/http" 24 | "os" 25 | "time" 26 | ) 27 | 28 | var ( 29 | out = flag.String("out", "", "Output file") 30 | ) 31 | 32 | // https://github.com/google/certificate-transparency-community-site/blob/master/docs/google/known-logs.md 33 | const knownLogsAddr = "https://www.gstatic.com/ct/log_list/v2/all_logs_list.json" 34 | 35 | type ctLog struct { 36 | operator string 37 | url string 38 | } 39 | 40 | func main() { 41 | flag.Parse() 42 | 43 | resp, err := http.Get(knownLogsAddr) 44 | if err != nil { 45 | log.Fatalf("Failed to fetch the list of known CT logs: %v", err) 46 | } 47 | defer resp.Body.Close() 48 | 49 | var logs struct { 50 | Operators []struct { 51 | Name string `json:"name"` 52 | Logs []struct { 53 | ID string `json:"log_id"` 54 | URL string `json:"url"` 55 | } `json:"logs"` 56 | } `json:"operators"` 57 | } 58 | 59 | if err := json.NewDecoder(resp.Body).Decode(&logs); err != nil { 60 | log.Fatalf("Failed to parse the list of known CT logs: %v", err) 61 | } 62 | 63 | f, err := os.Create(*out) 64 | if err != nil { 65 | log.Fatalf("Failed to open file %s", *out) 66 | } 67 | defer f.Close() 68 | 69 | fmt.Fprintf(f, `// Code generated by github.com/square/certigo/internal/gen-known-logs; DO NOT EDIT 70 | // Generated at %s 71 | package %s 72 | 73 | type ctLog struct { 74 | operator string 75 | url string 76 | } 77 | 78 | var knownLogs = map[string]*ctLog{ 79 | `, time.Now().Format(time.RFC3339), os.Getenv("GOPACKAGE")) 80 | knownLogs := make(map[string]*ctLog) 81 | for _, op := range logs.Operators { 82 | for _, l := range op.Logs { 83 | fmt.Fprintf(f, "\t%q: {operator: %q, url: %q},\n", l.ID, op.Name, l.URL) 84 | knownLogs[l.ID] = &ctLog{ 85 | operator: op.Name, 86 | url: l.URL, 87 | } 88 | } 89 | } 90 | fmt.Fprintln(f, "}") 91 | } 92 | -------------------------------------------------------------------------------- /starttls/mysql/rows.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | import ( 12 | "database/sql/driver" 13 | "io" 14 | ) 15 | 16 | type mysqlField struct { 17 | tableName string 18 | name string 19 | flags fieldFlag 20 | fieldType byte 21 | decimals byte 22 | } 23 | 24 | type mysqlRows struct { 25 | mc *mysqlConn 26 | columns []mysqlField 27 | } 28 | 29 | type binaryRows struct { 30 | mysqlRows 31 | } 32 | 33 | type textRows struct { 34 | mysqlRows 35 | } 36 | 37 | type emptyRows struct{} 38 | 39 | func (rows *mysqlRows) Columns() []string { 40 | columns := make([]string, len(rows.columns)) 41 | if rows.mc != nil && rows.mc.cfg.ColumnsWithAlias { 42 | for i := range columns { 43 | if tableName := rows.columns[i].tableName; len(tableName) > 0 { 44 | columns[i] = tableName + "." + rows.columns[i].name 45 | } else { 46 | columns[i] = rows.columns[i].name 47 | } 48 | } 49 | } else { 50 | for i := range columns { 51 | columns[i] = rows.columns[i].name 52 | } 53 | } 54 | return columns 55 | } 56 | 57 | func (rows *mysqlRows) Close() error { 58 | mc := rows.mc 59 | if mc == nil { 60 | return nil 61 | } 62 | if mc.netConn == nil { 63 | return ErrInvalidConn 64 | } 65 | 66 | // Remove unread packets from stream 67 | err := mc.readUntilEOF() 68 | if err == nil { 69 | if err = mc.discardResults(); err != nil { 70 | return err 71 | } 72 | } 73 | 74 | rows.mc = nil 75 | return err 76 | } 77 | 78 | func (rows *binaryRows) Next(dest []driver.Value) error { 79 | if mc := rows.mc; mc != nil { 80 | if mc.netConn == nil { 81 | return ErrInvalidConn 82 | } 83 | 84 | // Fetch next row from stream 85 | return rows.readRow(dest) 86 | } 87 | return io.EOF 88 | } 89 | 90 | func (rows *textRows) Next(dest []driver.Value) error { 91 | if mc := rows.mc; mc != nil { 92 | if mc.netConn == nil { 93 | return ErrInvalidConn 94 | } 95 | 96 | // Fetch next row from stream 97 | return rows.readRow(dest) 98 | } 99 | return io.EOF 100 | } 101 | 102 | func (rows emptyRows) Columns() []string { 103 | return nil 104 | } 105 | 106 | func (rows emptyRows) Close() error { 107 | return nil 108 | } 109 | 110 | func (rows emptyRows) Next(dest []driver.Value) error { 111 | return io.EOF 112 | } 113 | -------------------------------------------------------------------------------- /jceks/pkcs5_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package jceks 17 | 18 | import ( 19 | "fmt" 20 | "testing" 21 | 22 | "github.com/stretchr/testify/require" 23 | ) 24 | 25 | func TestPKCS5PadUnpad(t *testing.T) { 26 | t.Parallel() 27 | 28 | for _, tc := range []struct { 29 | in []byte 30 | expected []byte 31 | }{ 32 | {in: []byte{}, expected: []byte{8, 8, 8, 8, 8, 8, 8, 8}}, 33 | {in: []byte{42}, expected: []byte{42, 7, 7, 7, 7, 7, 7, 7}}, 34 | {in: []byte{42, 42}, expected: []byte{42, 42, 6, 6, 6, 6, 6, 6}}, 35 | {in: []byte{42, 42, 42}, expected: []byte{42, 42, 42, 5, 5, 5, 5, 5}}, 36 | {in: []byte{42, 42, 42, 42}, expected: []byte{42, 42, 42, 42, 4, 4, 4, 4}}, 37 | {in: []byte{42, 42, 42, 42, 42}, expected: []byte{42, 42, 42, 42, 42, 3, 3, 3}}, 38 | {in: []byte{42, 42, 42, 42, 42, 42}, expected: []byte{42, 42, 42, 42, 42, 42, 2, 2}}, 39 | {in: []byte{42, 42, 42, 42, 42, 42, 42}, expected: []byte{42, 42, 42, 42, 42, 42, 42, 1}}, 40 | {in: []byte{42, 42, 42, 42, 42, 42, 42, 42}, expected: []byte{42, 42, 42, 42, 42, 42, 42, 42, 8, 8, 8, 8, 8, 8, 8, 8}}, 41 | {in: []byte{0}, expected: []byte{0, 7, 7, 7, 7, 7, 7, 7}}, 42 | {in: []byte{1, 2, 3, 4, 5}, expected: []byte{1, 2, 3, 4, 5, 3, 3, 3}}, 43 | {in: []byte{3, 3, 3, 3, 3}, expected: []byte{3, 3, 3, 3, 3, 3, 3, 3}}, 44 | } { 45 | t.Run(fmt.Sprintf("In-%X", tc.in), func(t *testing.T) { 46 | t.Parallel() 47 | 48 | actual := pkcs5Pad(tc.in) 49 | require.Equal(t, tc.expected, actual) 50 | 51 | unpadded, err := pkcs5Unpad(actual) 52 | require.NoError(t, err) 53 | require.Equal(t, tc.in, unpadded) 54 | }) 55 | } 56 | } 57 | 58 | func TestPKCS5UnpadFailures(t *testing.T) { 59 | t.Parallel() 60 | 61 | for _, tc := range []struct { 62 | name string 63 | in []byte 64 | }{ 65 | {name: "Empty", in: nil}, 66 | {name: "TooShort", in: []byte{1, 2, 3, 4, 5, 6, 7}}, 67 | {name: "PadExceedsBlockSize", in: []byte{9, 9, 9, 9, 9, 9, 9, 9, 9}}, 68 | {name: "InvalidPadding", in: []byte{42, 7, 7, 7, 6, 7, 7, 7}}, 69 | } { 70 | t.Run(tc.name, func(t *testing.T) { 71 | t.Parallel() 72 | 73 | _, err := pkcs5Unpad(tc.in) 74 | require.Error(t, err) 75 | require.ErrorIs(t, err, ErrInvalidCiphertext) 76 | }) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /starttls/dialer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package starttls 17 | 18 | import ( 19 | "crypto/tls" 20 | "fmt" 21 | "net" 22 | "net/url" 23 | "time" 24 | 25 | "github.com/mwitkow/go-http-dialer" 26 | ) 27 | 28 | type timeoutError struct{} 29 | 30 | func (timeoutError) Error() string { return "tls: DialWithDialer timed out" } 31 | func (timeoutError) Timeout() bool { return true } 32 | func (timeoutError) Temporary() bool { return true } 33 | 34 | // Dialer is an interface for dialers (either net.Dialer, or http_dialer.HttpTunnel) 35 | type Dialer interface { 36 | Dial(network, address string) (net.Conn, error) 37 | } 38 | 39 | // Internal copy of tls.DialWithDialer, adapter so it can work with HTTP CONNECT dialers. 40 | // See: https://golang.org/pkg/crypto/tls/#DialWithDialer 41 | func dialWithDialer(dialer Dialer, timeout time.Duration, network, addr string, config *tls.Config) (*tls.Conn, error) { 42 | var errChannel chan error 43 | if timeout != 0 { 44 | errChannel = make(chan error, 2) 45 | time.AfterFunc(timeout, func() { 46 | errChannel <- timeoutError{} 47 | }) 48 | } 49 | 50 | rawConn, err := dialer.Dial(network, addr) 51 | if err != nil { 52 | return nil, err 53 | } 54 | 55 | conn := tls.Client(rawConn, config) 56 | if timeout == 0 { 57 | err = conn.Handshake() 58 | } else { 59 | go func() { 60 | errChannel <- conn.Handshake() 61 | }() 62 | 63 | err = <-errChannel 64 | } 65 | 66 | if err != nil { 67 | _ = rawConn.Close() 68 | return nil, err 69 | } 70 | 71 | return conn, nil 72 | } 73 | 74 | func wrapDialerWithProxy(dialer Dialer, connectProxy *url.URL, tlsConfig *tls.Config) (Dialer, error) { 75 | dialerOpt := http_dialer.WithDialer(dialer.(*net.Dialer)) 76 | tlsOpt := http_dialer.WithTls(tlsConfig) 77 | if connectProxy.User != nil { 78 | password, ok := connectProxy.User.Password() 79 | if !ok { 80 | return nil, fmt.Errorf("proxy username without password not currently supported") 81 | } 82 | auth := http_dialer.WithProxyAuth(http_dialer.AuthBasic(connectProxy.User.Username(), password)) 83 | dialer = http_dialer.New(connectProxy, dialerOpt, tlsOpt, auth) 84 | } else { 85 | dialer = http_dialer.New(connectProxy, dialerOpt, tlsOpt) 86 | } 87 | return dialer, nil 88 | } 89 | -------------------------------------------------------------------------------- /lib/oids.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package lib 17 | 18 | import "encoding/asn1" 19 | 20 | // OidDescription returns a human-readable name, a short acronym from RFC1485, a snake_case slug suitable as a json key, 21 | // and a boolean describing whether multiple copies can appear on an X509 cert. 22 | type OidDescription struct { 23 | Name string 24 | Short string 25 | Slug string 26 | Multiple bool 27 | } 28 | 29 | func describeOid(oid asn1.ObjectIdentifier) OidDescription { 30 | raw := oid.String() 31 | // Multiple should be true for any types that are []string in x509.pkix.Name. When in doubt, set it to true. 32 | names := map[string]OidDescription{ 33 | "2.5.4.3": {"CommonName", "CN", "common_name", false}, 34 | "2.5.4.5": {"EV Incorporation Registration Number", "", "ev_registration_number", false}, 35 | "2.5.4.6": {"Country", "C", "country", true}, 36 | "2.5.4.7": {"Locality", "L", "locality", true}, 37 | "2.5.4.8": {"Province", "ST", "province", true}, 38 | "2.5.4.9": {"Street", "", "street", true}, 39 | "2.5.4.10": {"Organization", "O", "organization", true}, 40 | "2.5.4.11": {"Organizational Unit", "OU", "organizational_unit", true}, 41 | "2.5.4.15": {"Business Category", "", "business_category", true}, 42 | "2.5.4.17": {"Postal Code", "", "postalcode", true}, 43 | "1.2.840.113549.1.9.1": {"Email Address", "", "email_address", true}, 44 | "1.3.6.1.4.1.311.60.2.1.1": {"EV Incorporation Locality", "", "ev_locality", true}, 45 | "1.3.6.1.4.1.311.60.2.1.2": {"EV Incorporation Province", "", "ev_province", true}, 46 | "1.3.6.1.4.1.311.60.2.1.3": {"EV Incorporation Country", "", "ev_country", true}, 47 | "0.9.2342.19200300.100.1.1": {"User ID", "UID", "user_id", true}, 48 | "0.9.2342.19200300.100.1.25": {"Domain Component", "DC", "domain_component", true}, 49 | } 50 | if description, ok := names[raw]; ok { 51 | return description 52 | } 53 | return OidDescription{raw, "", raw, true} 54 | } 55 | 56 | func oidShort(oid asn1.ObjectIdentifier) string { 57 | return describeOid(oid).Short 58 | } 59 | 60 | func oidName(oid asn1.ObjectIdentifier) string { 61 | return describeOid(oid).Name 62 | } 63 | -------------------------------------------------------------------------------- /tests/dump-spiffe-cert-to-text.t: -------------------------------------------------------------------------------- 1 | Set up test data. 2 | 3 | $ cat > example-spiffe.crt < -----BEGIN CERTIFICATE----- 5 | > MIIE1DCCArygAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzELMAkGA1UEBhMCVVMx 6 | > FzAVBgNVBAoMDnRlc3QxLmFjbWUuY29tMRcwFQYDVQQDDA5JbnRlcm1lZGlhZXRD 7 | > QTAeFw0xNzA3MTkxNjUwMjBaFw0xNzA3MjkxNjUwMjBaMDUxCzAJBgNVBAYTAlVT 8 | > MRcwFQYDVQQKDA50ZXN0MS5hY21lLmNvbTENMAsGA1UEAwwEYmxvZzCCASIwDQYJ 9 | > KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKm8P47lABp4+rz2nN+QYrxedbaFVWoF 10 | > FuoSkqcHsafMwbMrN+kI6wJVtlbwviDvxWFJ92q0H71QNFybTsmof3KUN/kYCp7P 11 | > +LKhBrN0ttWI5q6v5eDrjN0VdtVdnlZOYmJFbvETOgfK/qXKNRRM8HYW0tdqrtEw 12 | > CR5dIu53xVUSViBdwXpuy2c5W2mFn1gxTpdW+3hbZsL1pHrU9qPWLtTgl/KY8kjs 13 | > I7KW1cIcinE4SJomhB5L/4emhxKGY+kEa2+fN9IPjjvKSMOw9kiBKk1GHZcIY5EA 14 | > O3TIfUk3fysPzi5qA0su/bNtPQy1uXgXS10xUlV7pqRPvHjiNzgFkXUCAwEAAaOB 15 | > 4zCB4DAJBgNVHRMEAjAAMB0GA1UdDgQWBBRVQ91jSOONzVr1VGBdJOlPN+3XxTBg 16 | > BgNVHSMEWTBXgBQ13bfx50rDZO3y2CZdHPgleFUEoKE7pDkwNzELMAkGA1UEBhMC 17 | > VVMxFzAVBgNVBAoMDnRlc3QxLmFjbWUuY29tMQ8wDQYDVQQDDAZSb290Q0GCAhAA 18 | > MA4GA1UdDwEB/wQEAwIDqDATBgNVHSUEDDAKBggrBgEFBQcDATAtBgNVHREEJjAk 19 | > hiJzcGlmZmU6Ly9kZXYuYWNtZS5jb20vcGF0aC9zZXJ2aWNlMA0GCSqGSIb3DQEB 20 | > CwUAA4ICAQBp2+rtUxt1VmNM/vi6PwoSoYzWFmQ2nc4OM7bsOG4uppU54wRYZ+T7 21 | > c42EcrpyBgWn+rWHT1Hi6SNcmloKHydaUTZ4pq3IlKKnBNqwivU5BzIxYLDrhR/U 22 | > wd9s1tgmLvADqkQa1XjjSFn5Auoj1R640ry4qpw8IOusdm6wVhru4ssRnHX4E2uR 23 | > jQe7b3ws38aZhjtL78Ip0BB4yPxWJRp/WmEoT33QP+cZhA4IYWECxNODr6DSJeq2 24 | > VNu/6JACGrNfM2Sjt4Wxz+nIa3cKDNCA6PR8StTUTcoQ6ZBzpn+n/Q1xSRIOJz6N 25 | > hgfkyb9O7HAMdAP+TxehjqG3gh5Ky2DgYMCIZOztVzsuOb1DGJe/kGUKeRJLl2/O 26 | > QwkctwUOcVIxckNu6OvclriFzvoXObqO77XeCI2V1Vef0wGTWlWNOdbFa4708Y7f 27 | > 5UdwInYQUi87RFDnc1SDU4Jrsv4KzZiv9FCfDg8pCBIdWpWT7DAuI0d7i7PZ+iFt 28 | > ZZ6sb/YDkyiDXU4ar/dja0FDE2r7jsN9D+FfW49+iDvXr4ELQyhZpW3Zr1Ojwm58 29 | > CJzjZwbRYiVwPBRsKmiYfO1E7esvw3CmjK5chfz8c40f6/APDro9ZmYNBRv2CnJy 30 | > t/DtcM/GpAhBbLP9Tk7kPB41v5fRIxVDo50Iz/qvkr37pQ4RsejSFg== 31 | > -----END CERTIFICATE----- 32 | > EOF 33 | 34 | Dump a SPIFFE example certificate (example-spiffe.crt) 35 | 36 | $ certigo --verbose dump example-spiffe.crt 37 | ** CERTIFICATE 1 ** 38 | Input Format: PEM 39 | Serial: 4096 40 | Valid: 2017-07-19 16:50 UTC to 2017-07-29 16:50 UTC 41 | Signature: SHA256-RSA 42 | Subject Info: 43 | \tCountry: US (esc) 44 | \tOrganization: test1.acme.com (esc) 45 | \tCommonName: blog (esc) 46 | Issuer Info: 47 | \tCountry: US (esc) 48 | \tOrganization: test1.acme.com (esc) 49 | \tCommonName: IntermediaetCA (esc) 50 | Subject Key ID: 55:43:DD:63:48:E3:8D:CD:5A:F5:54:60:5D:24:E9:4F:37:ED:D7:C5 51 | Authority Key ID: 35:DD:B7:F1:E7:4A:C3:64:ED:F2:D8:26:5D:1C:F8:25:78:55:04:A0 52 | Basic Constraints: CA:false 53 | Key Usage: 54 | \tDigital Signature (esc) 55 | \tKey Encipherment (esc) 56 | \tKey Agreement (esc) 57 | Extended Key Usage: 58 | \tServer Auth (esc) 59 | URI Names: 60 | \tspiffe://dev.acme.com/path/service (esc) 61 | -------------------------------------------------------------------------------- /tests/dump-name-constraints-to-text.t: -------------------------------------------------------------------------------- 1 | Set up test data. 2 | 3 | $ cat > example-name-constraints.crt < -----BEGIN CERTIFICATE----- 5 | > MIID1DCCArygAwIBAgIJAL8xOGAOaSzBMA0GCSqGSIb3DQEBCwUAMGExCzAJBgNV 6 | > BAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMH 7 | > ZXhhbXBsZTEhMB8GA1UEAxMYZXhhbXBsZS1uYW1lLWNvbnN0cmFpbnRzMB4XDTE3 8 | > MDgxODE5NDg1MFoXDTI0MDYyMjE5NDg1MFowYTELMAkGA1UEBhMCVVMxCzAJBgNV 9 | > BAgTAkNBMRAwDgYDVQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMSEwHwYD 10 | > VQQDExhleGFtcGxlLW5hbWUtY29uc3RyYWludHMwggEiMA0GCSqGSIb3DQEBAQUA 11 | > A4IBDwAwggEKAoIBAQDkrrryvC1a63Vc2yCBb0SliieU6TnqZAQ5rJzAa4zf7uq9 12 | > obwLuGfZV5j/7nYl04M2gPiz4Rk8uia14tIqBjFjB5DH7czuaPiGKgMSKMXt9lPZ 13 | > xu3ZnRLRmF+I/zlIjxqLU9bxem7Qs7RBoncbbd65gy8PPe2J09rJdrcj5V/hD2jZ 14 | > pNWtrYA6ANGsMSxLchtpn3LKyDy/bW2HIhXmqVIq/2j2p2ljKf6E63L4S7e7FW8E 15 | > MGiW9y+DLO5U7qSZUiqgAiXHjTKWtYgbnlqboPtZRV5quN2PE0KD3FOLhAQX5MKS 16 | > 3P48+cjeswmruNVc8qjdCi1NLsRCTHukM/WxAC35AgMBAAGjgY4wgYswDgYDVR0P 17 | > AQH/BAQDAgIEMBIGA1UdEwEB/wQIMAYBAf8CAQAwZQYDVR0eBF4wXKAsMA6CDC5l 18 | > eGFtcGxlLmNvbTAKhwjAqAAA//8AADAOgQwuZXhhbXBsZS5jb22hLDAOggwuZXhh 19 | > bXBsZS5vcmcwCocICgoAAP//AAAwDoEMLmV4YW1wbGUub3JnMA0GCSqGSIb3DQEB 20 | > CwUAA4IBAQBDxhzTZc0fB7B2EvHmzz45J3xgk/aQksKFXeWxyX7UdgSY+gzABRMk 21 | > txHCMZtdTJxi4BACpRlO4QMbJXSiofhGJQb+ThSvM1CQv+W5X4j7/40RBfRnWbQP 22 | > ZGfxSyGhLYAIzUsHi6X7DbJmzV2EixPbYKCJ+5Vlf7IH0aDexzf4xKLQdy+cWy1g 23 | > Y4bHiiQbVqfTjlzdsMuiFqoZe3/NhOP93XSMZErx6pJ/lrWiO+6ytM3gdwh1y0DE 24 | > tFmaBKzI+2uQwt6DHQuPmOSEmI7NcrVS2WhPVPa7fIY/ExUy7jqIr2qM59eSl7OC 25 | > AhzgLtgqfwLgbj5PWZENYlNRXmZGi4GD 26 | > -----END CERTIFICATE----- 27 | > EOF 28 | 29 | Dump an example certificate with name constraints (example-name-constraints.crt) 30 | 31 | $ certigo --verbose dump example-name-constraints.crt 32 | ** CERTIFICATE 1 ** 33 | Input Format: PEM 34 | Serial: 13776854720312847553 35 | Valid: 2017-08-18 19:48 UTC to 2024-06-22 19:48 UTC 36 | Signature: SHA256-RSA (self-signed) 37 | Subject Info: 38 | \tCountry: US (esc) 39 | \tProvince: CA (esc) 40 | \tOrganization: certigo (esc) 41 | \tOrganizational Unit: example (esc) 42 | \tCommonName: example-name-constraints (esc) 43 | Issuer Info: 44 | \tCountry: US (esc) 45 | \tProvince: CA (esc) 46 | \tOrganization: certigo (esc) 47 | \tOrganizational Unit: example (esc) 48 | \tCommonName: example-name-constraints (esc) 49 | Basic Constraints: CA:true, pathlen:0 50 | Name Constraints: 51 | Permitted DNS domains: 52 | \t.example.com (esc) 53 | Permitted email addresses: 54 | \t.example.com (esc) 55 | Permitted IP ranges: 56 | \t192.168.0.0/16 (esc) 57 | Excluded DNS domains: 58 | \t.example.org (esc) 59 | Excluded email addresses: 60 | \t.example.org (esc) 61 | Excluded IP ranges: 62 | \t10.10.0.0/16 (esc) 63 | Key Usage: 64 | \tCert Sign (esc) 65 | Lints: 66 | \tERROR: [RFC5280] CAs MUST include a Subject Key Identifier in all CA certificates (esc) 67 | \tERROR: [RFC5280] If it is included, conforming CAs MUST mark the name constraints extension as critical (esc) 68 | -------------------------------------------------------------------------------- /cli/terminal/terminal.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package terminal 17 | 18 | import ( 19 | "fmt" 20 | "io" 21 | "os" 22 | "strings" 23 | 24 | "github.com/mattn/go-colorable" 25 | "golang.org/x/crypto/ssh/terminal" //nolint:staticcheck // TODO: Change dependencies when upgrading to Go >= 1.17 26 | ) 27 | 28 | const minWidth = 60 29 | const maxWidth = 80 30 | 31 | // Terminal handles interacting with the user in Certigo 32 | type Terminal interface { 33 | Output() io.Writer 34 | Error() io.Writer 35 | SetDefaultPassword(password string) 36 | ReadPassword(prompt string) string 37 | DetermineWidth() int 38 | } 39 | 40 | // TTY represents unixish stdio, possibly with /dev/tty used to read user input 41 | type TTY struct { 42 | defaultPassword *string 43 | } 44 | 45 | func OpenTTY() *TTY { 46 | return &TTY{} 47 | } 48 | 49 | func (t *TTY) Output() io.Writer { 50 | return colorable.NewColorableStdout() 51 | } 52 | 53 | func (t *TTY) Error() io.Writer { 54 | return os.Stderr 55 | } 56 | 57 | func (t *TTY) SetDefaultPassword(password string) { 58 | t.defaultPassword = &password 59 | } 60 | 61 | func (t *TTY) ReadPassword(prompt string) string { 62 | if t.defaultPassword != nil { 63 | return *t.defaultPassword 64 | } 65 | 66 | var tty *os.File 67 | tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0) 68 | if err != nil { 69 | tty = os.Stdin 70 | } else { 71 | defer func() { _ = tty.Close() }() 72 | } 73 | 74 | _, _ = tty.WriteString("Enter password") 75 | if prompt != "" { 76 | _, _ = fmt.Fprintf(tty, " for entry [%s]", prompt) 77 | } 78 | _, _ = tty.WriteString(": ") 79 | 80 | password, err := terminal.ReadPassword(int(tty.Fd())) 81 | _, _ = tty.WriteString("\n") 82 | if err != nil { 83 | _, _ = fmt.Fprintf(os.Stderr, "error reading password: %s\n", err) 84 | os.Exit(1) 85 | } 86 | 87 | return strings.TrimSuffix(string(password), "\n") 88 | } 89 | 90 | func (t *TTY) DetermineWidth() int { 91 | var width int 92 | fd := int(os.Stdout.Fd()) 93 | if terminal.IsTerminal(fd) { 94 | var err error 95 | width, _, err = terminal.GetSize(fd) 96 | if err != nil { 97 | width = minWidth 98 | } 99 | } else { 100 | width = minWidth 101 | } 102 | 103 | if width > maxWidth { 104 | width = maxWidth 105 | } else if width < minWidth { 106 | width = minWidth 107 | } 108 | return width 109 | } 110 | 111 | // Assert TTY implements terminal 112 | var _ Terminal = &TTY{} 113 | -------------------------------------------------------------------------------- /starttls/ciphersuites.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package starttls 17 | 18 | import ( 19 | "crypto/tls" 20 | "slices" 21 | ) 22 | 23 | // cipherSuitesPreferenceOrder is copied from the crypto/tls package and defines Go's internal client preferences. This 24 | // should be kept up to date with new Go versions, but if it drifts out of date, it only affects negotiation preferences 25 | // and not whether a particular cipher suite is supported. 26 | var cipherSuitesPreferenceOrder = []uint16{ 27 | // AEADs w/ ECDHE 28 | tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 29 | tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 30 | tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 31 | 32 | // CBC w/ ECDHE 33 | tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 34 | tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 35 | 36 | // AEADs w/o ECDHE 37 | tls.TLS_RSA_WITH_AES_128_GCM_SHA256, 38 | tls.TLS_RSA_WITH_AES_256_GCM_SHA384, 39 | 40 | // CBC w/o ECDHE 41 | tls.TLS_RSA_WITH_AES_128_CBC_SHA, 42 | tls.TLS_RSA_WITH_AES_256_CBC_SHA, 43 | 44 | // 3DES 45 | tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 46 | tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, 47 | 48 | // CBC_SHA256 49 | tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 50 | tls.TLS_RSA_WITH_AES_128_CBC_SHA256, 51 | 52 | // RC4 53 | tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, 54 | tls.TLS_RSA_WITH_RC4_128_SHA, 55 | } 56 | 57 | func allSupportedCipherSuiteIDs() []uint16 { 58 | secureSuites := tls.CipherSuites() 59 | insecureSuites := tls.InsecureCipherSuites() 60 | 61 | suites := make([]uint16, 0, len(secureSuites)+len(insecureSuites)) 62 | for _, suitesCategory := range [][]*tls.CipherSuite{secureSuites, insecureSuites} { 63 | for _, suite := range suitesCategory { 64 | suites = append(suites, suite.ID) 65 | } 66 | } 67 | 68 | ordering := make(map[uint16]int) 69 | for i, id := range cipherSuitesPreferenceOrder { 70 | ordering[id] = i 71 | } 72 | 73 | slices.SortFunc(suites, func(s1, s2 uint16) int { 74 | idx1, prefer1 := ordering[s1] 75 | idx2, prefer2 := ordering[s2] 76 | if prefer1 != prefer2 { 77 | if prefer1 { 78 | return -1 79 | } 80 | 81 | return 1 82 | } 83 | if prefer1 { 84 | if idx1 < idx2 { 85 | return -1 86 | } else if idx1 > idx2 { 87 | return 1 88 | } 89 | } 90 | 91 | return 0 // Equal or both not in the preference list (equally bad) 92 | }) 93 | 94 | return suites 95 | } 96 | -------------------------------------------------------------------------------- /pkcs7/pkcs7.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package pkcs7 17 | 18 | import ( 19 | "crypto/x509" 20 | "encoding/asn1" 21 | "fmt" 22 | ) 23 | 24 | var signedDataIdentifier = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 2}) 25 | 26 | // SignedDataEnvelope represents a wrapped SignedData 27 | // object found in PEM-encoded PKCS7 blocks. 28 | type SignedDataEnvelope struct { 29 | Raw asn1.RawContent 30 | Type asn1.ObjectIdentifier 31 | SignedData SignedData `asn1:"tag:0,explicit,optional"` 32 | } 33 | 34 | // SignedData contains signed data and related info. 35 | // Refer to RFC 2315, Section 9.1 for definition of this type. 36 | type SignedData struct { 37 | Version int 38 | DigestAlgorithms []asn1.RawValue `asn1:"set"` 39 | ContentInfo asn1.RawValue 40 | Certificates []asn1.RawValue `asn1:"tag:0,optional,set"` 41 | RevocationLists []asn1.RawValue `asn1:"tag:1,optional,set"` 42 | SignerInfos []asn1.RawValue `asn1:"set"` 43 | } 44 | 45 | // ParseSignedData parses one (or more) signed data blocks from a byte array. 46 | func ParseSignedData(data []byte) ([]*SignedDataEnvelope, error) { 47 | var err error 48 | var block *SignedDataEnvelope 49 | var out []*SignedDataEnvelope 50 | 51 | for rest := data; len(rest) > 0; { 52 | block, rest, err = parseSignedData(rest) 53 | if err != nil { 54 | break 55 | } 56 | out = append(out, block) 57 | } 58 | 59 | return out, err 60 | } 61 | 62 | func parseSignedData(data []byte) (*SignedDataEnvelope, []byte, error) { 63 | var envelope SignedDataEnvelope 64 | rest, err := asn1.Unmarshal(data, &envelope) 65 | if err != nil { 66 | return nil, data, err 67 | } 68 | 69 | if !signedDataIdentifier.Equal(envelope.Type) { 70 | return nil, data, fmt.Errorf("unexpected object identifier (was %s, expecting %s)", envelope.Type.String(), signedDataIdentifier.String()) 71 | } 72 | 73 | if envelope.SignedData.Version != 1 { 74 | return nil, data, fmt.Errorf("unknown version number in signed data block (was %d, expecting 1)", envelope.SignedData.Version) 75 | } 76 | 77 | return &envelope, rest, nil 78 | } 79 | 80 | // ExtractCertificates reads a SignedData type and returns the embedded 81 | // certificates (if present in the structure). 82 | func ExtractCertificates(data []byte) ([]*x509.Certificate, error) { 83 | blocks, err := ParseSignedData(data) 84 | if err != nil { 85 | return nil, err 86 | } 87 | 88 | certs := []*x509.Certificate{} 89 | for _, block := range blocks { 90 | for _, raw := range block.SignedData.Certificates { 91 | cert, err := x509.ParseCertificate(raw.FullBytes) 92 | if err != nil { 93 | return nil, err 94 | } 95 | certs = append(certs, cert) 96 | } 97 | } 98 | 99 | return certs, nil 100 | } 101 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release 3 | 4 | on: 5 | push: 6 | tags: ["*"] 7 | 8 | jobs: 9 | build: 10 | name: Build 11 | strategy: 12 | matrix: 13 | version: ['stable'] 14 | target: 15 | - {os: 'darwin', platform: 'macos-latest', arch: 'amd64'} 16 | - {os: 'linux', platform: 'ubuntu-latest', arch: 'amd64'} 17 | - {os: 'windows', platform: 'windows-latest', arch: 'amd64'} 18 | runs-on: ${{ matrix.target.platform }} 19 | permissions: 20 | contents: write 21 | steps: 22 | - name: Setup Go environment 23 | uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # ratchet:actions/setup-go@v5 24 | with: 25 | go-version: ${{ matrix.version }} 26 | check-latest: true 27 | id: go 28 | - name: Checkout 29 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4 30 | - name: Build binary 31 | run: | 32 | go env -w CGO_ENABLED=0 33 | go build -o certigo . 34 | - name: Upload a Build Artifact 35 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # ratchet:actions/upload-artifact@v4 36 | with: 37 | name: certigo-${{ matrix.target.os }}-${{ matrix.target.arch }} 38 | path: certigo 39 | 40 | release: 41 | name: Create release 42 | runs-on: ubuntu-latest 43 | needs: [build] 44 | permissions: 45 | contents: write 46 | outputs: 47 | upload_url: ${{ steps.create_release.outputs.upload_url }} 48 | steps: 49 | - name: Checkout 50 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4 51 | - name: Create release 52 | id: create_release 53 | uses: actions/create-release@0cb9c9b65d5d1901c1f53e5e66eaf4afd303e70e # ratchet:actions/create-release@v1 54 | env: 55 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 56 | with: 57 | tag_name: ${{ github.ref }} 58 | release_name: "Release Build (Draft)" 59 | body: "Release Build (from ${{ github.ref }}/${{ github.sha }})" 60 | draft: true 61 | prerelease: true 62 | 63 | add-assets: 64 | name: Add assets 65 | runs-on: ubuntu-latest 66 | needs: [build, release] 67 | permissions: 68 | contents: write 69 | strategy: 70 | matrix: 71 | target: 72 | - {os: 'darwin', arch: 'amd64'} 73 | - {os: 'linux', arch: 'amd64'} 74 | - {os: 'windows', arch: 'amd64'} 75 | steps: 76 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4 77 | - name: Download artifact 78 | uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # ratchet:actions/download-artifact@v4 79 | with: 80 | name: certigo-${{ matrix.target.os }}-${{ matrix.target.arch }} 81 | path: dist 82 | - name: Upload artifact to release 83 | uses: actions/upload-release-asset@e8f9f06c4b078e705bd2ea027f0926603fc9b4d5 # ratchet:actions/upload-release-asset@v1 84 | env: 85 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 86 | with: 87 | upload_url: ${{ needs.release.outputs.upload_url }} 88 | asset_path: ./dist/certigo 89 | asset_name: certigo-${{ matrix.target.os }}-${{ matrix.target.arch }} 90 | asset_content_type: application/octet-stream 91 | -------------------------------------------------------------------------------- /test-certs/Makefile: -------------------------------------------------------------------------------- 1 | TARGETS = \ 2 | example-root example-leaf example-sha1 example-md5 example-root-bad-ku \ 3 | example-bad-serial example-small-key example-expired example-elliptic-sha1 \ 4 | example-custom-oid example-name-constraints 5 | 6 | all: \ 7 | $(addsuffix .crt,$(TARGETS)) \ 8 | $(addsuffix .p12,$(TARGETS)) \ 9 | $(addsuffix .p7b,$(TARGETS)) \ 10 | $(addsuffix .jceks,$(TARGETS)) 11 | 12 | clean: 13 | rm -f example-* 14 | 15 | .PHONY: all clean 16 | 17 | # Generic commands 18 | %.key: 19 | openssl genrsa -out $@ 2048 20 | 21 | %.csr: %.key 22 | openssl req -new -key $< -out $@ -subj /C=US/ST=CA/O=certigo/OU=example/CN=$(@:.csr=) 23 | 24 | %.p12: %.crt %.key 25 | openssl pkcs12 -export -out $@ -in $< -inkey $(@:.p12=.key) -name $(@:.p12=) -password pass:password 26 | 27 | %.p7b: %.crt 28 | openssl crl2pkcs7 -nocrl -certfile $< -out $@ 29 | 30 | %.jceks: %.p12 31 | keytool -importkeystore \ 32 | -destkeystore $@ -deststoretype JCEKS -destkeypass password -deststorepass password -destalias $* \ 33 | -srckeystore $< -srcstoretype PKCS12 -srcstorepass password -srcalias $* -storetype JCEKS 34 | 35 | # Good root cert 36 | example-root.crt: example-root.csr 37 | openssl x509 -req -sha256 -in $< -signkey $(@:.crt=.key) -out $@ -days 2500 -extfile openssl.ext -extensions root 38 | 39 | # Good leaf cert 40 | example-leaf.crt: example-leaf.csr 41 | openssl x509 -req -sha256 -in $< -signkey $(@:.crt=.key) -out $@ -days 2500 -extfile openssl.ext -extensions leaf 42 | 43 | # Bad cert with SHA-1 instead of SHA-2 44 | example-sha1.crt: example-sha1.csr 45 | openssl x509 -req -in $< -signkey $(@:.crt=.key) -out $@ -days 2500 -extfile openssl.ext -extensions leaf 46 | 47 | # Bad cert with MD-5 instead of SHA-2 48 | example-md5.crt: example-md5.csr 49 | openssl x509 -req -md5 -in $< -signkey $(@:.crt=.key) -out $@ -days 2500 -extfile openssl.ext -extensions leaf 50 | 51 | # Bad root cert with missing "cert sign" key usage 52 | example-root-bad-ku.crt: example-root-bad-ku.csr 53 | openssl x509 -req -sha256 -in $< -signkey $(@:.crt=.key) -out $@ -days 2500 -extfile openssl.ext -extensions root_bad 54 | 55 | # Bad cert with invalid serial 56 | example-bad-serial.crt: example-bad-serial.csr 57 | openssl x509 -req -sha256 -in $< -signkey $(@:.crt=.key) -out $@ -set_serial 0 -days 2500 -extfile openssl.ext -extensions leaf 58 | 59 | # Example with small RSA key 60 | example-small-key.key: 61 | openssl genrsa -out $@ 1024 62 | 63 | example-small-key.crt: example-small-key.csr 64 | openssl x509 -req -sha256 -in $< -signkey $(@:.crt=.key) -out $@ -days 2500 65 | 66 | # Short expiry certificate (OpenSSL requires min. of 1 day, so let's do that) 67 | example-expired.crt: example-expired.csr 68 | openssl x509 -req -sha256 -in $< -signkey $(@:.crt=.key) -out $@ -days 1 69 | 70 | # Example ECC key 71 | example-elliptic-sha1.key: 72 | openssl ecparam -genkey -name secp521r1 -out $@ 73 | 74 | example-elliptic-sha1.crt: example-elliptic-sha1.csr 75 | openssl x509 -req -in $< -signkey $(@:.crt=.key) -out $@ -days 2500 -extfile openssl.ext -extensions leaf 76 | 77 | # Custom OID 78 | example-custom-oid.csr: example-custom-oid.key 79 | openssl req -new -config example-custom-oid.conf -key $< -out $@ 80 | 81 | example-custom-oid.crt: example-custom-oid.csr 82 | openssl x509 -req -sha256 -in $< -signkey $(@:.crt=.key) -out $@ -days 2500 -extfile openssl.ext -extensions leaf 83 | 84 | example-name-constraints.crt: example-name-constraints.csr 85 | openssl x509 -req -sha256 -in $< -signkey $(@:.crt=.key) -out $@ -days 2500 -extfile openssl.ext -extensions root_with_constraints 86 | -------------------------------------------------------------------------------- /lib/ct.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package lib 17 | 18 | import ( 19 | "crypto/x509" 20 | "encoding/asn1" 21 | "encoding/base64" 22 | "time" 23 | 24 | cttls "github.com/google/certificate-transparency-go/tls" 25 | ctx509 "github.com/google/certificate-transparency-go/x509" 26 | ctutil "github.com/google/certificate-transparency-go/x509util" 27 | ) 28 | 29 | //go:generate go run github.com/square/certigo/internal/gen-known-logs --out ctlogs.go 30 | //go:generate go fmt ctlogs.go 31 | 32 | func parseSCTList(cert *x509.Certificate) []*simpleSCT { 33 | // ctutil contains a fork of crypto/x509 with support for SCTs. We must re-parse the 34 | // whole certificate to get at them, so do a quick check to see if the SCT extension 35 | // is present before re-parsing the cert unnecessarily. 36 | if !hasSCTs(cert) { 37 | return nil 38 | } 39 | 40 | var sctList []*simpleSCT 41 | if scts, err := ctutil.ParseSCTsFromCertificate(cert.Raw); err == nil { 42 | for _, sct := range scts { 43 | id := sct.LogID.KeyID[:] 44 | ssct := &simpleSCT{ 45 | Version: uint64(sct.SCTVersion), 46 | LogID: id, 47 | Timestamp: time.UnixMilli(int64(sct.Timestamp)), 48 | SignatureAlgorithm: sctSignatureAlg(sct.Signature.Algorithm), 49 | } 50 | if log := getLogByID(id); log != nil { 51 | ssct.LogOperator = log.operator 52 | ssct.LogURL = log.url 53 | } 54 | sctList = append(sctList, ssct) 55 | } 56 | } 57 | return sctList 58 | } 59 | 60 | func hasSCTs(cert *x509.Certificate) bool { 61 | for _, e := range cert.Extensions { 62 | if e.Id.Equal(asn1.ObjectIdentifier(ctx509.OIDExtensionCTSCT)) { 63 | return true 64 | } 65 | } 66 | return false 67 | } 68 | 69 | func getLogByID(id []byte) *ctLog { 70 | b64 := base64.StdEncoding.EncodeToString(id) 71 | return knownLogs[b64] 72 | } 73 | 74 | func sctSignatureAlg(alg cttls.SignatureAndHashAlgorithm) simpleSigAlg { 75 | x509Alg := x509.UnknownSignatureAlgorithm 76 | switch alg.Signature { 77 | case cttls.RSA: 78 | switch alg.Hash { 79 | case cttls.MD5: 80 | x509Alg = x509.MD5WithRSA 81 | case cttls.SHA1: 82 | x509Alg = x509.SHA1WithRSA 83 | case cttls.SHA256: 84 | x509Alg = x509.SHA256WithRSA 85 | case cttls.SHA384: 86 | x509Alg = x509.SHA384WithRSA 87 | case cttls.SHA512: 88 | x509Alg = x509.SHA512WithRSA 89 | } 90 | case cttls.DSA: 91 | switch alg.Hash { 92 | case cttls.SHA1: 93 | x509Alg = x509.DSAWithSHA1 94 | case cttls.SHA256: 95 | x509Alg = x509.DSAWithSHA256 96 | } 97 | case cttls.ECDSA: 98 | switch alg.Hash { 99 | case cttls.SHA1: 100 | x509Alg = x509.ECDSAWithSHA1 101 | case cttls.SHA256: 102 | x509Alg = x509.ECDSAWithSHA256 103 | case cttls.SHA384: 104 | x509Alg = x509.ECDSAWithSHA384 105 | case cttls.SHA512: 106 | x509Alg = x509.ECDSAWithSHA512 107 | } 108 | } 109 | return simpleSigAlg(x509Alg) 110 | } 111 | -------------------------------------------------------------------------------- /jceks/jceks_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package jceks 17 | 18 | import ( 19 | "bytes" 20 | "crypto/sha1" 21 | "errors" 22 | "flag" 23 | "os" 24 | "path/filepath" 25 | "testing" 26 | 27 | "github.com/stretchr/testify/require" 28 | ) 29 | 30 | func TestMain(m *testing.M) { 31 | flag.Parse() 32 | m.Run() 33 | } 34 | 35 | func TestEncodeIntegrityPassword(t *testing.T) { 36 | t.Parallel() 37 | 38 | for _, tc := range []struct { 39 | name string 40 | password string 41 | expected []byte 42 | expectErr bool 43 | }{ 44 | { 45 | name: "BasicASCII", 46 | password: "Hi!", 47 | expected: []byte{0, 'H', 0, 'i', 0, '!'}, 48 | }, 49 | { 50 | name: "BasicMultilingual", 51 | password: `¯\_(ツ)_/¯`, 52 | expected: []byte{0, 0xaf, 0, '\\', 0, '_', 0, '(', 0x30, 0xc4, 0, ')', 0, '_', 0, '/', 0, 0xaf}, 53 | }, 54 | { 55 | name: "ErrTooShort", 56 | expectErr: true, 57 | }, 58 | { 59 | name: "ErrOutOfRange", 60 | password: "\U00010000", 61 | expectErr: true, 62 | }, 63 | } { 64 | t.Run(tc.name, func(t *testing.T) { 65 | t.Parallel() 66 | 67 | actual, err := encodeIntegrityPassword(tc.password) 68 | if tc.expectErr { 69 | require.Error(t, err) 70 | require.ErrorIs(t, err, ErrInvalidPassword) 71 | 72 | return 73 | } 74 | require.NoError(t, err) 75 | require.Equal(t, tc.expected, actual) 76 | }) 77 | } 78 | } 79 | 80 | func TestMakeIntegrityHash(t *testing.T) { 81 | t.Parallel() 82 | 83 | h := makeIntegrityHash([]byte("changeit")) 84 | actual := h.Sum(nil) 85 | 86 | expected := sha1.Sum([]byte("changeit" + jceksIntegrityMagic)) 87 | 88 | require.Equal(t, expected[:], actual) 89 | } 90 | 91 | func FuzzLoadFromReader(f *testing.F) { 92 | for filename, password := range map[string]string{ 93 | "private-key.jceks": "store-password", 94 | "trusted-cert.jceks": "store-password", 95 | "encoder-private-store.jceks": "changeit", 96 | "encoder-cert-store.jceks": "changeit", 97 | "encoder-re-encode-private-key.jceks": "changeit", 98 | "encoder-re-encode-trusted-cert.jceks": "changeit", 99 | } { 100 | data, err := os.ReadFile(filepath.Join("testdata", filename)) 101 | require.NoError(f, err) 102 | f.Add(data, []byte(password)) 103 | } 104 | 105 | f.Fuzz(func(t *testing.T, data []byte, password []byte) { 106 | var ks KeyStore 107 | err := ks.ParseWithOptions(bytes.NewReader(data), password, 108 | WithMaxCertificateBytes(20*1024), WithMaxPrivateKeyBytes(20*1024)) 109 | if err != nil { 110 | for _, mightBe := range []error{ 111 | ErrInvalidPassword, 112 | ErrInvalidJCEKSData, 113 | ErrUnsupportedJCEKSData, 114 | ErrIntegrityProtectionViolation, 115 | } { 116 | if errors.Is(err, mightBe) { 117 | return 118 | } 119 | } 120 | t.Fatal(err) 121 | } 122 | }) 123 | } 124 | -------------------------------------------------------------------------------- /jceks/jceks.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // Package jceks parses JCEKS (Java Cryptogaphy Extension Key Store) 17 | // files and extracts keys and certificates. This module only implements 18 | // a fraction of the JCEKS cryptographic protocols. In particular, it 19 | // implements the SHA1 signature verification of the key store and the 20 | // PBEWithMD5AndDES3CBC cipher for encrypting private keys. 21 | package jceks 22 | 23 | import ( 24 | "crypto/sha1" 25 | "crypto/x509/pkix" 26 | "encoding/asn1" 27 | "encoding/binary" 28 | "errors" 29 | "fmt" 30 | "hash" 31 | "math" 32 | ) 33 | 34 | var ( 35 | ErrInvalidPassword = errors.New("password is unsupported by JCEKS format") 36 | ) 37 | 38 | const ( 39 | jceksMagic = 0xcececece 40 | jceksVersion = 0x02 41 | jksMagic = 0xfeedfeed 42 | privateKeyEntryTag uint32 = 1 43 | trustedCertEntryTag uint32 = 2 44 | secretKeyEntryTag uint32 = 3 45 | x509CertTag = "X.509" 46 | jceksIntegrityMagic = "Mighty Aphrodite" 47 | maxAliasLen = 0xFFFF 48 | ) 49 | 50 | var ( 51 | oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} 52 | oidPublicKeyEC = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1} 53 | ) 54 | 55 | type encryptedPrivateKeyInfo struct { 56 | Algo pkix.AlgorithmIdentifier 57 | EncryptedKey []byte 58 | } 59 | 60 | type privateKeyInfo struct { 61 | Version int 62 | Algo pkix.AlgorithmIdentifier 63 | PrivateKey []byte 64 | } 65 | 66 | type ecPrivateKey struct { 67 | Version int 68 | PrivateKey []byte 69 | NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"` 70 | PublicKey asn1.BitString `asn1:"optional,explicit,tag:1"` 71 | } 72 | 73 | // encodeIntegrityPassword transforms a password string into the byte sequence that the JCEKS format defines as input to 74 | // the integrity protection hash. The format does not support all possible password strings, so the function returns 75 | // ErrInvalidPassword for invalid passwords. 76 | func encodeIntegrityPassword(password string) ([]byte, error) { 77 | if len(password) < 1 { 78 | return nil, fmt.Errorf("%w: empty passwords are not interoperable", ErrInvalidPassword) 79 | } 80 | for _, r := range password { 81 | if r > math.MaxUint16 { 82 | return nil, fmt.Errorf("%w: password contains unsupported codepoints", ErrInvalidPassword) 83 | } 84 | } 85 | 86 | var integrityPassword []byte 87 | for _, r := range password { 88 | integrityPassword = binary.BigEndian.AppendUint16(integrityPassword, uint16(r)) 89 | } 90 | 91 | return integrityPassword, nil 92 | } 93 | 94 | // makeIntegrityHash initializes the hash function used to check JCEKS file integrity. 95 | func makeIntegrityHash(encodedPassword []byte) hash.Hash { 96 | h := sha1.New() 97 | h.Write(encodedPassword) 98 | h.Write([]byte(jceksIntegrityMagic)) 99 | 100 | return h 101 | } 102 | -------------------------------------------------------------------------------- /tests/dump-leaf-to-json.t: -------------------------------------------------------------------------------- 1 | Set up test data. 2 | 3 | $ cat > example-leaf.crt < -----BEGIN CERTIFICATE----- 5 | > MIIDfDCCAmSgAwIBAgIJANWAkzF7PA8/MA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNV 6 | > BAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMH 7 | > ZXhhbXBsZTEVMBMGA1UEAxMMZXhhbXBsZS1sZWFmMB4XDTE2MDYxMDIyMTQxMVoX 8 | > DTIzMDQxNTIyMTQxMVowVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYD 9 | > VQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRUwEwYDVQQDEwxleGFtcGxl 10 | > LWxlYWYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7stSvfQyGuHw3 11 | > v34fisqIdDXberrFoFk9ht/WdXgYzX2uLNKdsR/J5sbWSl8K/5djpzj31eIzqU69 12 | > w8v7SChM5x9bouDsABHz3kZucx5cSafEgJojysBkcrq3VY+aJanzbL+qErYX+lhR 13 | > pPcZK6JMWIwar8Y3B2la4yWwieecw2/WfEVvG0M/DOYKnR8QHFsfl3US1dnBM84c 14 | > zKPyt9r40gDk2XiH/lGts5a94rAGvbr8IMCtq0mA5aH3Fx3mDSi3+4MZwygCAHrF 15 | > 5O5iSV9rEI+m2+7j2S+jHDUnvV+nqcpb9m6ENECnYX8FD2KcqlOjTmw8smDy09N2 16 | > Np6i464lAgMBAAGjTzBNMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAs 17 | > BgNVHREEJTAjhwR/AAABhxAAAAAAAAAAAAAAAAAAAAABgglsb2NhbGhvc3QwDQYJ 18 | > KoZIhvcNAQELBQADggEBAGM4aa/qrURUweZBIwZYv8O9b2+r4l0HjGAh982/B9sM 19 | > lM05kojyDCUGvj86z18Lm8mKr4/y+i0nJ+vDIksEvfDuzw5ALAXGcBzPJKtICUf7 20 | > LstA/n9NNpshWz0kld9ylnB5mbUzSFDncVyeXkEf5sGQXdIIZT9ChRBoiloSaa7d 21 | > vBVCcsX1LGP2LWqKtD+7nUnw5qCwtyAVT8pthEUxFTpywoiJS5ZdzeEx8MNGvUeL 22 | > Fj2kleqPF78EioEQlSOxViCuctEtnQuPcDLHNFr10byTZY9roObiqdsJLMVvb2Xl 23 | > iJjAqaPa9AkYwGE6xHw2ispwg64Rse0+AtKups19WIU= 24 | > -----END CERTIFICATE----- 25 | > EOF 26 | 27 | Dump an example certificate (example-leaf.crt) to JSON output 28 | 29 | $ certigo dump --json example-leaf.crt 30 | {"certificates":[{"serial":"15384458167827828543","not_before":"2016-06-10T22:14:11Z","not_after":"2023-04-15T22:14:11Z","signature_algorithm":"SHA256-RSA","is_self_signed":false,"subject":{"common_name":"example-leaf","country":["US"],"organization":["certigo"],"organizational_unit":["example"],"province":["CA"]},"issuer":{"common_name":"example-leaf","country":["US"],"organization":["certigo"],"organizational_unit":["example"],"province":["CA"]},"extended_key_usage":["Client Auth","Server Auth"],"dns_names":["localhost"],"ip_addresses":["127.0.0.1","::1"],"lints":["ERROR: [RFC5280] CAs must include keyIdentifer field of AKI in all non-self-issued certificates","WARN: [RFC5280] Sub certificates SHOULD include Subject Key Identifier in end entity certs"],"pem":"-----BEGIN CERTIFICATE-----\nMIIDfDCCAmSgAwIBAgIJANWAkzF7PA8/MA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNV\nBAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMH\nZXhhbXBsZTEVMBMGA1UEAxMMZXhhbXBsZS1sZWFmMB4XDTE2MDYxMDIyMTQxMVoX\nDTIzMDQxNTIyMTQxMVowVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYD\nVQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRUwEwYDVQQDEwxleGFtcGxl\nLWxlYWYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7stSvfQyGuHw3\nv34fisqIdDXberrFoFk9ht/WdXgYzX2uLNKdsR/J5sbWSl8K/5djpzj31eIzqU69\nw8v7SChM5x9bouDsABHz3kZucx5cSafEgJojysBkcrq3VY+aJanzbL+qErYX+lhR\npPcZK6JMWIwar8Y3B2la4yWwieecw2/WfEVvG0M/DOYKnR8QHFsfl3US1dnBM84c\nzKPyt9r40gDk2XiH/lGts5a94rAGvbr8IMCtq0mA5aH3Fx3mDSi3+4MZwygCAHrF\n5O5iSV9rEI+m2+7j2S+jHDUnvV+nqcpb9m6ENECnYX8FD2KcqlOjTmw8smDy09N2\nNp6i464lAgMBAAGjTzBNMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAs\nBgNVHREEJTAjhwR/AAABhxAAAAAAAAAAAAAAAAAAAAABgglsb2NhbGhvc3QwDQYJ\nKoZIhvcNAQELBQADggEBAGM4aa/qrURUweZBIwZYv8O9b2+r4l0HjGAh982/B9sM\nlM05kojyDCUGvj86z18Lm8mKr4/y+i0nJ+vDIksEvfDuzw5ALAXGcBzPJKtICUf7\nLstA/n9NNpshWz0kld9ylnB5mbUzSFDncVyeXkEf5sGQXdIIZT9ChRBoiloSaa7d\nvBVCcsX1LGP2LWqKtD+7nUnw5qCwtyAVT8pthEUxFTpywoiJS5ZdzeEx8MNGvUeL\nFj2kleqPF78EioEQlSOxViCuctEtnQuPcDLHNFr10byTZY9roObiqdsJLMVvb2Xl\niJjAqaPa9AkYwGE6xHw2ispwg64Rse0+AtKups19WIU=\n-----END CERTIFICATE-----\n"}]} 31 | -------------------------------------------------------------------------------- /jceks/decoder_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package jceks 17 | 18 | import ( 19 | "crypto/rsa" 20 | "os" 21 | "testing" 22 | 23 | "github.com/stretchr/testify/require" 24 | ) 25 | 26 | //go:generate testdata/generate-jceks.sh 27 | 28 | func TestPrivateKey(t *testing.T) { 29 | ks, err := LoadFromFile("testdata/private-key.jceks", []byte("store-password")) 30 | require.NoError(t, err) 31 | key, certs, err := ks.GetPrivateKeyAndCerts("private-key-some-alias", []byte("key-password")) 32 | require.NoError(t, err) 33 | require.NotNil(t, key) 34 | require.IsType(t, &rsa.PrivateKey{}, key) 35 | rsaKey := key.(*rsa.PrivateKey) 36 | 37 | expected, err := LoadPEMKey("testdata/private-key.key") 38 | require.NoError(t, err) 39 | require.Equal(t, expected, rsaKey) 40 | require.True(t, rsaKey.Equal(expected)) 41 | 42 | expectedLeafCert, err := LoadPEMCert("testdata/private-key.crt") 43 | require.NoError(t, err) 44 | expectedCACert, err := LoadPEMCert("testdata/private-key-ca.crt") 45 | require.NoError(t, err) 46 | 47 | require.Len(t, certs, 2) 48 | require.True(t, certs[0].Equal(expectedLeafCert)) 49 | require.True(t, certs[1].Equal(expectedCACert)) 50 | 51 | keyAliases := ks.ListPrivateKeys() 52 | require.Equal(t, []string{"private-key-some-alias"}, keyAliases) 53 | } 54 | 55 | func TestTrustedCert(t *testing.T) { 56 | ks, err := LoadFromFile("testdata/trusted-cert.jceks", []byte("store-password")) 57 | require.NoError(t, err) 58 | cert, err := ks.GetCert("trusted-cert-some-alias") 59 | require.NoError(t, err) 60 | require.NotNil(t, cert) 61 | 62 | expectedCert, err := LoadPEMCert("testdata/trusted-cert.crt") 63 | require.NoError(t, err) 64 | 65 | require.True(t, cert.Equal(expectedCert)) 66 | 67 | certAliases := ks.ListCerts() 68 | require.Equal(t, []string{"trusted-cert-some-alias"}, certAliases) 69 | } 70 | 71 | func TestLoadFromReader(t *testing.T) { 72 | f, err := os.Open("testdata/trusted-cert.jceks") 73 | require.NoError(t, err) 74 | 75 | ks, err := LoadFromReader(f, []byte("store-password")) 76 | require.NoError(t, err) 77 | cert, err := ks.GetCert("trusted-cert-some-alias") 78 | require.NoError(t, err) 79 | require.NotNil(t, cert) 80 | 81 | err = f.Close() 82 | require.NoError(t, err) 83 | } 84 | 85 | func TestParseWithEmpty(t *testing.T) { 86 | f, err := os.Open("testdata/trusted-cert.jceks") 87 | require.NoError(t, err) 88 | 89 | var ks KeyStore 90 | err = ks.Parse(f, []byte("store-password")) 91 | require.NoError(t, err) 92 | cert, err := ks.GetCert("trusted-cert-some-alias") 93 | require.NoError(t, err) 94 | require.NotNil(t, cert) 95 | 96 | err = f.Close() 97 | require.NoError(t, err) 98 | } 99 | 100 | func TestEmptyKeyStoreReadyToUse(t *testing.T) { 101 | var ks KeyStore 102 | 103 | certs := ks.ListCerts() 104 | require.Empty(t, certs) 105 | 106 | keys := ks.ListPrivateKeys() 107 | require.Empty(t, keys) 108 | 109 | cert, err := ks.GetCert("non-existent") 110 | require.NoError(t, err) 111 | require.Nil(t, cert) 112 | 113 | sk, keyCerts, err := ks.GetPrivateKeyAndCerts("non-existent", []byte("password")) 114 | require.NoError(t, err) 115 | require.Nil(t, sk) 116 | require.Empty(t, keyCerts) 117 | } 118 | -------------------------------------------------------------------------------- /starttls/mysql/const.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | const ( 12 | minProtocolVersion byte = 10 13 | maxPacketSize = 1<<24 - 1 14 | timeFormat = "2006-01-02 15:04:05.999999" 15 | ) 16 | 17 | // MySQL constants documentation: 18 | // http://dev.mysql.com/doc/internals/en/client-server-protocol.html 19 | 20 | const ( 21 | iOK byte = 0x00 22 | iLocalInFile byte = 0xfb 23 | iEOF byte = 0xfe 24 | iERR byte = 0xff 25 | ) 26 | 27 | // https://dev.mysql.com/doc/internals/en/capability-flags.html#packet-Protocol::CapabilityFlags 28 | type clientFlag uint32 29 | 30 | const ( 31 | clientLongPassword clientFlag = 1 << iota 32 | clientFoundRows 33 | clientLongFlag 34 | clientConnectWithDB 35 | clientNoSchema 36 | clientCompress 37 | clientODBC 38 | clientLocalFiles 39 | clientIgnoreSpace 40 | clientProtocol41 41 | clientInteractive 42 | clientSSL 43 | clientIgnoreSIGPIPE 44 | clientTransactions 45 | clientReserved 46 | clientSecureConn 47 | clientMultiStatements 48 | clientMultiResults 49 | clientPSMultiResults 50 | clientPluginAuth 51 | clientConnectAttrs 52 | clientPluginAuthLenEncClientData 53 | clientCanHandleExpiredPasswords 54 | clientSessionTrack 55 | clientDeprecateEOF 56 | ) 57 | 58 | const ( 59 | comQuit byte = iota + 1 60 | comInitDB 61 | comQuery 62 | comFieldList 63 | comCreateDB 64 | comDropDB 65 | comRefresh 66 | comShutdown 67 | comStatistics 68 | comProcessInfo 69 | comConnect 70 | comProcessKill 71 | comDebug 72 | comPing 73 | comTime 74 | comDelayedInsert 75 | comChangeUser 76 | comBinlogDump 77 | comTableDump 78 | comConnectOut 79 | comRegisterSlave 80 | comStmtPrepare 81 | comStmtExecute 82 | comStmtSendLongData 83 | comStmtClose 84 | comStmtReset 85 | comSetOption 86 | comStmtFetch 87 | ) 88 | 89 | // https://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnType 90 | const ( 91 | fieldTypeDecimal byte = iota 92 | fieldTypeTiny 93 | fieldTypeShort 94 | fieldTypeLong 95 | fieldTypeFloat 96 | fieldTypeDouble 97 | fieldTypeNULL 98 | fieldTypeTimestamp 99 | fieldTypeLongLong 100 | fieldTypeInt24 101 | fieldTypeDate 102 | fieldTypeTime 103 | fieldTypeDateTime 104 | fieldTypeYear 105 | fieldTypeNewDate 106 | fieldTypeVarChar 107 | fieldTypeBit 108 | ) 109 | const ( 110 | fieldTypeJSON byte = iota + 0xf5 111 | fieldTypeNewDecimal 112 | fieldTypeEnum 113 | fieldTypeSet 114 | fieldTypeTinyBLOB 115 | fieldTypeMediumBLOB 116 | fieldTypeLongBLOB 117 | fieldTypeBLOB 118 | fieldTypeVarString 119 | fieldTypeString 120 | fieldTypeGeometry 121 | ) 122 | 123 | type fieldFlag uint16 124 | 125 | const ( 126 | flagNotNULL fieldFlag = 1 << iota 127 | flagPriKey 128 | flagUniqueKey 129 | flagMultipleKey 130 | flagBLOB 131 | flagUnsigned 132 | flagZeroFill 133 | flagBinary 134 | flagEnum 135 | flagAutoIncrement 136 | flagTimestamp 137 | flagSet 138 | flagUnknown1 139 | flagUnknown2 140 | flagUnknown3 141 | flagUnknown4 142 | ) 143 | 144 | // http://dev.mysql.com/doc/internals/en/status-flags.html 145 | type statusFlag uint16 146 | 147 | const ( 148 | statusInTrans statusFlag = 1 << iota 149 | statusInAutocommit 150 | statusReserved // Not in documentation 151 | statusMoreResultsExists 152 | statusNoGoodIndexUsed 153 | statusNoIndexUsed 154 | statusCursorExists 155 | statusLastRowSent 156 | statusDbDropped 157 | statusNoBackslashEscapes 158 | statusMetadataChanged 159 | statusQueryWasSlow 160 | statusPsOutParams 161 | statusInTransReadonly 162 | statusSessionStateChanged 163 | ) 164 | -------------------------------------------------------------------------------- /starttls/mysql/buffer.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | import ( 12 | "io" 13 | "net" 14 | "time" 15 | ) 16 | 17 | const defaultBufSize = 4096 18 | 19 | // A buffer which is used for both reading and writing. 20 | // This is possible since communication on each connection is synchronous. 21 | // In other words, we can't write and read simultaneously on the same connection. 22 | // The buffer is similar to bufio.Reader / Writer but zero-copy-ish 23 | // Also highly optimized for this particular use case. 24 | type buffer struct { 25 | buf []byte 26 | nc net.Conn 27 | idx int 28 | length int 29 | timeout time.Duration 30 | } 31 | 32 | func newBuffer(nc net.Conn) buffer { 33 | var b [defaultBufSize]byte 34 | return buffer{ 35 | buf: b[:], 36 | nc: nc, 37 | } 38 | } 39 | 40 | // fill reads into the buffer until at least _need_ bytes are in it 41 | func (b *buffer) fill(need int) error { 42 | n := b.length 43 | 44 | // move existing data to the beginning 45 | if n > 0 && b.idx > 0 { 46 | copy(b.buf[0:n], b.buf[b.idx:]) 47 | } 48 | 49 | // grow buffer if necessary 50 | // TODO: let the buffer shrink again at some point 51 | // Maybe keep the org buf slice and swap back? 52 | if need > len(b.buf) { 53 | // Round up to the next multiple of the default size 54 | newBuf := make([]byte, ((need/defaultBufSize)+1)*defaultBufSize) 55 | copy(newBuf, b.buf) 56 | b.buf = newBuf 57 | } 58 | 59 | b.idx = 0 60 | 61 | for { 62 | if b.timeout > 0 { 63 | if err := b.nc.SetReadDeadline(time.Now().Add(b.timeout)); err != nil { 64 | return err 65 | } 66 | } 67 | 68 | nn, err := b.nc.Read(b.buf[n:]) 69 | n += nn 70 | 71 | switch err { 72 | case nil: 73 | if n < need { 74 | continue 75 | } 76 | b.length = n 77 | return nil 78 | 79 | case io.EOF: 80 | if n >= need { 81 | b.length = n 82 | return nil 83 | } 84 | return io.ErrUnexpectedEOF 85 | 86 | default: 87 | return err 88 | } 89 | } 90 | } 91 | 92 | // returns next N bytes from buffer. 93 | // The returned slice is only guaranteed to be valid until the next read 94 | func (b *buffer) readNext(need int) ([]byte, error) { 95 | if b.length < need { 96 | // refill 97 | if err := b.fill(need); err != nil { 98 | return nil, err 99 | } 100 | } 101 | 102 | offset := b.idx 103 | b.idx += need 104 | b.length -= need 105 | return b.buf[offset:b.idx], nil 106 | } 107 | 108 | // returns a buffer with the requested size. 109 | // If possible, a slice from the existing buffer is returned. 110 | // Otherwise a bigger buffer is made. 111 | // Only one buffer (total) can be used at a time. 112 | func (b *buffer) takeBuffer(length int) []byte { 113 | if b.length > 0 { 114 | return nil 115 | } 116 | 117 | // test (cheap) general case first 118 | if length <= defaultBufSize || length <= cap(b.buf) { 119 | return b.buf[:length] 120 | } 121 | 122 | if length < maxPacketSize { 123 | b.buf = make([]byte, length) 124 | return b.buf 125 | } 126 | return make([]byte, length) 127 | } 128 | 129 | // shortcut which can be used if the requested buffer is guaranteed to be 130 | // smaller than defaultBufSize 131 | // Only one buffer (total) can be used at a time. 132 | func (b *buffer) takeSmallBuffer(length int) []byte { 133 | if b.length == 0 { 134 | return b.buf[:length] 135 | } 136 | return nil 137 | } 138 | 139 | // takeCompleteBuffer returns the complete existing buffer. 140 | // This can be used if the necessary buffer size is unknown. 141 | // Only one buffer (total) can be used at a time. 142 | func (b *buffer) takeCompleteBuffer() []byte { 143 | if b.length == 0 { 144 | return b.buf 145 | } 146 | return nil 147 | } 148 | -------------------------------------------------------------------------------- /starttls/mysql/statement.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | import ( 12 | "database/sql/driver" 13 | "fmt" 14 | "reflect" 15 | "strconv" 16 | ) 17 | 18 | type mysqlStmt struct { 19 | mc *mysqlConn 20 | id uint32 21 | paramCount int 22 | columns []mysqlField // cached from the first query 23 | } 24 | 25 | func (stmt *mysqlStmt) Close() error { 26 | if stmt.mc == nil || stmt.mc.netConn == nil { 27 | // driver.Stmt.Close can be called more than once, thus this function 28 | // has to be idempotent. 29 | // See also Issue #450 and golang/go#16019. 30 | //errLog.Print(ErrInvalidConn) 31 | return driver.ErrBadConn 32 | } 33 | 34 | err := stmt.mc.writeCommandPacketUint32(comStmtClose, stmt.id) 35 | stmt.mc = nil 36 | return err 37 | } 38 | 39 | func (stmt *mysqlStmt) NumInput() int { 40 | return stmt.paramCount 41 | } 42 | 43 | func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter { 44 | return converter{} 45 | } 46 | 47 | func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) { 48 | if stmt.mc.netConn == nil { 49 | errLog.Print(ErrInvalidConn) 50 | return nil, driver.ErrBadConn 51 | } 52 | // Send command 53 | err := stmt.writeExecutePacket(args) 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | mc := stmt.mc 59 | 60 | mc.affectedRows = 0 61 | mc.insertId = 0 62 | 63 | // Read Result 64 | resLen, err := mc.readResultSetHeaderPacket() 65 | if err == nil { 66 | if resLen > 0 { 67 | // Columns 68 | err = mc.readUntilEOF() 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | // Rows 74 | err = mc.readUntilEOF() 75 | } 76 | if err == nil { 77 | return &mysqlResult{ 78 | affectedRows: int64(mc.affectedRows), 79 | insertId: int64(mc.insertId), 80 | }, nil 81 | } 82 | } 83 | 84 | return nil, err 85 | } 86 | 87 | func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) { 88 | if stmt.mc.netConn == nil { 89 | errLog.Print(ErrInvalidConn) 90 | return nil, driver.ErrBadConn 91 | } 92 | // Send command 93 | err := stmt.writeExecutePacket(args) 94 | if err != nil { 95 | return nil, err 96 | } 97 | 98 | mc := stmt.mc 99 | 100 | // Read Result 101 | resLen, err := mc.readResultSetHeaderPacket() 102 | if err != nil { 103 | return nil, err 104 | } 105 | 106 | rows := new(binaryRows) 107 | 108 | if resLen > 0 { 109 | rows.mc = mc 110 | // Columns 111 | // If not cached, read them and cache them 112 | if stmt.columns == nil { 113 | rows.columns, err = mc.readColumns(resLen) 114 | stmt.columns = rows.columns 115 | } else { 116 | rows.columns = stmt.columns 117 | err = mc.readUntilEOF() 118 | } 119 | } 120 | 121 | return rows, err 122 | } 123 | 124 | type converter struct{} 125 | 126 | func (c converter) ConvertValue(v interface{}) (driver.Value, error) { 127 | if driver.IsValue(v) { 128 | return v, nil 129 | } 130 | 131 | rv := reflect.ValueOf(v) 132 | switch rv.Kind() { 133 | case reflect.Ptr: 134 | // indirect pointers 135 | if rv.IsNil() { 136 | return nil, nil 137 | } 138 | return c.ConvertValue(rv.Elem().Interface()) 139 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 140 | return rv.Int(), nil 141 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: 142 | return int64(rv.Uint()), nil 143 | case reflect.Uint64: 144 | u64 := rv.Uint() 145 | if u64 >= 1<<63 { 146 | return strconv.FormatUint(u64, 10), nil 147 | } 148 | return int64(u64), nil 149 | case reflect.Float32, reflect.Float64: 150 | return rv.Float(), nil 151 | } 152 | return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind()) 153 | } 154 | -------------------------------------------------------------------------------- /jceks/encoding.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package jceks 17 | 18 | import ( 19 | "crypto/x509" 20 | "encoding/binary" 21 | "fmt" 22 | "io" 23 | "time" 24 | ) 25 | 26 | // readBytes reads a byte array from the reader. The encoding provides a 4-byte prefix indicating the number of bytes 27 | // that follow. 28 | func readBytes(r io.Reader, maxLen uint) ([]byte, error) { 29 | length, err := readInt32(r) 30 | if err != nil { 31 | return nil, err 32 | } 33 | if length < 0 { 34 | return nil, ErrInvalidJCEKSData 35 | } 36 | if uint(length) > maxLen { 37 | return nil, fmt.Errorf("%w: data field of size %d bytes exceeds maximimum length of %d", 38 | ErrJCEKSDataTooLarge, length, maxLen) 39 | } 40 | buf := make([]byte, length) 41 | _, err = io.ReadFull(r, buf) 42 | if err != nil { 43 | return nil, err 44 | } 45 | return buf, nil 46 | } 47 | 48 | func writeBytes(w io.Writer, b []byte) error { 49 | if err := binary.Write(w, binary.BigEndian, uint32(len(b))); err != nil { 50 | return err 51 | } 52 | if _, err := w.Write(b); err != nil { 53 | return err 54 | } 55 | 56 | return nil 57 | } 58 | 59 | func readInt32(r io.Reader) (int32, error) { 60 | var v int32 61 | err := binary.Read(r, binary.BigEndian, &v) 62 | return v, err 63 | } 64 | 65 | func writeInt32(w io.Writer, v int32) error { 66 | return binary.Write(w, binary.BigEndian, v) 67 | } 68 | 69 | func readUint32(r io.Reader) (uint32, error) { 70 | var v uint32 71 | err := binary.Read(r, binary.BigEndian, &v) 72 | return v, err 73 | } 74 | 75 | func writeUint32(w io.Writer, v uint32) error { 76 | return binary.Write(w, binary.BigEndian, v) 77 | } 78 | 79 | func readDate(r io.Reader) (time.Time, error) { 80 | var v int64 81 | err := binary.Read(r, binary.BigEndian, &v) 82 | if err != nil { 83 | return time.Time{}, err 84 | } 85 | return time.UnixMilli(v), nil 86 | } 87 | 88 | func writeDate(w io.Writer, v time.Time) error { 89 | return binary.Write(w, binary.BigEndian, v.UnixMilli()) 90 | } 91 | 92 | // readString reads a length-prefixed modified UTF-8 string. 93 | func readString(r io.Reader) (string, error) { 94 | var length uint16 95 | err := binary.Read(r, binary.BigEndian, &length) 96 | if err != nil { 97 | return "", err 98 | } 99 | 100 | return readModifiedUTF8(io.LimitReader(r, int64(length))) 101 | } 102 | 103 | func writeString(w io.Writer, str string) error { 104 | if err := binary.Write(w, binary.BigEndian, uint16(len(str))); err != nil { 105 | return err 106 | } 107 | if err := writeModifiedUTF8(w, str); err != nil { 108 | return err 109 | } 110 | 111 | return nil 112 | } 113 | 114 | func readCertificate(r io.Reader, maxLen uint) (*x509.Certificate, error) { 115 | certType, err := readString(r) 116 | if err != nil { 117 | return nil, err 118 | } 119 | if certType != x509CertTag { 120 | return nil, fmt.Errorf("%w: unable to handle certificate type: %s", ErrUnsupportedJCEKSData, certType) 121 | } 122 | certDER, err := readBytes(r, maxLen) 123 | if err != nil { 124 | return nil, err 125 | } 126 | cert, err := x509.ParseCertificate(certDER) 127 | if err != nil { 128 | return nil, fmt.Errorf("failed to parse certificate: %w", err) 129 | } 130 | 131 | return cert, nil 132 | } 133 | 134 | func writeCertificate(w io.Writer, certDER []byte) error { 135 | if err := writeString(w, x509CertTag); err != nil { 136 | return err 137 | } 138 | if err := writeBytes(w, certDER); err != nil { 139 | return err 140 | } 141 | 142 | return nil 143 | } 144 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test 3 | 4 | on: 5 | push: 6 | branches: [master] 7 | pull_request: 8 | branches: [master] 9 | 10 | jobs: 11 | build: 12 | name: Unit tests 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: read 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4 19 | - name: Setup Go environment 20 | uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # ratchet:actions/setup-go@v5 21 | with: 22 | go-version: 'stable' 23 | check-latest: true 24 | - name: Build binary 25 | run: go build 26 | - name: Run tests 27 | run: go test -v ./... 28 | integration: 29 | name: Integration tests 30 | runs-on: ubuntu-latest 31 | permissions: 32 | contents: read 33 | steps: 34 | - name: Setup Go environment 35 | uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # ratchet:actions/setup-go@v5 36 | with: 37 | go-version: 'stable' 38 | check-latest: true 39 | - name: Setup Python 40 | uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # ratchet:actions/setup-python@v5 41 | with: 42 | python-version: '3.x' 43 | - name: Install cram 44 | run: pip install cram 45 | - name: Checkout 46 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4 47 | - name: Build binary 48 | run: go build 49 | - name: Run tests 50 | run: PATH=$PWD:$PATH cram -v tests/*.t 51 | integration-jceks: 52 | name: JCEKS Integration tests 53 | runs-on: ubuntu-latest 54 | permissions: 55 | contents: read 56 | strategy: 57 | matrix: 58 | include: 59 | # Oldest version for all tested distributions 60 | - distribution: temurin 61 | java-version: 8 62 | java-package: jre 63 | - distribution: zulu 64 | java-version: 8 65 | java-package: jre 66 | - distribution: microsoft 67 | java-version: 11 68 | java-package: jdk 69 | - distribution: corretto 70 | java-version: 8 71 | java-package: jdk 72 | - distribution: oracle 73 | java-version: 17 74 | java-package: jdk 75 | 76 | # Intermediate LTS versions for Temurin 77 | - distribution: temurin 78 | java-version: 11 79 | java-package: jre 80 | - distribution: temurin 81 | java-version: 17 82 | java-package: jre 83 | - distribution: temurin 84 | java-version: 21 85 | java-package: jre 86 | 87 | # Newest versions for all tested distributions 88 | - distribution: temurin 89 | java-version: 24 90 | java-package: jre 91 | - distribution: zulu 92 | java-version: 24 93 | java-package: jre 94 | - distribution: microsoft 95 | java-version: 21 96 | java-package: jdk 97 | - distribution: corretto 98 | java-version: 24 99 | java-package: jdk 100 | - distribution: oracle 101 | java-version: 24 102 | java-package: jdk 103 | steps: 104 | - name: Checkout 105 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4 106 | - name: Setup Go environment 107 | uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # ratchet:actions/setup-go@v5 108 | with: 109 | go-version: 'stable' 110 | check-latest: true 111 | - name: Setup java 112 | uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # ratchet:actions/setup-java@v4 113 | with: 114 | distribution: ${{ matrix.distribution }} 115 | java-version: ${{ matrix.java-version }} 116 | java-package: ${{ matrix.java-package }} 117 | - name: Run tests 118 | run: 119 | go test -v github.com/square/certigo/jceks -test.run "TestIntegrationKeytool*" -jceks.keytool-tests=true 120 | -------------------------------------------------------------------------------- /starttls/mysql/errors.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | import ( 12 | "database/sql/driver" 13 | "errors" 14 | "fmt" 15 | "io" 16 | "log" 17 | "os" 18 | ) 19 | 20 | // Various errors the driver might return. Can change between driver versions. 21 | var ( 22 | ErrInvalidConn = errors.New("invalid connection") 23 | ErrMalformPkt = errors.New("malformed packet") 24 | ErrNoTLS = errors.New("TLS requested but server does not support TLS") 25 | ErrCleartextPassword = errors.New("this user requires clear text authentication. If you still want to use it, please add 'allowCleartextPasswords=1' to your DSN") 26 | ErrNativePassword = errors.New("this user requires mysql native password authentication.") 27 | ErrOldPassword = errors.New("this user requires old password authentication. If you still want to use it, please add 'allowOldPasswords=1' to your DSN. See also https://github.com/go-sql-driver/mysql/wiki/old_passwords") 28 | ErrUnknownPlugin = errors.New("this authentication plugin is not supported") 29 | ErrOldProtocol = errors.New("MySQL server does not support required protocol 41+") 30 | ErrPktSync = errors.New("commands out of sync. You can't run this command now") 31 | ErrPktSyncMul = errors.New("commands out of sync. Did you run multiple statements at once?") 32 | ErrPktTooLarge = errors.New("packet for query is too large. Try adjusting the 'max_allowed_packet' variable on the server") 33 | ErrBusyBuffer = errors.New("busy buffer") 34 | ) 35 | 36 | var errLog = Logger(log.New(os.Stderr, "[mysql] ", log.Ldate|log.Ltime|log.Lshortfile)) 37 | 38 | // Logger is used to log critical error messages. 39 | type Logger interface { 40 | Print(v ...interface{}) 41 | } 42 | 43 | // SetLogger is used to set the logger for critical errors. 44 | // The initial logger is os.Stderr. 45 | func SetLogger(logger Logger) error { 46 | if logger == nil { 47 | return errors.New("logger is nil") 48 | } 49 | errLog = logger 50 | return nil 51 | } 52 | 53 | // MySQLError is an error type which represents a single MySQL error 54 | type MySQLError struct { 55 | Number uint16 56 | Message string 57 | } 58 | 59 | func (me *MySQLError) Error() string { 60 | return fmt.Sprintf("Error %d: %s", me.Number, me.Message) 61 | } 62 | 63 | // MySQLWarnings is an error type which represents a group of one or more MySQL 64 | // warnings 65 | type MySQLWarnings []MySQLWarning 66 | 67 | func (mws MySQLWarnings) Error() string { 68 | var msg string 69 | for i, warning := range mws { 70 | if i > 0 { 71 | msg += "\r\n" 72 | } 73 | msg += fmt.Sprintf( 74 | "%s %s: %s", 75 | warning.Level, 76 | warning.Code, 77 | warning.Message, 78 | ) 79 | } 80 | return msg 81 | } 82 | 83 | // MySQLWarning is an error type which represents a single MySQL warning. 84 | // Warnings are returned in groups only. See MySQLWarnings 85 | type MySQLWarning struct { 86 | Level string 87 | Code string 88 | Message string 89 | } 90 | 91 | func (mc *mysqlConn) getWarnings() (err error) { 92 | rows, err := mc.Query("SHOW WARNINGS", nil) 93 | if err != nil { 94 | return 95 | } 96 | 97 | var warnings = MySQLWarnings{} 98 | var values = make([]driver.Value, 3) 99 | 100 | for { 101 | err = rows.Next(values) 102 | switch err { 103 | case nil: 104 | warning := MySQLWarning{} 105 | 106 | if raw, ok := values[0].([]byte); ok { 107 | warning.Level = string(raw) 108 | } else { 109 | warning.Level = fmt.Sprintf("%s", values[0]) 110 | } 111 | if raw, ok := values[1].([]byte); ok { 112 | warning.Code = string(raw) 113 | } else { 114 | warning.Code = fmt.Sprintf("%s", values[1]) 115 | } 116 | if raw, ok := values[2].([]byte); ok { 117 | warning.Message = string(raw) 118 | } else { 119 | warning.Message = fmt.Sprintf("%s", values[0]) 120 | } 121 | 122 | warnings = append(warnings, warning) 123 | 124 | case io.EOF: 125 | return warnings 126 | 127 | default: 128 | rows.Close() 129 | return 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /tests/dump-spiffe-cert-to-json.t: -------------------------------------------------------------------------------- 1 | Set up test data. 2 | 3 | $ cat > example-spiffe.crt < -----BEGIN CERTIFICATE----- 5 | > MIIE1DCCArygAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzELMAkGA1UEBhMCVVMx 6 | > FzAVBgNVBAoMDnRlc3QxLmFjbWUuY29tMRcwFQYDVQQDDA5JbnRlcm1lZGlhZXRD 7 | > QTAeFw0xNzA3MTkxNjUwMjBaFw0xNzA3MjkxNjUwMjBaMDUxCzAJBgNVBAYTAlVT 8 | > MRcwFQYDVQQKDA50ZXN0MS5hY21lLmNvbTENMAsGA1UEAwwEYmxvZzCCASIwDQYJ 9 | > KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKm8P47lABp4+rz2nN+QYrxedbaFVWoF 10 | > FuoSkqcHsafMwbMrN+kI6wJVtlbwviDvxWFJ92q0H71QNFybTsmof3KUN/kYCp7P 11 | > +LKhBrN0ttWI5q6v5eDrjN0VdtVdnlZOYmJFbvETOgfK/qXKNRRM8HYW0tdqrtEw 12 | > CR5dIu53xVUSViBdwXpuy2c5W2mFn1gxTpdW+3hbZsL1pHrU9qPWLtTgl/KY8kjs 13 | > I7KW1cIcinE4SJomhB5L/4emhxKGY+kEa2+fN9IPjjvKSMOw9kiBKk1GHZcIY5EA 14 | > O3TIfUk3fysPzi5qA0su/bNtPQy1uXgXS10xUlV7pqRPvHjiNzgFkXUCAwEAAaOB 15 | > 4zCB4DAJBgNVHRMEAjAAMB0GA1UdDgQWBBRVQ91jSOONzVr1VGBdJOlPN+3XxTBg 16 | > BgNVHSMEWTBXgBQ13bfx50rDZO3y2CZdHPgleFUEoKE7pDkwNzELMAkGA1UEBhMC 17 | > VVMxFzAVBgNVBAoMDnRlc3QxLmFjbWUuY29tMQ8wDQYDVQQDDAZSb290Q0GCAhAA 18 | > MA4GA1UdDwEB/wQEAwIDqDATBgNVHSUEDDAKBggrBgEFBQcDATAtBgNVHREEJjAk 19 | > hiJzcGlmZmU6Ly9kZXYuYWNtZS5jb20vcGF0aC9zZXJ2aWNlMA0GCSqGSIb3DQEB 20 | > CwUAA4ICAQBp2+rtUxt1VmNM/vi6PwoSoYzWFmQ2nc4OM7bsOG4uppU54wRYZ+T7 21 | > c42EcrpyBgWn+rWHT1Hi6SNcmloKHydaUTZ4pq3IlKKnBNqwivU5BzIxYLDrhR/U 22 | > wd9s1tgmLvADqkQa1XjjSFn5Auoj1R640ry4qpw8IOusdm6wVhru4ssRnHX4E2uR 23 | > jQe7b3ws38aZhjtL78Ip0BB4yPxWJRp/WmEoT33QP+cZhA4IYWECxNODr6DSJeq2 24 | > VNu/6JACGrNfM2Sjt4Wxz+nIa3cKDNCA6PR8StTUTcoQ6ZBzpn+n/Q1xSRIOJz6N 25 | > hgfkyb9O7HAMdAP+TxehjqG3gh5Ky2DgYMCIZOztVzsuOb1DGJe/kGUKeRJLl2/O 26 | > QwkctwUOcVIxckNu6OvclriFzvoXObqO77XeCI2V1Vef0wGTWlWNOdbFa4708Y7f 27 | > 5UdwInYQUi87RFDnc1SDU4Jrsv4KzZiv9FCfDg8pCBIdWpWT7DAuI0d7i7PZ+iFt 28 | > ZZ6sb/YDkyiDXU4ar/dja0FDE2r7jsN9D+FfW49+iDvXr4ELQyhZpW3Zr1Ojwm58 29 | > CJzjZwbRYiVwPBRsKmiYfO1E7esvw3CmjK5chfz8c40f6/APDro9ZmYNBRv2CnJy 30 | > t/DtcM/GpAhBbLP9Tk7kPB41v5fRIxVDo50Iz/qvkr37pQ4RsejSFg== 31 | > -----END CERTIFICATE----- 32 | > EOF 33 | 34 | Dump a SPIFFE example certificate (example-spiffe.crt) to JSON output 35 | 36 | $ certigo dump --json example-spiffe.crt 37 | {"certificates":[{"serial":"4096","not_before":"2017-07-19T16:50:20Z","not_after":"2017-07-29T16:50:20Z","signature_algorithm":"SHA256-RSA","is_self_signed":false,"subject":{"common_name":"blog","country":["US"],"key_id":"55:43:DD:63:48:E3:8D:CD:5A:F5:54:60:5D:24:E9:4F:37:ED:D7:C5","organization":["test1.acme.com"]},"issuer":{"common_name":"IntermediaetCA","country":["US"],"key_id":"35:DD:B7:F1:E7:4A:C3:64:ED:F2:D8:26:5D:1C:F8:25:78:55:04:A0","organization":["test1.acme.com"]},"basic_constraints":{"is_ca":false},"key_usage":["Digital Signature","Key Encipherment","Key Agreement"],"extended_key_usage":["Server Auth"],"uri_names":["spiffe://dev.acme.com/path/service"],"pem":"-----BEGIN CERTIFICATE-----\nMIIE1DCCArygAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzELMAkGA1UEBhMCVVMx\nFzAVBgNVBAoMDnRlc3QxLmFjbWUuY29tMRcwFQYDVQQDDA5JbnRlcm1lZGlhZXRD\nQTAeFw0xNzA3MTkxNjUwMjBaFw0xNzA3MjkxNjUwMjBaMDUxCzAJBgNVBAYTAlVT\nMRcwFQYDVQQKDA50ZXN0MS5hY21lLmNvbTENMAsGA1UEAwwEYmxvZzCCASIwDQYJ\nKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKm8P47lABp4+rz2nN+QYrxedbaFVWoF\nFuoSkqcHsafMwbMrN+kI6wJVtlbwviDvxWFJ92q0H71QNFybTsmof3KUN/kYCp7P\n+LKhBrN0ttWI5q6v5eDrjN0VdtVdnlZOYmJFbvETOgfK/qXKNRRM8HYW0tdqrtEw\nCR5dIu53xVUSViBdwXpuy2c5W2mFn1gxTpdW+3hbZsL1pHrU9qPWLtTgl/KY8kjs\nI7KW1cIcinE4SJomhB5L/4emhxKGY+kEa2+fN9IPjjvKSMOw9kiBKk1GHZcIY5EA\nO3TIfUk3fysPzi5qA0su/bNtPQy1uXgXS10xUlV7pqRPvHjiNzgFkXUCAwEAAaOB\n4zCB4DAJBgNVHRMEAjAAMB0GA1UdDgQWBBRVQ91jSOONzVr1VGBdJOlPN+3XxTBg\nBgNVHSMEWTBXgBQ13bfx50rDZO3y2CZdHPgleFUEoKE7pDkwNzELMAkGA1UEBhMC\nVVMxFzAVBgNVBAoMDnRlc3QxLmFjbWUuY29tMQ8wDQYDVQQDDAZSb290Q0GCAhAA\nMA4GA1UdDwEB/wQEAwIDqDATBgNVHSUEDDAKBggrBgEFBQcDATAtBgNVHREEJjAk\nhiJzcGlmZmU6Ly9kZXYuYWNtZS5jb20vcGF0aC9zZXJ2aWNlMA0GCSqGSIb3DQEB\nCwUAA4ICAQBp2+rtUxt1VmNM/vi6PwoSoYzWFmQ2nc4OM7bsOG4uppU54wRYZ+T7\nc42EcrpyBgWn+rWHT1Hi6SNcmloKHydaUTZ4pq3IlKKnBNqwivU5BzIxYLDrhR/U\nwd9s1tgmLvADqkQa1XjjSFn5Auoj1R640ry4qpw8IOusdm6wVhru4ssRnHX4E2uR\njQe7b3ws38aZhjtL78Ip0BB4yPxWJRp/WmEoT33QP+cZhA4IYWECxNODr6DSJeq2\nVNu/6JACGrNfM2Sjt4Wxz+nIa3cKDNCA6PR8StTUTcoQ6ZBzpn+n/Q1xSRIOJz6N\nhgfkyb9O7HAMdAP+TxehjqG3gh5Ky2DgYMCIZOztVzsuOb1DGJe/kGUKeRJLl2/O\nQwkctwUOcVIxckNu6OvclriFzvoXObqO77XeCI2V1Vef0wGTWlWNOdbFa4708Y7f\n5UdwInYQUi87RFDnc1SDU4Jrsv4KzZiv9FCfDg8pCBIdWpWT7DAuI0d7i7PZ+iFt\nZZ6sb/YDkyiDXU4ar/dja0FDE2r7jsN9D+FfW49+iDvXr4ELQyhZpW3Zr1Ojwm58\nCJzjZwbRYiVwPBRsKmiYfO1E7esvw3CmjK5chfz8c40f6/APDro9ZmYNBRv2CnJy\nt/DtcM/GpAhBbLP9Tk7kPB41v5fRIxVDo50Iz/qvkr37pQ4RsejSFg==\n-----END CERTIFICATE-----\n"}]} 38 | -------------------------------------------------------------------------------- /starttls/mysql/infile.go: -------------------------------------------------------------------------------- 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package 2 | // 3 | // Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. 4 | // 5 | // This Source Code Form is subject to the terms of the Mozilla Public 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | package mysql 10 | 11 | import ( 12 | "fmt" 13 | "io" 14 | "os" 15 | "strings" 16 | "sync" 17 | ) 18 | 19 | var ( 20 | fileRegister map[string]bool 21 | fileRegisterLock sync.RWMutex 22 | readerRegister map[string]func() io.Reader 23 | readerRegisterLock sync.RWMutex 24 | ) 25 | 26 | // RegisterLocalFile adds the given file to the file whitelist, 27 | // so that it can be used by "LOAD DATA LOCAL INFILE ". 28 | // Alternatively you can allow the use of all local files with 29 | // the DSN parameter 'allowAllFiles=true' 30 | // 31 | // filePath := "/home/gopher/data.csv" 32 | // mysql.RegisterLocalFile(filePath) 33 | // err := db.Exec("LOAD DATA LOCAL INFILE '" + filePath + "' INTO TABLE foo") 34 | // if err != nil { 35 | // ... 36 | // 37 | func RegisterLocalFile(filePath string) { 38 | fileRegisterLock.Lock() 39 | // lazy map init 40 | if fileRegister == nil { 41 | fileRegister = make(map[string]bool) 42 | } 43 | 44 | fileRegister[strings.Trim(filePath, `"`)] = true 45 | fileRegisterLock.Unlock() 46 | } 47 | 48 | // DeregisterLocalFile removes the given filepath from the whitelist. 49 | func DeregisterLocalFile(filePath string) { 50 | fileRegisterLock.Lock() 51 | delete(fileRegister, strings.Trim(filePath, `"`)) 52 | fileRegisterLock.Unlock() 53 | } 54 | 55 | // RegisterReaderHandler registers a handler function which is used 56 | // to receive a io.Reader. 57 | // The Reader can be used by "LOAD DATA LOCAL INFILE Reader::". 58 | // If the handler returns a io.ReadCloser Close() is called when the 59 | // request is finished. 60 | // 61 | // mysql.RegisterReaderHandler("data", func() io.Reader { 62 | // var csvReader io.Reader // Some Reader that returns CSV data 63 | // ... // Open Reader here 64 | // return csvReader 65 | // }) 66 | // err := db.Exec("LOAD DATA LOCAL INFILE 'Reader::data' INTO TABLE foo") 67 | // if err != nil { 68 | // ... 69 | // 70 | func RegisterReaderHandler(name string, handler func() io.Reader) { 71 | readerRegisterLock.Lock() 72 | // lazy map init 73 | if readerRegister == nil { 74 | readerRegister = make(map[string]func() io.Reader) 75 | } 76 | 77 | readerRegister[name] = handler 78 | readerRegisterLock.Unlock() 79 | } 80 | 81 | // DeregisterReaderHandler removes the ReaderHandler function with 82 | // the given name from the registry. 83 | func DeregisterReaderHandler(name string) { 84 | readerRegisterLock.Lock() 85 | delete(readerRegister, name) 86 | readerRegisterLock.Unlock() 87 | } 88 | 89 | func deferredClose(err *error, closer io.Closer) { 90 | closeErr := closer.Close() 91 | if *err == nil { 92 | *err = closeErr 93 | } 94 | } 95 | 96 | func (mc *mysqlConn) handleInFileRequest(name string) (err error) { 97 | var rdr io.Reader 98 | var data []byte 99 | packetSize := 16 * 1024 // 16KB is small enough for disk readahead and large enough for TCP 100 | if mc.maxWriteSize < packetSize { 101 | packetSize = mc.maxWriteSize 102 | } 103 | 104 | if idx := strings.Index(name, "Reader::"); idx == 0 || (idx > 0 && name[idx-1] == '/') { // io.Reader 105 | // The server might return an an absolute path. See issue #355. 106 | name = name[idx+8:] 107 | 108 | readerRegisterLock.RLock() 109 | handler, inMap := readerRegister[name] 110 | readerRegisterLock.RUnlock() 111 | 112 | if inMap { 113 | rdr = handler() 114 | if rdr != nil { 115 | if cl, ok := rdr.(io.Closer); ok { 116 | defer deferredClose(&err, cl) 117 | } 118 | } else { 119 | err = fmt.Errorf("Reader '%s' is ", name) 120 | } 121 | } else { 122 | err = fmt.Errorf("Reader '%s' is not registered", name) 123 | } 124 | } else { // File 125 | name = strings.Trim(name, `"`) 126 | fileRegisterLock.RLock() 127 | fr := fileRegister[name] 128 | fileRegisterLock.RUnlock() 129 | if mc.cfg.AllowAllFiles || fr { 130 | var file *os.File 131 | var fi os.FileInfo 132 | 133 | if file, err = os.Open(name); err == nil { 134 | defer deferredClose(&err, file) 135 | 136 | // get file size 137 | if fi, err = file.Stat(); err == nil { 138 | rdr = file 139 | if fileSize := int(fi.Size()); fileSize < packetSize { 140 | packetSize = fileSize 141 | } 142 | } 143 | } 144 | } else { 145 | err = fmt.Errorf("local file '%s' is not registered", name) 146 | } 147 | } 148 | 149 | // send content packets 150 | if err == nil { 151 | data := make([]byte, 4+packetSize) 152 | var n int 153 | for err == nil { 154 | n, err = rdr.Read(data[4:]) 155 | if n > 0 { 156 | if ioErr := mc.writePacket(data[:4+n]); ioErr != nil { 157 | return ioErr 158 | } 159 | } 160 | } 161 | if err == io.EOF { 162 | err = nil 163 | } 164 | } 165 | 166 | // send empty packet (termination) 167 | if data == nil { 168 | data = make([]byte, 4) 169 | } 170 | if ioErr := mc.writePacket(data[:4]); ioErr != nil { 171 | return ioErr 172 | } 173 | 174 | // read OK packet 175 | if err == nil { 176 | _, err = mc.readResultOK() 177 | return err 178 | } 179 | 180 | mc.readPacket() 181 | return err 182 | } 183 | -------------------------------------------------------------------------------- /starttls/psql/oid/types.go: -------------------------------------------------------------------------------- 1 | // generated by 'go run gen.go'; do not edit 2 | 3 | package oid 4 | 5 | const ( 6 | T_bool Oid = 16 7 | T_bytea Oid = 17 8 | T_char Oid = 18 9 | T_name Oid = 19 10 | T_int8 Oid = 20 11 | T_int2 Oid = 21 12 | T_int2vector Oid = 22 13 | T_int4 Oid = 23 14 | T_regproc Oid = 24 15 | T_text Oid = 25 16 | T_oid Oid = 26 17 | T_tid Oid = 27 18 | T_xid Oid = 28 19 | T_cid Oid = 29 20 | T_oidvector Oid = 30 21 | T_pg_type Oid = 71 22 | T_pg_attribute Oid = 75 23 | T_pg_proc Oid = 81 24 | T_pg_class Oid = 83 25 | T_json Oid = 114 26 | T_xml Oid = 142 27 | T__xml Oid = 143 28 | T_pg_node_tree Oid = 194 29 | T__json Oid = 199 30 | T_smgr Oid = 210 31 | T_point Oid = 600 32 | T_lseg Oid = 601 33 | T_path Oid = 602 34 | T_box Oid = 603 35 | T_polygon Oid = 604 36 | T_line Oid = 628 37 | T__line Oid = 629 38 | T_cidr Oid = 650 39 | T__cidr Oid = 651 40 | T_float4 Oid = 700 41 | T_float8 Oid = 701 42 | T_abstime Oid = 702 43 | T_reltime Oid = 703 44 | T_tinterval Oid = 704 45 | T_unknown Oid = 705 46 | T_circle Oid = 718 47 | T__circle Oid = 719 48 | T_money Oid = 790 49 | T__money Oid = 791 50 | T_macaddr Oid = 829 51 | T_inet Oid = 869 52 | T__bool Oid = 1000 53 | T__bytea Oid = 1001 54 | T__char Oid = 1002 55 | T__name Oid = 1003 56 | T__int2 Oid = 1005 57 | T__int2vector Oid = 1006 58 | T__int4 Oid = 1007 59 | T__regproc Oid = 1008 60 | T__text Oid = 1009 61 | T__tid Oid = 1010 62 | T__xid Oid = 1011 63 | T__cid Oid = 1012 64 | T__oidvector Oid = 1013 65 | T__bpchar Oid = 1014 66 | T__varchar Oid = 1015 67 | T__int8 Oid = 1016 68 | T__point Oid = 1017 69 | T__lseg Oid = 1018 70 | T__path Oid = 1019 71 | T__box Oid = 1020 72 | T__float4 Oid = 1021 73 | T__float8 Oid = 1022 74 | T__abstime Oid = 1023 75 | T__reltime Oid = 1024 76 | T__tinterval Oid = 1025 77 | T__polygon Oid = 1027 78 | T__oid Oid = 1028 79 | T_aclitem Oid = 1033 80 | T__aclitem Oid = 1034 81 | T__macaddr Oid = 1040 82 | T__inet Oid = 1041 83 | T_bpchar Oid = 1042 84 | T_varchar Oid = 1043 85 | T_date Oid = 1082 86 | T_time Oid = 1083 87 | T_timestamp Oid = 1114 88 | T__timestamp Oid = 1115 89 | T__date Oid = 1182 90 | T__time Oid = 1183 91 | T_timestamptz Oid = 1184 92 | T__timestamptz Oid = 1185 93 | T_interval Oid = 1186 94 | T__interval Oid = 1187 95 | T__numeric Oid = 1231 96 | T_pg_database Oid = 1248 97 | T__cstring Oid = 1263 98 | T_timetz Oid = 1266 99 | T__timetz Oid = 1270 100 | T_bit Oid = 1560 101 | T__bit Oid = 1561 102 | T_varbit Oid = 1562 103 | T__varbit Oid = 1563 104 | T_numeric Oid = 1700 105 | T_refcursor Oid = 1790 106 | T__refcursor Oid = 2201 107 | T_regprocedure Oid = 2202 108 | T_regoper Oid = 2203 109 | T_regoperator Oid = 2204 110 | T_regclass Oid = 2205 111 | T_regtype Oid = 2206 112 | T__regprocedure Oid = 2207 113 | T__regoper Oid = 2208 114 | T__regoperator Oid = 2209 115 | T__regclass Oid = 2210 116 | T__regtype Oid = 2211 117 | T_record Oid = 2249 118 | T_cstring Oid = 2275 119 | T_any Oid = 2276 120 | T_anyarray Oid = 2277 121 | T_void Oid = 2278 122 | T_trigger Oid = 2279 123 | T_language_handler Oid = 2280 124 | T_internal Oid = 2281 125 | T_opaque Oid = 2282 126 | T_anyelement Oid = 2283 127 | T__record Oid = 2287 128 | T_anynonarray Oid = 2776 129 | T_pg_authid Oid = 2842 130 | T_pg_auth_members Oid = 2843 131 | T__txid_snapshot Oid = 2949 132 | T_uuid Oid = 2950 133 | T__uuid Oid = 2951 134 | T_txid_snapshot Oid = 2970 135 | T_fdw_handler Oid = 3115 136 | T_anyenum Oid = 3500 137 | T_tsvector Oid = 3614 138 | T_tsquery Oid = 3615 139 | T_gtsvector Oid = 3642 140 | T__tsvector Oid = 3643 141 | T__gtsvector Oid = 3644 142 | T__tsquery Oid = 3645 143 | T_regconfig Oid = 3734 144 | T__regconfig Oid = 3735 145 | T_regdictionary Oid = 3769 146 | T__regdictionary Oid = 3770 147 | T_anyrange Oid = 3831 148 | T_event_trigger Oid = 3838 149 | T_int4range Oid = 3904 150 | T__int4range Oid = 3905 151 | T_numrange Oid = 3906 152 | T__numrange Oid = 3907 153 | T_tsrange Oid = 3908 154 | T__tsrange Oid = 3909 155 | T_tstzrange Oid = 3910 156 | T__tstzrange Oid = 3911 157 | T_daterange Oid = 3912 158 | T__daterange Oid = 3913 159 | T_int8range Oid = 3926 160 | T__int8range Oid = 3927 161 | ) 162 | -------------------------------------------------------------------------------- /starttls/psql/ssl.go: -------------------------------------------------------------------------------- 1 | package pq 2 | 3 | import ( 4 | "crypto/tls" 5 | "crypto/x509" 6 | "net" 7 | "os" 8 | "os/user" 9 | "path/filepath" 10 | ) 11 | 12 | // ssl generates a function to upgrade a net.Conn based on the "sslmode" and 13 | // related settings. The function is nil when no upgrade should take place. 14 | func ssl(o values, tlsConf *tls.Config) func(net.Conn) net.Conn { 15 | verifyCaOnly := false 16 | tlsConf = tlsConf.Clone() 17 | switch mode := o.Get("sslmode"); mode { 18 | // "require" is the default. 19 | case "", "require": 20 | // We must skip TLS's own verification since it requires full 21 | // verification since Go 1.3. 22 | tlsConf.InsecureSkipVerify = true 23 | 24 | // From http://www.postgresql.org/docs/current/static/libpq-ssl.html: 25 | // Note: For backwards compatibility with earlier versions of PostgreSQL, if a 26 | // root CA file exists, the behavior of sslmode=require will be the same as 27 | // that of verify-ca, meaning the server certificate is validated against the 28 | // CA. Relying on this behavior is discouraged, and applications that need 29 | // certificate validation should always use verify-ca or verify-full. 30 | if _, err := os.Stat(o.Get("sslrootcert")); err == nil { 31 | verifyCaOnly = true 32 | } else { 33 | o.Set("sslrootcert", "") 34 | } 35 | case "verify-ca": 36 | // We must skip TLS's own verification since it requires full 37 | // verification since Go 1.3. 38 | tlsConf.InsecureSkipVerify = true 39 | verifyCaOnly = true 40 | case "verify-full": 41 | tlsConf.ServerName = o.Get("host") 42 | case "disable": 43 | return nil 44 | default: 45 | errorf(`unsupported sslmode %q; only "require" (default), "verify-full", "verify-ca", and "disable" supported`, mode) 46 | } 47 | 48 | sslClientCertificates(tlsConf, o) 49 | sslCertificateAuthority(tlsConf, o) 50 | sslRenegotiation(tlsConf) 51 | 52 | return func(conn net.Conn) net.Conn { 53 | client := tls.Client(conn, tlsConf) 54 | if verifyCaOnly { 55 | sslVerifyCertificateAuthority(client, tlsConf) 56 | } 57 | return client 58 | } 59 | } 60 | 61 | // sslClientCertificates adds the certificate specified in the "sslcert" and 62 | // "sslkey" settings, or if they aren't set, from the .postgresql directory 63 | // in the user's home directory. The configured files must exist and have 64 | // the correct permissions. 65 | func sslClientCertificates(tlsConf *tls.Config, o values) { 66 | sslkey := o.Get("sslkey") 67 | sslcert := o.Get("sslcert") 68 | 69 | var cinfo, kinfo os.FileInfo 70 | var err error 71 | 72 | if sslcert != "" && sslkey != "" { 73 | // Check that both files exist. Note that we don't do any more extensive 74 | // checks than this (such as checking that the paths aren't directories); 75 | // LoadX509KeyPair() will take care of the rest. 76 | cinfo, err = os.Stat(sslcert) 77 | if err != nil { 78 | panic(err) 79 | } 80 | 81 | kinfo, err = os.Stat(sslkey) 82 | if err != nil { 83 | panic(err) 84 | } 85 | } else { 86 | // Automatically find certificates from ~/.postgresql 87 | sslcert, sslkey, cinfo, kinfo = sslHomeCertificates() 88 | 89 | if cinfo == nil || kinfo == nil { 90 | // No certificates to load 91 | return 92 | } 93 | } 94 | 95 | // The files must also have the correct permissions 96 | sslCertificatePermissions(cinfo, kinfo) 97 | 98 | cert, err := tls.LoadX509KeyPair(sslcert, sslkey) 99 | if err != nil { 100 | panic(err) 101 | } 102 | tlsConf.Certificates = []tls.Certificate{cert} 103 | } 104 | 105 | // sslCertificateAuthority adds the RootCA specified in the "sslrootcert" setting. 106 | func sslCertificateAuthority(tlsConf *tls.Config, o values) { 107 | if sslrootcert := o.Get("sslrootcert"); sslrootcert != "" { 108 | tlsConf.RootCAs = x509.NewCertPool() 109 | 110 | cert, err := os.ReadFile(sslrootcert) 111 | if err != nil { 112 | panic(err) 113 | } 114 | 115 | ok := tlsConf.RootCAs.AppendCertsFromPEM(cert) 116 | if !ok { 117 | errorf("couldn't parse pem in sslrootcert") 118 | } 119 | } 120 | } 121 | 122 | // sslHomeCertificates returns the path and stats of certificates in the current 123 | // user's home directory. 124 | func sslHomeCertificates() (cert, key string, cinfo, kinfo os.FileInfo) { 125 | user, err := user.Current() 126 | 127 | if err != nil { 128 | // user.Current() might fail when cross-compiling. We have to ignore the 129 | // error and continue without client certificates, since we wouldn't know 130 | // from where to load them. 131 | return 132 | } 133 | 134 | cert = filepath.Join(user.HomeDir, ".postgresql", "postgresql.crt") 135 | key = filepath.Join(user.HomeDir, ".postgresql", "postgresql.key") 136 | 137 | cinfo, err = os.Stat(cert) 138 | if err != nil { 139 | cinfo = nil 140 | } 141 | 142 | kinfo, err = os.Stat(key) 143 | if err != nil { 144 | kinfo = nil 145 | } 146 | 147 | return 148 | } 149 | 150 | // sslVerifyCertificateAuthority carries out a TLS handshake to the server and 151 | // verifies the presented certificate against the CA, i.e. the one specified in 152 | // sslrootcert or the system CA if sslrootcert was not specified. 153 | func sslVerifyCertificateAuthority(client *tls.Conn, tlsConf *tls.Config) { 154 | err := client.Handshake() 155 | if err != nil { 156 | panic(err) 157 | } 158 | certs := client.ConnectionState().PeerCertificates 159 | opts := x509.VerifyOptions{ 160 | DNSName: client.ConnectionState().ServerName, 161 | Intermediates: x509.NewCertPool(), 162 | Roots: tlsConf.RootCAs, 163 | } 164 | for i, cert := range certs { 165 | if i == 0 { 166 | continue 167 | } 168 | opts.Intermediates.AddCert(cert) 169 | } 170 | _, err = certs[0].Verify(opts) 171 | if err != nil { 172 | panic(err) 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /jceks/modutf8.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package jceks 17 | 18 | import ( 19 | "bufio" 20 | "errors" 21 | "fmt" 22 | "io" 23 | "strings" 24 | "unicode/utf16" 25 | "unicode/utf8" 26 | ) 27 | 28 | var errInvalidModifiedUTF8 = errors.New("invalid JCEKS string") 29 | 30 | // writeModifiedUTF8 writes a string with "modified UTF-8" encoding to the given writer, as specified in Java's 31 | // DataInput class. See: 32 | // https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/io/DataInput.html#modified-utf-8 33 | func writeModifiedUTF8(w io.Writer, str string) error { 34 | for _, r := range str { 35 | if r == 0 { // Zero gets a special encoding so that there are no NULs 36 | _, _ = w.Write([]byte{0xC0, 0x80}) 37 | 38 | continue 39 | } 40 | if r > 0xFFFF { 41 | // UTF-16 surrogate pair encoding outside the basic multilingual plane 42 | r1, r2 := utf16.EncodeRune(r) 43 | for _, r := range []rune{r1, r2} { 44 | _, _ = w.Write([]byte{ 45 | 0b1110_0000 | byte((r>>12)&0b0000_1111), 46 | 0b10_000000 | byte((r>>6)&0b00_111111), 47 | 0b10_000000 | byte(r&0b00_111111), 48 | }) 49 | } 50 | 51 | continue 52 | } 53 | 54 | // Normal UTF-8 encoding 55 | var p [3]byte 56 | n := utf8.EncodeRune(p[:], r) 57 | if _, err := w.Write(p[:n]); err != nil { 58 | return err 59 | } 60 | } 61 | 62 | return nil 63 | } 64 | 65 | // readModifiedUTF8 reads a string with "modified UTF-8" encoding from the given reader, as specified in Java's 66 | // DataInput class. The function reads until the reader returns EOF, then returns the decoded string. See: 67 | // https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/io/DataInput.html#modified-utf-8 68 | func readModifiedUTF8(r io.Reader) (string, error) { 69 | br, ok := r.(io.ByteReader) 70 | if !ok { 71 | br = bufio.NewReader(r) 72 | } 73 | 74 | var sb strings.Builder 75 | buf := make([]byte, 6) 76 | for { 77 | var err error 78 | buf[0], err = br.ReadByte() 79 | if err != nil { 80 | if err == io.EOF { 81 | break 82 | } 83 | 84 | return "", fmt.Errorf("failed to read rune byte 1: %w", err) 85 | } 86 | 87 | // Basic Latin 88 | if buf[0] < 0x80 { 89 | if buf[0] == 0x00 { 90 | // NULs require the special 2-byte encoding, so this is not valid 91 | return "", fmt.Errorf("%w: 1-byte NUL is not valid in modified UTF-8", errInvalidModifiedUTF8) 92 | } 93 | sb.WriteByte(buf[0]) 94 | 95 | continue 96 | } 97 | if buf[0]&0b11_000000 == 0b10_000000 { 98 | return "", fmt.Errorf("%w: invalid first rune byte %02x", errInvalidModifiedUTF8, buf[0]) 99 | } 100 | 101 | buf[1], err = br.ReadByte() 102 | if err != nil { 103 | if err == io.EOF { 104 | err = io.ErrUnexpectedEOF 105 | } 106 | 107 | return "", fmt.Errorf("failed to read rune byte 2: %w", err) 108 | } 109 | 110 | if buf[0]&0b111_00000 == 0b110_00000 { 111 | if buf[0] == 0xC0 && buf[1] == 0x80 { 112 | // Special zero encoding 113 | sb.WriteByte(0) 114 | } else { 115 | // Regular UTF-8 up to U+07FF 116 | rn, _ := utf8.DecodeRune(buf[:2]) 117 | if rn == utf8.RuneError { 118 | return "", fmt.Errorf("%w: invalid 2-byte UTF-8 codepoint", errInvalidModifiedUTF8) 119 | } 120 | sb.WriteRune(rn) 121 | } 122 | 123 | continue 124 | } 125 | if buf[0]&0b1111_0000 != 0b1110_0000 { 126 | return "", fmt.Errorf("%w: encountered UTF-8 outside basic multilingual plane", 127 | errInvalidModifiedUTF8) 128 | } 129 | 130 | buf[2], err = br.ReadByte() 131 | if err != nil { 132 | if err == io.EOF { 133 | err = io.ErrUnexpectedEOF 134 | } 135 | 136 | return "", fmt.Errorf("failed to read rune byte 3: %w", err) 137 | } 138 | 139 | if buf[0] != 0b1110_1101 || (buf[1]&0b111_00000 != 0b101_00000) { 140 | // Regular UTF-8 encoding from U+8000 to U+FFFF 141 | rn, _ := utf8.DecodeRune(buf[:3]) 142 | if rn == utf8.RuneError { 143 | return "", fmt.Errorf("%w: invalid 3-byte UTF-8 codepoint", errInvalidModifiedUTF8) 144 | } 145 | sb.WriteRune(rn) 146 | 147 | continue 148 | } 149 | 150 | // UTF-16 surrogate pair encoded as though the surrogates were UTF-8 codepoints 151 | for i := 3; i < 6; i++ { 152 | buf[i], err = br.ReadByte() 153 | if err != nil { 154 | if err == io.EOF { 155 | err = io.ErrUnexpectedEOF 156 | } 157 | 158 | return "", fmt.Errorf("failed to read rune byte %d: %w", i+1, err) 159 | } 160 | } 161 | var surrogates [2]rune 162 | for i, rnBytes := range [][]byte{buf[0:3], buf[3:6]} { 163 | if rnBytes[0] != 0b1110_1101 || 164 | rnBytes[1]&0b111_00000 != 0b101_00000 || 165 | rnBytes[2]&0b11_000000 != 0b10_000000 { 166 | return "", fmt.Errorf("%w: invalid modified surrogate %d encoding: %02x", 167 | errInvalidModifiedUTF8, i, rnBytes) 168 | } 169 | surrogates[i] = rune( 170 | 0xd800 | 171 | uint16(rnBytes[1]&0b000_11111)<<6 | 172 | uint16(rnBytes[2]&0b00_111111), 173 | ) 174 | } 175 | rn := utf16.DecodeRune(surrogates[0], surrogates[1]) 176 | if rn == utf8.RuneError { 177 | return "", fmt.Errorf("%w: invalid supplementary character", errInvalidModifiedUTF8) 178 | } 179 | sb.WriteRune(rn) 180 | } 181 | 182 | return sb.String(), nil 183 | } 184 | -------------------------------------------------------------------------------- /lib/ocsp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Block, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package lib 17 | 18 | import ( 19 | "bytes" 20 | "crypto/x509" 21 | "encoding/base64" 22 | "errors" 23 | "fmt" 24 | "io" 25 | "net/http" 26 | "time" 27 | 28 | "github.com/fatih/color" 29 | "golang.org/x/crypto/ocsp" 30 | ) 31 | 32 | var ( 33 | errSkippedRevocationCheck = errors.New("skipped revocation check") 34 | 35 | revocationStatusColor = map[int]*color.Color{ 36 | ocsp.Good: green, 37 | ocsp.Revoked: red, 38 | ocsp.Unknown: yellow, 39 | } 40 | 41 | revocationStatusDescription = map[int]string{ 42 | ocsp.Good: "Good", 43 | ocsp.Revoked: "Revoked", 44 | ocsp.Unknown: "Unknown", 45 | } 46 | 47 | revocationReasonDescription = map[int]string{ 48 | ocsp.Unspecified: "Unspecified", 49 | ocsp.KeyCompromise: "KeyCompromise", 50 | ocsp.CACompromise: "CACompromise", 51 | ocsp.AffiliationChanged: "AffiliationChanged", 52 | ocsp.Superseded: "Superseded", 53 | ocsp.CessationOfOperation: "CessationOfOperation", 54 | ocsp.CertificateHold: "CertificateHold", 55 | ocsp.RemoveFromCRL: "RemoveFromCRL", 56 | ocsp.PrivilegeWithdrawn: "PrivilegeWithdrawn", 57 | ocsp.AACompromise: "AACompromise", 58 | } 59 | 60 | ocspHttpClient = &http.Client{ 61 | // Set a timeout so we don't block forever on broken servers. 62 | Timeout: 5 * time.Second, 63 | } 64 | ) 65 | 66 | const ( 67 | // We retry multiple times, because OCSP servers are often a bit unreliable. 68 | maxOCSPValidationRetries = 3 69 | ) 70 | 71 | func checkOCSP(chain []*x509.Certificate, ocspStaple []byte) (status *ocsp.Response, err error) { 72 | if len(chain) < 2 { 73 | // Nothing to check here 74 | return nil, errSkippedRevocationCheck 75 | } 76 | 77 | leaf, issuer := chain[0], chain[1] 78 | if len(leaf.OCSPServer) == 0 { 79 | return nil, errSkippedRevocationCheck 80 | } 81 | 82 | retries := maxOCSPValidationRetries 83 | if len(ocspStaple) > 0 { 84 | // Don't retry if stapled 85 | retries = 1 86 | } 87 | 88 | for i := 0; i < retries; i++ { 89 | encoded := ocspStaple 90 | if len(encoded) == 0 { 91 | encoded, err = fetchOCSP(leaf, issuer) 92 | if err != nil { 93 | return nil, err 94 | } 95 | } 96 | 97 | status, err = ocsp.ParseResponse(encoded, issuer) 98 | if err == nil { 99 | break 100 | } 101 | } 102 | 103 | return status, err 104 | } 105 | 106 | func fetchOCSP(cert, issuer *x509.Certificate) ([]byte, error) { 107 | encoded, err := ocsp.CreateRequest(cert, issuer, nil) 108 | if err != nil { 109 | return nil, fmt.Errorf("failure building request: %s", err) 110 | } 111 | 112 | // Try all the OCSP servers listed in the certificate 113 | var lastError error 114 | for _, server := range cert.OCSPServer { 115 | // We try both GET and POST requests, because some servers are janky. 116 | var reqs []*http.Request 117 | if len(encoded) < 255 { 118 | // GET only supported for requests with small payloads, so we can stash 119 | // them in the path. RFC says 255 bytes encoded, but doesn't mention if that 120 | // refers to the DER-encoded payload before or after base64 is applied. We 121 | // just assume it's the former and try both GET and POST in case one fails. 122 | req, err := buildOCSPwithGET(server, encoded) 123 | if err != nil { 124 | lastError = err 125 | continue 126 | } 127 | reqs = append(reqs, req) 128 | } 129 | 130 | // POST should always be supported, but some servers don't like it 131 | req, err := buildOCSPwithPOST(server, encoded) 132 | if err != nil { 133 | lastError = err 134 | continue 135 | } 136 | reqs = append(reqs, req) 137 | 138 | for _, req := range reqs { 139 | body, err := func() ([]byte, error) { 140 | resp, err := ocspHttpClient.Do(req) 141 | if err != nil { 142 | return nil, err 143 | } 144 | defer func() { _ = resp.Body.Close() }() 145 | 146 | if resp.StatusCode != http.StatusOK { 147 | return nil, fmt.Errorf("unexpected status code, got: %s", resp.Status) 148 | } 149 | 150 | return io.ReadAll(resp.Body) 151 | }() 152 | if err != nil { 153 | lastError = err 154 | continue 155 | } 156 | 157 | return body, nil 158 | } 159 | } 160 | 161 | return nil, lastError 162 | } 163 | 164 | func buildOCSPwithPOST(server string, encoded []byte) (*http.Request, error) { 165 | req, err := http.NewRequest("POST", server, nil) 166 | if err != nil { 167 | return nil, err 168 | } 169 | 170 | req.Header.Add("Content-Type", "application/ocsp-request") 171 | req.Header.Add("Accept", "application/ocsp-response") 172 | err = req.Write(bytes.NewBuffer(encoded)) 173 | if err != nil { 174 | return nil, err 175 | } 176 | 177 | return req, nil 178 | } 179 | 180 | func buildOCSPwithGET(server string, encoded []byte) (*http.Request, error) { 181 | // https://datatracker.ietf.org/doc/html/rfc6960#appendix-A.1 182 | // GET {url}/{url-encoding of base-64 encoding of the DER encoding of the OCSPRequest} 183 | url := fmt.Sprintf("%s/%s", server, base64.StdEncoding.EncodeToString(encoded)) 184 | 185 | req, err := http.NewRequest("GET", url, nil) 186 | if err != nil { 187 | return nil, err 188 | } 189 | 190 | req.Header.Add("Accept", "application/ocsp-response") 191 | 192 | return req, nil 193 | } 194 | --------------------------------------------------------------------------------