├── .github ├── dependabot.yml └── workflows │ ├── lint.yaml │ ├── release.yaml │ └── test.yaml ├── .gitignore ├── CODEOWNERS ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── build ├── cli ├── cli.go ├── cli_test.go └── terminal │ ├── terminal.go │ └── testing.go ├── go.mod ├── go.sum ├── internal └── gen-known-logs │ ├── README.md │ └── main.go ├── jceks ├── AUTHORS ├── README.md ├── jceks.go ├── jceks_test.go ├── pbe.go ├── testdata │ ├── private-key.crt │ ├── private-key.jceks │ ├── private-key.key │ ├── private-key.p12 │ ├── trusted-cert.crt │ ├── trusted-cert.jceks │ └── trusted-cert.key └── utils_test.go ├── lib ├── certs.go ├── ct.go ├── ctlogs.go ├── display.go ├── encoder.go ├── ocsp.go ├── oids.go ├── tls.go └── verify.go ├── main.go ├── pkcs7 ├── pkcs7.go └── pkcs7_test.go ├── starttls ├── dialer.go ├── ftp.go ├── imap.go ├── ldap │ ├── .gitignore │ ├── .travis.yml │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── add.go │ ├── atomic_value.go │ ├── atomic_value_go13.go │ ├── bind.go │ ├── client.go │ ├── compare.go │ ├── conn.go │ ├── control.go │ ├── debug.go │ ├── del.go │ ├── dn.go │ ├── doc.go │ ├── error.go │ ├── error_test.go │ ├── filter.go │ ├── ldap.go │ ├── modify.go │ ├── passwdmodify.go │ └── search.go ├── mysql │ ├── AUTHORS │ ├── LICENSE │ ├── README.md │ ├── buffer.go │ ├── collations.go │ ├── connection.go │ ├── const.go │ ├── driver.go │ ├── dsn.go │ ├── errors.go │ ├── infile.go │ ├── packets.go │ ├── result.go │ ├── rows.go │ ├── statement.go │ ├── transaction.go │ └── utils.go ├── psql │ ├── LICENSE.md │ ├── README.md │ ├── array.go │ ├── buf.go │ ├── conn.go │ ├── conn_go18.go │ ├── copy.go │ ├── doc.go │ ├── encode.go │ ├── error.go │ ├── notify.go │ ├── oid │ │ ├── doc.go │ │ ├── gen.go │ │ └── types.go │ ├── ssl.go │ ├── ssl_go1.7.go │ ├── ssl_permissions.go │ ├── ssl_renegotiation.go │ ├── ssl_windows.go │ ├── url.go │ ├── user_posix.go │ ├── user_windows.go │ └── uuid.go └── starttls.go ├── test-certs ├── Makefile ├── README.md ├── example-bad-serial.crt ├── example-bad-serial.csr ├── example-bad-serial.jceks ├── example-bad-serial.p12 ├── example-bad-serial.p7b ├── example-custom-oid.conf ├── example-custom-oid.crt ├── example-custom-oid.csr ├── example-custom-oid.jceks ├── example-custom-oid.key ├── example-custom-oid.p12 ├── example-custom-oid.p7b ├── example-elliptic-sha1.crt ├── example-elliptic-sha1.csr ├── example-elliptic-sha1.jceks ├── example-elliptic-sha1.key ├── example-elliptic-sha1.p12 ├── example-elliptic-sha1.p7b ├── example-expired.crt ├── example-expired.csr ├── example-expired.jceks ├── example-expired.p12 ├── example-expired.p7b ├── example-leaf.crt ├── example-leaf.csr ├── example-leaf.jceks ├── example-leaf.p12 ├── example-leaf.p7b ├── example-md5.crt ├── example-md5.csr ├── example-md5.jceks ├── example-md5.p12 ├── example-md5.p7b ├── example-name-constraints.crt ├── example-name-constraints.csr ├── example-name-constraints.jceks ├── example-name-constraints.p12 ├── example-name-constraints.p7b ├── example-root-bad-ku.crt ├── example-root-bad-ku.csr ├── example-root-bad-ku.jceks ├── example-root-bad-ku.p12 ├── example-root-bad-ku.p7b ├── example-root.crt ├── example-root.csr ├── example-root.jceks ├── example-root.p12 ├── example-root.p7b ├── example-sha1.crt ├── example-sha1.csr ├── example-sha1.jceks ├── example-sha1.p12 ├── example-sha1.p7b ├── example-small-key.crt ├── example-small-key.csr ├── example-small-key.jceks ├── example-small-key.key ├── example-small-key.p12 ├── example-small-key.p7b └── openssl.ext └── tests ├── dump-cert-chain-to-text.t ├── dump-csr-to-text.t ├── dump-first-only-to-json.t ├── dump-first-only-to-text.t ├── dump-jceks-to-pem.t ├── dump-leaf-to-json.t ├── dump-leaf-to-not-verbose.t ├── dump-leaf-to-text.t ├── dump-mozilla-cacert-bundle.t ├── dump-name-constraints-to-text.t ├── dump-pkcs12-chain-to-pem-first-only.t ├── dump-pkcs12-chain-to-pem.t ├── dump-pkcs12-to-pem.t ├── dump-small-key-to-json.t ├── dump-small-key-to-text.t ├── dump-spiffe-cert-to-json.t └── dump-spiffe-cert-to-text.t /.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 | -------------------------------------------------------------------------------- /.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 | steps: 13 | - uses: actions/checkout@v2 14 | - name: golangci-lint 15 | uses: golangci/golangci-lint-action@v2 16 | with: 17 | version: v1.47.2 18 | only-new-issues: true -------------------------------------------------------------------------------- /.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: [1.19.x] 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 | steps: 20 | - name: Set up toolchain 21 | uses: actions/setup-go@v2 22 | with: 23 | go-version: ${{ matrix.version }} 24 | id: go 25 | - name: Check out code 26 | uses: actions/checkout@v2 27 | - name: Build binary 28 | run: go build -o certigo . 29 | - name: Upload artifact 30 | uses: actions/upload-artifact@v2 31 | with: 32 | name: certigo-${{ matrix.target.os }}-${{ matrix.target.arch }} 33 | path: certigo 34 | 35 | release: 36 | name: Create release 37 | runs-on: ubuntu-latest 38 | needs: [ build ] 39 | outputs: 40 | upload_url: ${{ steps.create_release.outputs.upload_url }} 41 | steps: 42 | - uses: actions/checkout@v2 43 | - name: Create release 44 | id: create_release 45 | uses: actions/create-release@v1 46 | env: 47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 48 | with: 49 | tag_name: ${{ github.ref }} 50 | release_name: "Release Build (Draft)" 51 | body: "Release Build (from ${{ github.ref }}/${{ github.sha }})" 52 | draft: true 53 | prerelease: true 54 | 55 | add-assets: 56 | name: Add assets 57 | runs-on: ubuntu-latest 58 | needs: [ build, release ] 59 | strategy: 60 | matrix: 61 | target: 62 | - { os: 'darwin', arch: 'amd64' } 63 | - { os: 'linux', arch: 'amd64' } 64 | - { os: 'windows', arch: 'amd64' } 65 | steps: 66 | - uses: actions/checkout@v2 67 | - name: Download artifact 68 | uses: actions/download-artifact@v2 69 | with: 70 | name: certigo-${{ matrix.target.os }}-${{ matrix.target.arch }} 71 | path: dist 72 | - name: Upload artifact to release 73 | uses: actions/upload-release-asset@v1 74 | env: 75 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 76 | with: 77 | upload_url: ${{ needs.release.outputs.upload_url }} 78 | asset_path: ./dist/certigo 79 | asset_name: certigo-${{ matrix.target.os }}-${{ matrix.target.arch }} 80 | asset_content_type: application/octet-stream -------------------------------------------------------------------------------- /.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 | steps: 15 | - name: Check out code 16 | uses: actions/checkout@v2 17 | - name: Set up Go 18 | uses: actions/setup-go@v2 19 | with: 20 | go-version: '1.19.x' 21 | - name: Build binary 22 | run: go build 23 | - name: Run tests 24 | run: go test -v ./... 25 | integration: 26 | name: Integration tests 27 | runs-on: ubuntu-latest 28 | steps: 29 | - name: Set up Go 30 | uses: actions/setup-go@v2 31 | with: 32 | go-version: '1.19.x' 33 | - name: Set up Python 34 | uses: actions/setup-python@v2 35 | with: 36 | python-version: '3.x' 37 | - name: Install cram 38 | run: pip install cram --user 39 | - name: Check out code 40 | uses: actions/checkout@v2 41 | - name: Build binary 42 | run: go build 43 | - name: Run tests 44 | run: PATH=$PWD:$PATH cram -v tests/*.t -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | certigo 2 | certigo.exe 3 | /gen-known-logs 4 | man/ 5 | shell/ 6 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @square/cryptographic-identity 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /build: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | # Copyright (C) 2016 Square, Inc. 3 | 4 | ORG_PATH="github.com/square" 5 | REPO_PATH="${ORG_PATH}/certigo" 6 | 7 | export GOPATH=${PWD}/gopath 8 | 9 | rm -f $GOPATH/src/${REPO_PATH} 10 | mkdir -p $GOPATH/src/${ORG_PATH} 11 | ln -s ${PWD} $GOPATH/src/${REPO_PATH} 12 | 13 | eval $(go env) 14 | 15 | go build -o bin/certigo ${REPO_PATH} 16 | 17 | # Generate man page 18 | mkdir -p man 19 | bin/certigo --help-man > man/certigo.1 20 | 21 | # Generate completion scripts 22 | mkdir -p shell 23 | bin/certigo --completion-script-zsh > shell/certigo.completion.zsh 24 | bin/certigo --completion-script-bash > shell/certigo.completion.bash 25 | -------------------------------------------------------------------------------- /cli/terminal/terminal.go: -------------------------------------------------------------------------------- 1 | package terminal 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "strings" 8 | 9 | "github.com/mattn/go-colorable" 10 | "golang.org/x/crypto/ssh/terminal" 11 | ) 12 | 13 | const minWidth = 60 14 | const maxWidth = 80 15 | 16 | // Terminal handles interacting with the user in Certigo 17 | type Terminal interface { 18 | Output() io.Writer 19 | Error() io.Writer 20 | SetDefaultPassword(password string) 21 | ReadPassword(prompt string) string 22 | DetermineWidth() int 23 | } 24 | 25 | // TTY represents unixish stdio, possibly with /dev/tty used to read user input 26 | type TTY struct { 27 | defaultPassword *string 28 | } 29 | 30 | func OpenTTY() *TTY { 31 | return &TTY{} 32 | } 33 | 34 | func (t *TTY) Output() io.Writer { 35 | return colorable.NewColorableStdout() 36 | } 37 | 38 | func (t *TTY) Error() io.Writer { 39 | return os.Stderr 40 | } 41 | 42 | func (t *TTY) SetDefaultPassword(password string) { 43 | t.defaultPassword = &password 44 | } 45 | 46 | func (t *TTY) ReadPassword(prompt string) string { 47 | if t.defaultPassword != nil { 48 | return *t.defaultPassword 49 | } 50 | 51 | var tty *os.File 52 | tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0) 53 | if err != nil { 54 | tty = os.Stdin 55 | } else { 56 | defer tty.Close() 57 | } 58 | 59 | tty.WriteString("Enter password") 60 | if prompt != "" { 61 | tty.WriteString(fmt.Sprintf(" for entry [%s]", prompt)) 62 | } 63 | tty.WriteString(": ") 64 | 65 | password, err := terminal.ReadPassword(int(tty.Fd())) 66 | tty.WriteString("\n") 67 | if err != nil { 68 | fmt.Fprintf(os.Stderr, "error reading password: %s\n", err) 69 | os.Exit(1) 70 | } 71 | 72 | return strings.TrimSuffix(string(password), "\n") 73 | } 74 | 75 | func (t *TTY) DetermineWidth() int { 76 | var width int 77 | fd := int(os.Stdout.Fd()) 78 | if terminal.IsTerminal(fd) { 79 | var err error 80 | width, _, err = terminal.GetSize(fd) 81 | if err != nil { 82 | width = minWidth 83 | } 84 | } else { 85 | width = minWidth 86 | } 87 | 88 | if width > maxWidth { 89 | width = maxWidth 90 | } else if width < minWidth { 91 | width = minWidth 92 | } 93 | return width 94 | } 95 | 96 | // Assert TTY implements terminal 97 | var _ Terminal = &TTY{} 98 | -------------------------------------------------------------------------------- /cli/terminal/testing.go: -------------------------------------------------------------------------------- 1 | package terminal 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | 7 | "github.com/mattn/go-colorable" 8 | ) 9 | 10 | // TestTerminal just collects input into buffers 11 | // That can be used to check output in tests 12 | type TestTerminal struct { 13 | OutputBuf bytes.Buffer 14 | ErrorBuf bytes.Buffer 15 | Password string 16 | Width int 17 | } 18 | 19 | var _ Terminal = &TestTerminal{} 20 | 21 | func (t *TestTerminal) Output() io.Writer { 22 | return colorable.NewNonColorable(&t.OutputBuf) 23 | } 24 | 25 | func (t *TestTerminal) Error() io.Writer { 26 | return &t.ErrorBuf 27 | } 28 | 29 | func (t *TestTerminal) SetDefaultPassword(password string) { 30 | t.Password = password 31 | } 32 | 33 | func (t *TestTerminal) ReadPassword(prompt string) string { 34 | return t.Password 35 | } 36 | 37 | func (t TestTerminal) DetermineWidth() int { 38 | return t.Width 39 | } 40 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/square/certigo 2 | 3 | require ( 4 | github.com/Masterminds/sprig v2.22.0+incompatible 5 | github.com/fatih/color v1.13.0 6 | github.com/google/certificate-transparency-go v1.1.4 7 | github.com/mattn/go-colorable v0.1.12 8 | github.com/mwitkow/go-http-dialer v0.0.0-20161116154839-378f744fb2b8 9 | github.com/stretchr/testify v1.8.2 10 | github.com/zmap/zcrypto v0.0.0-20220402174210-599ec18ecbac 11 | github.com/zmap/zlint/v3 v3.3.1 12 | golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 13 | gopkg.in/alecthomas/kingpin.v2 v2.2.6 14 | gopkg.in/asn1-ber.v1 v1.0.0-20170511165959-379148ca0225 15 | software.sslmate.com/src/go-pkcs12 v0.2.0 16 | ) 17 | 18 | go 1.13 19 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /internal/gen-known-logs/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "net/http" 9 | "os" 10 | "time" 11 | ) 12 | 13 | var ( 14 | out = flag.String("out", "", "Output file") 15 | ) 16 | 17 | // https://github.com/google/certificate-transparency-community-site/blob/master/docs/google/known-logs.md 18 | const knownLogsAddr = "https://www.gstatic.com/ct/log_list/v2/all_logs_list.json" 19 | 20 | type ctLog struct { 21 | operator string 22 | url string 23 | } 24 | 25 | func main() { 26 | flag.Parse() 27 | 28 | resp, err := http.Get(knownLogsAddr) 29 | if err != nil { 30 | log.Fatalf("Failed to fetch the list of known CT logs: %v", err) 31 | } 32 | defer resp.Body.Close() 33 | 34 | var logs struct { 35 | Operators []struct { 36 | Name string `json:"name"` 37 | Logs []struct { 38 | ID string `json:"log_id"` 39 | URL string `json:"url"` 40 | } `json:"logs"` 41 | } `json:"operators"` 42 | } 43 | 44 | if err := json.NewDecoder(resp.Body).Decode(&logs); err != nil { 45 | log.Fatalf("Failed to parse the list of known CT logs: %v", err) 46 | } 47 | 48 | f, err := os.Create(*out) 49 | if err != nil { 50 | log.Fatalf("Failed to open file %s", *out) 51 | } 52 | defer f.Close() 53 | 54 | fmt.Fprintf(f, `// Code generated by github.com/square/certigo/internal/gen-known-logs; DO NOT EDIT 55 | // Generated at %s 56 | package %s 57 | 58 | type ctLog struct { 59 | operator string 60 | url string 61 | } 62 | 63 | var knownLogs = map[string]*ctLog{ 64 | `, time.Now().Format(time.RFC3339), os.Getenv("GOPACKAGE")) 65 | knownLogs := make(map[string]*ctLog) 66 | for _, op := range logs.Operators { 67 | for _, l := range op.Logs { 68 | fmt.Fprintf(f, "\t%q: {operator: %q, url: %q},\n", l.ID, op.Name, l.URL) 69 | knownLogs[l.ID] = &ctLog{ 70 | operator: op.Name, 71 | url: l.URL, 72 | } 73 | } 74 | } 75 | fmt.Fprintln(f, "}") 76 | } 77 | -------------------------------------------------------------------------------- /jceks/AUTHORS: -------------------------------------------------------------------------------- 1 | This package was originally written by Peter Mattis. 2 | -------------------------------------------------------------------------------- /jceks/README.md: -------------------------------------------------------------------------------- 1 | # JCEKS 2 | 3 | Package jceks parses JCEKS (Java Cryptogaphy Extension Key Store) 4 | files and extracts keys and certificates. This module only implements 5 | a fraction of the JCEKS cryptographic protocols. In particular, it 6 | implements the SHA1 signature verification of the key store and the 7 | PBEWithMD5AndDES3CBC cipher for encrypting private keys. 8 | -------------------------------------------------------------------------------- /jceks/jceks_test.go: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2016 Square Inc. 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 | 17 | package jceks 18 | 19 | import ( 20 | "bytes" 21 | "crypto/rsa" 22 | "errors" 23 | "flag" 24 | "fmt" 25 | "os" 26 | "os/exec" 27 | "reflect" 28 | "testing" 29 | ) 30 | 31 | var ( 32 | generateTestData = flag.Bool("generate", false, "generate test data") 33 | ) 34 | 35 | type testData struct { 36 | certFilename string 37 | keyFilename string 38 | p12Filename string 39 | jceksFilename string 40 | storePassword string 41 | keyPassword string 42 | alias string 43 | } 44 | 45 | func newTestData(prefix string) *testData { 46 | return &testData{ 47 | certFilename: "testdata/" + prefix + ".crt", 48 | keyFilename: "testdata/" + prefix + ".key", 49 | p12Filename: "testdata/" + prefix + ".p12", 50 | jceksFilename: "testdata/" + prefix + ".jceks", 51 | storePassword: prefix + "-store-password", 52 | keyPassword: prefix + "-key-password", 53 | alias: prefix + "-some-alias", 54 | } 55 | } 56 | 57 | func (d *testData) cleanup() { 58 | os.Remove(d.certFilename) 59 | os.Remove(d.keyFilename) 60 | os.Remove(d.p12Filename) 61 | os.Remove(d.jceksFilename) 62 | } 63 | 64 | func runCommand(name string, args ...string) (string, error) { 65 | cmd := exec.Command(name, args...) 66 | var buf bytes.Buffer 67 | cmd.Stdout = &buf 68 | cmd.Stderr = &buf 69 | err := cmd.Run() 70 | out := buf.Bytes() 71 | if err != nil { 72 | return "", errors.New(string(out)) 73 | } 74 | return string(out), nil 75 | } 76 | 77 | func (d *testData) generatePrivateKeyAndCert(t *testing.T) { 78 | _, err := runCommand("openssl", "req", "-x509", 79 | "-nodes", "-days", "365", "-newkey", "rsa:2048", 80 | "-subj", "/CN=Test User/O=Test Organization/C=US", 81 | "-extensions", "v3_req", 82 | "-keyout", d.keyFilename, 83 | "-out", d.certFilename) 84 | if err != nil { 85 | t.Fatal(err) 86 | } 87 | } 88 | 89 | func (d *testData) convertPrivateKeyAndCertToPkcs12(t *testing.T) { 90 | _, err := runCommand("openssl", "pkcs12", "-export", 91 | "-in", d.certFilename, 92 | "-inkey", d.keyFilename, 93 | "-name", d.alias, 94 | "-out", d.p12Filename, 95 | "-passout", fmt.Sprintf("pass:%s", d.storePassword)) 96 | if err != nil { 97 | t.Fatal(err) 98 | } 99 | } 100 | 101 | func (d *testData) convertPkcs12ToJceks(t *testing.T) { 102 | _, err := runCommand("keytool", "-importkeystore", 103 | "-alias", d.alias, 104 | "-destkeypass", d.keyPassword, 105 | "-destkeystore", d.jceksFilename, 106 | "-deststorepass", d.storePassword, 107 | "-srckeystore", d.p12Filename, 108 | "-srcstoretype", "PKCS12", 109 | "-srcstorepass", d.storePassword, 110 | "-storetype", "JCEKS") 111 | if err != nil { 112 | t.Fatal(err) 113 | } 114 | } 115 | 116 | func (d *testData) importCertToJceks(t *testing.T) { 117 | _, err := runCommand("keytool", "-importcert", "-noprompt", 118 | "-alias", d.alias, 119 | "-file", d.certFilename, 120 | "-keystore", d.jceksFilename, 121 | "-storepass", d.storePassword, 122 | "-storetype", "JCEKS") 123 | if err != nil { 124 | t.Fatal(err) 125 | } 126 | } 127 | 128 | func equalRSAPublicKey(a, b *rsa.PublicKey) bool { 129 | if a.E != b.E { 130 | return false 131 | } 132 | return a.N.Cmp(b.N) == 0 133 | } 134 | 135 | func equalRSAPrivateKey(a, b *rsa.PrivateKey) bool { 136 | if !equalRSAPublicKey(&a.PublicKey, &b.PublicKey) { 137 | return false 138 | } 139 | if a.D.Cmp(b.D) != 0 { 140 | return false 141 | } 142 | if len(a.Primes) != len(b.Primes) { 143 | return false 144 | } 145 | for i := 0; i < len(a.Primes); i++ { 146 | if a.Primes[i].Cmp(b.Primes[i]) != 0 { 147 | return false 148 | } 149 | } 150 | return true 151 | } 152 | 153 | func TestPrivateKey(t *testing.T) { 154 | d := newTestData("private-key") 155 | if *generateTestData { 156 | d.cleanup() 157 | d.generatePrivateKeyAndCert(t) 158 | d.convertPrivateKeyAndCertToPkcs12(t) 159 | d.convertPkcs12ToJceks(t) 160 | } 161 | 162 | ks, err := LoadFromFile(d.jceksFilename, []byte(d.storePassword)) 163 | if err != nil { 164 | t.Fatal(err) 165 | } 166 | key, certs, err := ks.GetPrivateKeyAndCerts(d.alias, []byte(d.keyPassword)) 167 | if err != nil { 168 | t.Fatal(err) 169 | } 170 | if key == nil { 171 | t.Fatal("unable to load key") 172 | } 173 | 174 | expected, err := LoadPEMKey(d.keyFilename) 175 | if err != nil { 176 | t.Fatal(err) 177 | } 178 | if !equalRSAPrivateKey(key.(*rsa.PrivateKey), expected) { 179 | t.Fatalf("keys are not equal") 180 | } 181 | 182 | if len(certs) != 1 { 183 | t.Fatalf("unexpected number of certs: %d != 1", len(certs)) 184 | } 185 | 186 | expectedCert, err := LoadPEMCert(d.certFilename) 187 | if err != nil { 188 | t.Fatal(err) 189 | } 190 | 191 | if !certs[0].Equal(expectedCert) { 192 | t.Fatalf("certs are not equal") 193 | } 194 | 195 | keyAliases := ks.ListPrivateKeys() 196 | if !reflect.DeepEqual(keyAliases, []string{d.alias}) { 197 | t.Fatalf("unexpected private key aliases: %s", keyAliases) 198 | } 199 | } 200 | 201 | func TestTrustedCert(t *testing.T) { 202 | d := newTestData("trusted-cert") 203 | if *generateTestData { 204 | d.cleanup() 205 | d.generatePrivateKeyAndCert(t) 206 | d.importCertToJceks(t) 207 | } 208 | 209 | ks, err := LoadFromFile(d.jceksFilename, []byte(d.storePassword)) 210 | if err != nil { 211 | t.Fatal(err) 212 | } 213 | cert, err := ks.GetCert(d.alias) 214 | if err != nil { 215 | t.Fatal(err) 216 | } 217 | if cert == nil { 218 | t.Fatal("unable to load cert") 219 | } 220 | 221 | expectedCert, err := LoadPEMCert(d.certFilename) 222 | if err != nil { 223 | t.Fatal(err) 224 | } 225 | 226 | if !cert.Equal(expectedCert) { 227 | t.Fatalf("certs are not equal") 228 | } 229 | 230 | certAliases := ks.ListCerts() 231 | if !reflect.DeepEqual(certAliases, []string{d.alias}) { 232 | t.Fatalf("unexpected cert aliases: %s", certAliases) 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /jceks/pbe.go: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2016 Square Inc. 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 | 17 | package jceks 18 | 19 | import ( 20 | "bytes" 21 | "crypto/cipher" 22 | "crypto/des" 23 | "crypto/md5" 24 | "crypto/x509/pkix" 25 | "encoding/asn1" 26 | "fmt" 27 | ) 28 | 29 | var ( 30 | oidPBEWithMD5AndDES3CBC = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 42, 2, 19, 1} 31 | ) 32 | 33 | type pbeParameters struct { 34 | Salt []byte 35 | Iterations int 36 | } 37 | 38 | // Here's how this algorithm works: 39 | // 40 | // 1. Split salt in two halves. If the two halves are identical, 41 | // invert one of them. 42 | // 2. Concatenate password with each of the halves. 43 | // 3. Digest each concatenation with c iterations, where c is the 44 | // iterationCount. Concatenate the output from each digest round with the 45 | // password, and use the result as the input to the next digest operation. 46 | // The digest algorithm is MD5. 47 | // 4. After c iterations, use the 2 resulting digests as follows: 48 | // The 16 bytes of the first digest and the 1st 8 bytes of the 2nd digest 49 | // form the triple DES key, and the last 8 bytes of the 2nd digest form the 50 | // IV. 51 | func recoverPBEWithMD5AndDES3CBC( 52 | algo pkix.AlgorithmIdentifier, encryptedKey, password []byte) ([]byte, error) { 53 | var params pbeParameters 54 | if _, err := asn1.Unmarshal(algo.Parameters.FullBytes, ¶ms); err != nil { 55 | return nil, err 56 | } 57 | 58 | // Convert password to byte array, so that it can be digested. 59 | passwdBytes := make([]byte, len(password)) 60 | for i := 0; i < len(password); i++ { 61 | passwdBytes[i] = password[i] & 0x7f 62 | } 63 | 64 | salt := params.Salt 65 | if len(salt) != 8 { 66 | return nil, fmt.Errorf("unexpected salt length: %d", len(salt)) 67 | } 68 | 69 | if bytes.Compare(salt[0:4], salt[4:]) == 0 { 70 | // First and second half of salt are equal, invert first half. 71 | for i := 0; i < 2; i++ { 72 | salt[i], salt[3-i] = salt[3-i], salt[i] 73 | } 74 | } 75 | 76 | const keyLen = 24 77 | const blockSize = des.BlockSize 78 | derivedKey := make([]byte, keyLen+blockSize) 79 | // Now digest each half (concatenated with password). For each 80 | // half, go through the loop as many times as specified by the 81 | // iteration count parameter (inner for loop). Concatenate the 82 | // output from each digest round with the password, and use the 83 | // result as the input to the next digest operation. 84 | md := md5.New() 85 | for i := 0; i < 2; i++ { 86 | n := len(salt) / 2 87 | toBeHashed := salt[i*n : (i+1)*n] 88 | for j := 0; j < params.Iterations; j++ { 89 | md.Write(toBeHashed) 90 | md.Write(passwdBytes) 91 | toBeHashed = md.Sum([]byte{}) 92 | md.Reset() 93 | } 94 | copy(derivedKey[i*len(toBeHashed):], toBeHashed) 95 | } 96 | 97 | cipherKey := derivedKey[0:keyLen] 98 | iv := derivedKey[keyLen:] 99 | 100 | des3, err := des.NewTripleDESCipher(cipherKey) 101 | if err != nil { 102 | return nil, err 103 | } 104 | 105 | decrypter := cipher.NewCBCDecrypter(des3, iv) 106 | if (len(encryptedKey) % decrypter.BlockSize()) != 0 { 107 | return nil, fmt.Errorf("encrypted data must be a multiple of block length: %d %d", 108 | len(encryptedKey), decrypter.BlockSize()) 109 | } 110 | 111 | decryptedKey := make([]byte, len(encryptedKey)) 112 | decrypter.CryptBlocks(decryptedKey, encryptedKey) 113 | return decryptedKey, nil 114 | } 115 | -------------------------------------------------------------------------------- /jceks/testdata/private-key.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDFzCCAf+gAwIBAgIJALRGD3GBYHSZMA0GCSqGSIb3DQEBBQUAMD0xEjAQBgNV 3 | BAMTCVRlc3QgVXNlcjEaMBgGA1UEChMRVGVzdCBPcmdhbml6YXRpb24xCzAJBgNV 4 | BAYTAlVTMB4XDTE0MDMxNDE0MTA0NFoXDTE1MDMxNDE0MTA0NFowPTESMBAGA1UE 5 | AxMJVGVzdCBVc2VyMRowGAYDVQQKExFUZXN0IE9yZ2FuaXphdGlvbjELMAkGA1UE 6 | BhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDjX4e65+mxX5Ha 7 | SbSHwluBy9v7+pJrOuEco+w1dFjw7g5ijjB/5HW72N9J8LfQrEAMX9FuMue7lE1y 8 | GcgL9Kepuz71jPjyjrHpFNa90dKzrhwkQ6f9aLkKd5D+84ua7/O7/l5yiuD+slvm 9 | dppj+WxfWfQ58bgFjoxwkt2voYa/+pYK52CkykaQ0jtfEIdbftRAT2rLVSD6UljT 10 | 9t54xUL80CHgog8nlySTFdpPnTQ2jpYBDziRpO6Agjz6zjOo2tiY+yo/2QL3rhzQ 11 | e8zYVjH+jZ3hKAeQp5kOTdfjkxJzQe3LLm0pQCkaWhglcnZhSqdErVScNmv87nR8 12 | CEXXK9O/AgMBAAGjGjAYMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMA0GCSqGSIb3 13 | DQEBBQUAA4IBAQAzbdBJFsYq2Rrh0xwHuYTcA/NesrV1wC36kCcVbFI3JVqZ2lZz 14 | MlA89mJ8oOfH2Cg0hB9XlQoysdYiKXwl9tJP6lOimSp2nSn0NOKBaSvvpUyMBocn 15 | 29L1IPm6BrauGGDhKxByA3sPc/FSZnCx2p1OO9HG3vAprYJppiiOSTwuRHO4+PBj 16 | gowPrvXGucKHfNlekxN1D6dYi+Zg+61pA/6qDJ+ZKXVECE3NN3H/o6WuOIB5tFVE 17 | tCJNSxo/8KnrU1TMG1e9EvcL3kq3rmXEID6pwmxqg3piKg3Dar/GkpxGmdB306UF 18 | HnRjsjYEj+HWOpfOy1vPbbKMXtC3TayPyGZ6 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /jceks/testdata/private-key.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/jceks/testdata/private-key.jceks -------------------------------------------------------------------------------- /jceks/testdata/private-key.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEA41+HuufpsV+R2km0h8Jbgcvb+/qSazrhHKPsNXRY8O4OYo4w 3 | f+R1u9jfSfC30KxADF/RbjLnu5RNchnIC/Snqbs+9Yz48o6x6RTWvdHSs64cJEOn 4 | /Wi5CneQ/vOLmu/zu/5ecorg/rJb5naaY/lsX1n0OfG4BY6McJLdr6GGv/qWCudg 5 | pMpGkNI7XxCHW37UQE9qy1Ug+lJY0/beeMVC/NAh4KIPJ5ckkxXaT500No6WAQ84 6 | kaTugII8+s4zqNrYmPsqP9kC964c0HvM2FYx/o2d4SgHkKeZDk3X45MSc0Htyy5t 7 | KUApGloYJXJ2YUqnRK1UnDZr/O50fAhF1yvTvwIDAQABAoIBAGnboCW0s2iRRiaL 8 | CjHqmw/jCZhXILQrxYLADskUUhRZwPjBmnLwup+qaMrT98B/cZJRSgA0Uw9SDHyC 9 | 5FAsp6KuOrG34G+NX+dUfGYDukVNWmzH0v54My5cXHVWjjikXqW31+EcJ4RtJbw2 10 | m8rP5VS/XKVdlH+BzoDa37tVSJc4AraA3XHaU6ebuWojSiHJ3T1L1gTZKmOIL2dw 11 | ETIgxKfQTVwceWBmQv61pG5bxqIsmDpeCWjeULchSvuiHG0xRfzJYewhv8LKVpEJ 12 | /bSb5kciAm+nnigme2kjsp05AGmrncv7XYRrn1caJc30kwGsbv5Y7UJ9ZG5amlL6 13 | ltBxSQECgYEA+iqKyEdf/3qORker/WvHEL54mrbfSYoB3B6mr7uKb/JuIOk1yYX5 14 | Fb0xDavxypBvwAUTNpRjjMRr1HpKggfv4ARudgjBxjD5Sy1TaBypYlB2gVuPRHZv 15 | TLbM7erVQVsg2jC36nQboO8ZiKiSpFlZiAn0ieCxmE9ixATzOJp9lwcCgYEA6Kzq 16 | uX/8BMFLmcHodpAakvxTfv3qGgz0GpSQAmu6bo4qyWuYH4hiBe8PDzCfSfwkhWFr 17 | Q06s9rKseSyAzqF5YBjq8+8bKjZyBaC8m4QEpgocZpwQqKicrnngh7LWiYVNLfw/ 18 | qPfrLc0RmgZistWbES1/6Pm3x+Z49PLyVcUAt4kCgYB4RYO7jjUlCrLkLwkNKYfn 19 | EOvC1jC7llIWldXlnvCLqa4wvG5TmMmMHg07WXNBw/c2BjqafvTtdHGzEahIo7A7 20 | r2W78bHXqyvvbLcw0rbMwYp33qEedSJFa41SxRgJ99nvjISff3rZAJryDLmTsjFN 21 | KhwbPZ+kbmY5f3e/uuaueQKBgDkfyvkD/QHF2yPCwanqMzwHCxDQkhsXNw8Xjkup 22 | 2zmtWb/d1JlZSIega5gVHeZyKx08D7OUq05eC44saOtSJZR8SaLd/1Nbzp6nGecs 23 | gF+rd9GRW12tF9qWPZPTSmy093/kwFRhmbHC+SFRlAXH/6w1+YNfW8mOQgARbYG1 24 | PjnRAoGBAIbnGfWSLl6jlo1W9CVk8AylY3TFmVygbijjrK3UYL/3KLJn6nb2BeW0 25 | IsigU5qe+za2zW2KDkw//9vqm0omda66ubwU24uXjKwhZbrAAhmy8kFZNRzpMZIF 26 | Q3RqsGxPxWt7Rk79OBxtnPLWc8nxpGTliOUzgeZqnLy1aSAZnngn 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /jceks/testdata/private-key.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/jceks/testdata/private-key.p12 -------------------------------------------------------------------------------- /jceks/testdata/trusted-cert.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDFzCCAf+gAwIBAgIJANpdtwWQVU6qMA0GCSqGSIb3DQEBBQUAMD0xEjAQBgNV 3 | BAMTCVRlc3QgVXNlcjEaMBgGA1UEChMRVGVzdCBPcmdhbml6YXRpb24xCzAJBgNV 4 | BAYTAlVTMB4XDTE0MDMxNDE0MTA0NVoXDTE1MDMxNDE0MTA0NVowPTESMBAGA1UE 5 | AxMJVGVzdCBVc2VyMRowGAYDVQQKExFUZXN0IE9yZ2FuaXphdGlvbjELMAkGA1UE 6 | BhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTX69McLtRMUhP 7 | ZSKzyTee1kY6XUyiHqEapxhKxTkT5HyQaKspIeP60keOiCn6y/stwXK/4fjm4XMx 8 | jDIw2HE6/gwLBXPr3IH3x44XXjK9U3SGSCrFhP0aI4dNxLq7oKhzfR15Wz3pw4zY 9 | q3mtT4muefqjkcVUNTwJyzxA5D5km1Coq7xKZ9Demt71NUcAYOfJswFh97bI8QpK 10 | MfaYbUSWw0um+Mz2vlL20Yq2xWjX+CgclnVWjsf/zrbXkuGyWPE1VbTCzwvdbUHW 11 | 8cokI+UBQC/FGIxVq11hGn+Tk8cMubnDLEqRf3oiCGx0wvKixP55YlthRElSaNKt 12 | DmduKH2hAgMBAAGjGjAYMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMA0GCSqGSIb3 13 | DQEBBQUAA4IBAQAY6BFSl2zjhsD2udgb7mgOFgCwzWzCis5+Oy+2H/uNjyeBuSgM 14 | qrh779y+BRapxrqBSYo8QWltapAlRJ8MwIYJpHz2FqN4bY38r3WMZ380wVT/wDYh 15 | 6WFB3REVKEkhsW2HWX4cOF8m2iD/WOR2x2T0F5EC4W1nofl/oIhgujDNjmerZEMF 16 | JPnXHGRNOpYtyZhWuo+CFh500vV9Azomcw3z3NJ4teW5Z2T0mmOQWLB1cc1rAVYB 17 | W9uqE5GyHcQ6VHdQHjq3luxOvzH88slQ2S/SC9a41l1Z6EAe8wlVEa17wTQ3PR2g 18 | UHFTtH5OUR9neGEx+G+GqnR7jfRAPZNv0DsH 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /jceks/testdata/trusted-cert.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/jceks/testdata/trusted-cert.jceks -------------------------------------------------------------------------------- /jceks/testdata/trusted-cert.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEA01+vTHC7UTFIT2Uis8k3ntZGOl1Moh6hGqcYSsU5E+R8kGir 3 | KSHj+tJHjogp+sv7LcFyv+H45uFzMYwyMNhxOv4MCwVz69yB98eOF14yvVN0hkgq 4 | xYT9GiOHTcS6u6Coc30deVs96cOM2Kt5rU+Jrnn6o5HFVDU8Ccs8QOQ+ZJtQqKu8 5 | SmfQ3pre9TVHAGDnybMBYfe2yPEKSjH2mG1ElsNLpvjM9r5S9tGKtsVo1/goHJZ1 6 | Vo7H/86215LhsljxNVW0ws8L3W1B1vHKJCPlAUAvxRiMVatdYRp/k5PHDLm5wyxK 7 | kX96IghsdMLyosT+eWJbYURJUmjSrQ5nbih9oQIDAQABAoIBAQCV28Ds8P/dEJOz 8 | toBj9sT4V/JybrNmPVD7FHykhi9xawzlVVAEWYLI0UzqQJ+CsBvk1MIGSK+vASgq 9 | eLsc5ldg+7yOE8+b6To78b9L0f0nPYPfsEqivyay4X2MJW4+mCjVuF6tK4M5uOqi 10 | svARb9KtYM3SKgc9LIDkcLLHTwrtR+ZRZNQ0rSXuE2dSJMETCXCdzuSaoQfoOThM 11 | Gmv6Sm04PVws5x2BQso9oThQHZnwCquSaveLMPguxnwanRKvCQkNvtnGzAeFQ7o3 12 | Lx5Zag4/4L5XLh0QT9+mWH8oRSFysPIk2pMhqzAAUxVjpo0RKmNYw7wEgYdwsXJl 13 | pC2X+iABAoGBAPGroa7gPgDYCzcf7tNYOTz/nDbwUXsv4B0bCsUId+71ExnVwqxI 14 | ubqYB0v23qjX+679CC1qsMb38//CBNlwruyLjxaDct3UFo3IfiXY1hAeN+Anvzeb 15 | gfoD3s2XGHPIG5niViHIEwZKubpzsQ6UXl8HSFBCPFD1piXJ7qs/zeHJAoGBAN/o 16 | LGbWww3vrYKZ+BCd3wIFrxurco94RDq7CfhTAinkc26WTmagyw4kXmZS0mtL8ycn 17 | 5PhIvCJaugnEUG1kEWDsYH57GMBG/BVoJC8to0yLrVh5fVzYl343i+KkqA3Y99bE 18 | NFoshK+L1JH3klPhqmN7wnra/VNp2LJm4Nz+4mkZAoGAHjc5VeYPmodoj5HciGwl 19 | a+0BmRTe+yn3OWxiIlR2ulfF9Zr2ZhgJsLzFXMgW+sFWZICafyMxyw7BYR7fAFjI 20 | Zibk0wnIWNflogCJVS4RRZ6hmdMeY1N8IshNGSNlGUTRvqG/5yVey5CYPCmu34XJ 21 | btQ4RGCjrfOovFzNDHhDw5ECgYAE5jWigmx+L5JiWzAcXPf2OV2dg2DcVstXZaRQ 22 | NLDFbeRAtTU99aK7ynvuTT2hb2YAo1TVQfIr5kRP1mXUHu5qaoGqAtOF0YfOiBrS 23 | lXMPR7chSnc9wtd9wYVkDipHM0oo/t4OYw78MFkUYJBpGXT6/EhDG+uTGavOK4Yc 24 | D8+wcQKBgQDU6tlGCHK1kfc1xO6uoxHB7yLVM7lh+irLHJb0gt/zGdJUOHom+4r3 25 | zBW22vWWrYv2kck0ez6hvwgw1eIys6yY3Cz+8GpR0wVPxj4CiVDWfhQZeuu4rP1d 26 | BFGjC0y1npoI4bJE8G3QiNJYRKaHCaf2hdvNr3AhwDT6QCDo+kQCtQ== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /jceks/utils_test.go: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2016 Square Inc. 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 | 17 | package jceks 18 | 19 | import ( 20 | "crypto/rsa" 21 | "crypto/x509" 22 | "encoding/pem" 23 | "fmt" 24 | "os" 25 | "strings" 26 | ) 27 | 28 | // LoadPEMKey extracts a private key from a PEM file. 29 | func LoadPEMKey(filename string) (*rsa.PrivateKey, error) { 30 | keyPEMBlock, err := os.ReadFile(filename) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | var keyDERBlock *pem.Block 36 | for { 37 | keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock) 38 | if keyDERBlock == nil { 39 | return nil, fmt.Errorf("failed to parse key PEM data") 40 | } 41 | if keyDERBlock.Type == "PRIVATE KEY" || 42 | strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") { 43 | break 44 | } 45 | } 46 | 47 | return x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes) 48 | } 49 | 50 | // LoadPEMCert extracts a certificate from a PEM file. 51 | func LoadPEMCert(filename string) (*x509.Certificate, error) { 52 | certPEMBlock, err := os.ReadFile(filename) 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | var certDERBlock *pem.Block 58 | for { 59 | certDERBlock, certPEMBlock = pem.Decode(certPEMBlock) 60 | if certDERBlock == nil { 61 | return nil, fmt.Errorf("failed to parse certificate PEM data") 62 | } 63 | if certDERBlock.Type == "CERTIFICATE" { 64 | break 65 | } 66 | } 67 | 68 | return x509.ParseCertificate(certDERBlock.Bytes) 69 | } 70 | -------------------------------------------------------------------------------- /lib/ct.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "crypto/x509" 5 | "encoding/asn1" 6 | "encoding/base64" 7 | "time" 8 | 9 | cttls "github.com/google/certificate-transparency-go/tls" 10 | ctx509 "github.com/google/certificate-transparency-go/x509" 11 | ctutil "github.com/google/certificate-transparency-go/x509util" 12 | ) 13 | 14 | //go:generate go run github.com/square/certigo/internal/gen-known-logs --out ctlogs.go 15 | //go:generate go fmt ctlogs.go 16 | 17 | func parseSCTList(cert *x509.Certificate) []*simpleSCT { 18 | // ctutil contains a fork of crypto/x509 with support for SCTs. We must re-parse the 19 | // whole certificate to get at them, so do a quick check to see if the SCT extension 20 | // is present before re-parsing the cert unnecessarily. 21 | if !hasSCTs(cert) { 22 | return nil 23 | } 24 | 25 | var sctList []*simpleSCT 26 | if scts, err := ctutil.ParseSCTsFromCertificate(cert.Raw); err == nil { 27 | for _, sct := range scts { 28 | id := sct.LogID.KeyID[:] 29 | ssct := &simpleSCT{ 30 | Version: uint64(sct.SCTVersion), 31 | LogID: id, 32 | Timestamp: time.UnixMilli(int64(sct.Timestamp)), 33 | SignatureAlgorithm: sctSignatureAlg(sct.Signature.Algorithm), 34 | } 35 | if log := getLogByID(id); log != nil { 36 | ssct.LogOperator = log.operator 37 | ssct.LogURL = log.url 38 | } 39 | sctList = append(sctList, ssct) 40 | } 41 | } 42 | return sctList 43 | } 44 | 45 | func hasSCTs(cert *x509.Certificate) bool { 46 | for _, e := range cert.Extensions { 47 | if e.Id.Equal(asn1.ObjectIdentifier(ctx509.OIDExtensionCTSCT)) { 48 | return true 49 | } 50 | } 51 | return false 52 | } 53 | 54 | func getLogByID(id []byte) *ctLog { 55 | b64 := base64.StdEncoding.EncodeToString(id) 56 | return knownLogs[b64] 57 | } 58 | 59 | func sctSignatureAlg(alg cttls.SignatureAndHashAlgorithm) simpleSigAlg { 60 | x509Alg := x509.UnknownSignatureAlgorithm 61 | switch alg.Signature { 62 | case cttls.RSA: 63 | switch alg.Hash { 64 | case cttls.MD5: 65 | x509Alg = x509.MD5WithRSA 66 | case cttls.SHA1: 67 | x509Alg = x509.SHA1WithRSA 68 | case cttls.SHA256: 69 | x509Alg = x509.SHA256WithRSA 70 | case cttls.SHA384: 71 | x509Alg = x509.SHA384WithRSA 72 | case cttls.SHA512: 73 | x509Alg = x509.SHA512WithRSA 74 | } 75 | case cttls.DSA: 76 | switch alg.Hash { 77 | case cttls.SHA1: 78 | x509Alg = x509.DSAWithSHA1 79 | case cttls.SHA256: 80 | x509Alg = x509.DSAWithSHA256 81 | } 82 | case cttls.ECDSA: 83 | switch alg.Hash { 84 | case cttls.SHA1: 85 | x509Alg = x509.ECDSAWithSHA1 86 | case cttls.SHA256: 87 | x509Alg = x509.ECDSAWithSHA256 88 | case cttls.SHA384: 89 | x509Alg = x509.ECDSAWithSHA384 90 | case cttls.SHA512: 91 | x509Alg = x509.ECDSAWithSHA512 92 | } 93 | } 94 | return simpleSigAlg(x509Alg) 95 | } 96 | -------------------------------------------------------------------------------- /lib/ocsp.go: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2018 Square Inc. 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 | 17 | package lib 18 | 19 | import ( 20 | "bytes" 21 | "crypto/x509" 22 | "encoding/base64" 23 | "errors" 24 | "fmt" 25 | "io" 26 | "net/http" 27 | "time" 28 | 29 | "github.com/fatih/color" 30 | "golang.org/x/crypto/ocsp" 31 | ) 32 | 33 | var ( 34 | errSkippedRevocationCheck = errors.New("skipped revocation check") 35 | 36 | revocationStatusColor = map[int]*color.Color{ 37 | ocsp.Good: green, 38 | ocsp.Revoked: red, 39 | ocsp.Unknown: yellow, 40 | } 41 | 42 | revocationStatusDescription = map[int]string{ 43 | ocsp.Good: "Good", 44 | ocsp.Revoked: "Revoked", 45 | ocsp.Unknown: "Unknown", 46 | } 47 | 48 | revocationReasonDescription = map[int]string{ 49 | ocsp.Unspecified: "Unspecified", 50 | ocsp.KeyCompromise: "KeyCompromise", 51 | ocsp.CACompromise: "CACompromise", 52 | ocsp.AffiliationChanged: "AffiliationChanged", 53 | ocsp.Superseded: "Superseded", 54 | ocsp.CessationOfOperation: "CessationOfOperation", 55 | ocsp.CertificateHold: "CertificateHold", 56 | ocsp.RemoveFromCRL: "RemoveFromCRL", 57 | ocsp.PrivilegeWithdrawn: "PrivilegeWithdrawn", 58 | ocsp.AACompromise: "AACompromise", 59 | } 60 | 61 | ocspHttpClient = &http.Client{ 62 | // Set a timeout so we don't block forever on broken servers. 63 | Timeout: 5 * time.Second, 64 | } 65 | ) 66 | 67 | const ( 68 | // We retry multiple times, because OCSP servers are often a bit unreliable. 69 | maxOCSPValidationRetries = 3 70 | ) 71 | 72 | func checkOCSP(chain []*x509.Certificate, ocspStaple []byte) (status *ocsp.Response, err error) { 73 | if len(chain) < 2 { 74 | // Nothing to check here 75 | return nil, errSkippedRevocationCheck 76 | } 77 | 78 | leaf, issuer := chain[0], chain[1] 79 | if len(leaf.OCSPServer) == 0 { 80 | return nil, errSkippedRevocationCheck 81 | } 82 | 83 | retries := maxOCSPValidationRetries 84 | if len(ocspStaple) > 0 { 85 | // Don't retry if stapled 86 | retries = 1 87 | } 88 | 89 | for i := 0; i < retries; i++ { 90 | encoded := ocspStaple 91 | if len(encoded) == 0 { 92 | encoded, err = fetchOCSP(leaf, issuer) 93 | if err != nil { 94 | return nil, err 95 | } 96 | } 97 | 98 | status, err = ocsp.ParseResponse(encoded, issuer) 99 | if err == nil { 100 | break 101 | } 102 | } 103 | 104 | return status, err 105 | } 106 | 107 | func fetchOCSP(cert, issuer *x509.Certificate) ([]byte, error) { 108 | encoded, err := ocsp.CreateRequest(cert, issuer, nil) 109 | if err != nil { 110 | return nil, fmt.Errorf("failure building request: %s", err) 111 | } 112 | 113 | // Try all the OCSP servers listed in the certificate 114 | var lastError error 115 | for _, server := range cert.OCSPServer { 116 | // We try both GET and POST requests, because some servers are janky. 117 | reqs := []*http.Request{} 118 | if len(encoded) < 255 { 119 | // GET only supported for requests with small payloads, so we can stash 120 | // them in the path. RFC says 255 bytes encoded, but doesn't mention if that 121 | // refers to the DER-encoded payload before or after base64 is applied. We 122 | // just assume it's the former and try both GET and POST in case one fails. 123 | req, err := buildOCSPwithGET(server, encoded) 124 | if err != nil { 125 | lastError = err 126 | continue 127 | } 128 | reqs = append(reqs, req) 129 | } 130 | 131 | // POST should always be supported, but some servers don't like it 132 | req, err := buildOCSPwithPOST(server, encoded) 133 | if err != nil { 134 | lastError = err 135 | continue 136 | } 137 | reqs = append(reqs, req) 138 | 139 | for _, req := range reqs { 140 | resp, err := ocspHttpClient.Do(req) 141 | if err != nil { 142 | lastError = err 143 | continue 144 | } 145 | 146 | if resp.StatusCode != http.StatusOK { 147 | lastError = fmt.Errorf("unexpected status code, got: %s", resp.Status) 148 | continue 149 | } 150 | 151 | body, err := io.ReadAll(resp.Body) 152 | defer resp.Body.Close() 153 | if err != nil { 154 | lastError = err 155 | continue 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 | req.Write(bytes.NewBuffer(encoded)) 173 | 174 | return req, nil 175 | } 176 | 177 | func buildOCSPwithGET(server string, encoded []byte) (*http.Request, error) { 178 | // https://datatracker.ietf.org/doc/html/rfc6960#appendix-A.1 179 | // GET {url}/{url-encoding of base-64 encoding of the DER encoding of the OCSPRequest} 180 | url := fmt.Sprintf("%s/%s", server, base64.StdEncoding.EncodeToString(encoded)) 181 | 182 | req, err := http.NewRequest("GET", url, nil) 183 | if err != nil { 184 | return nil, err 185 | } 186 | 187 | req.Header.Add("Accept", "application/ocsp-response") 188 | 189 | return req, nil 190 | } 191 | -------------------------------------------------------------------------------- /lib/oids.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import "encoding/asn1" 4 | 5 | // OidDescription returns a human-readable name, a short acronym from RFC1485, a snake_case slug suitable as a json key, 6 | // and a boolean describing whether multiple copies can appear on an X509 cert. 7 | type OidDescription struct { 8 | Name string 9 | Short string 10 | Slug string 11 | Multiple bool 12 | } 13 | 14 | func describeOid(oid asn1.ObjectIdentifier) OidDescription { 15 | raw := oid.String() 16 | // Multiple should be true for any types that are []string in x509.pkix.Name. When in doubt, set it to true. 17 | names := map[string]OidDescription{ 18 | "2.5.4.3": {"CommonName", "CN", "common_name", false}, 19 | "2.5.4.5": {"EV Incorporation Registration Number", "", "ev_registration_number", false}, 20 | "2.5.4.6": {"Country", "C", "country", true}, 21 | "2.5.4.7": {"Locality", "L", "locality", true}, 22 | "2.5.4.8": {"Province", "ST", "province", true}, 23 | "2.5.4.9": {"Street", "", "street", true}, 24 | "2.5.4.10": {"Organization", "O", "organization", true}, 25 | "2.5.4.11": {"Organizational Unit", "OU", "organizational_unit", true}, 26 | "2.5.4.15": {"Business Category", "", "business_category", true}, 27 | "2.5.4.17": {"Postal Code", "", "postalcode", true}, 28 | "1.2.840.113549.1.9.1": {"Email Address", "", "email_address", true}, 29 | "1.3.6.1.4.1.311.60.2.1.1": {"EV Incorporation Locality", "", "ev_locality", true}, 30 | "1.3.6.1.4.1.311.60.2.1.2": {"EV Incorporation Province", "", "ev_province", true}, 31 | "1.3.6.1.4.1.311.60.2.1.3": {"EV Incorporation Country", "", "ev_country", true}, 32 | "0.9.2342.19200300.100.1.1": {"User ID", "UID", "user_id", true}, 33 | "0.9.2342.19200300.100.1.25": {"Domain Component", "DC", "domain_component", true}, 34 | } 35 | if description, ok := names[raw]; ok { 36 | return description 37 | } 38 | return OidDescription{raw, "", raw, true} 39 | } 40 | 41 | func oidShort(oid asn1.ObjectIdentifier) string { 42 | return describeOid(oid).Short 43 | } 44 | 45 | func oidName(oid asn1.ObjectIdentifier) string { 46 | return describeOid(oid).Name 47 | } 48 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2016 Square Inc. 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 | 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 | -------------------------------------------------------------------------------- /pkcs7/pkcs7.go: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2016 Square Inc. 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 | 17 | package pkcs7 18 | 19 | import ( 20 | "crypto/x509" 21 | "encoding/asn1" 22 | "fmt" 23 | ) 24 | 25 | var signedDataIdentifier = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 2}) 26 | 27 | // SignedDataEnvelope represents a wrapped SignedData 28 | // object found in PEM-encoded PKCS7 blocks. 29 | type SignedDataEnvelope struct { 30 | Raw asn1.RawContent 31 | Type asn1.ObjectIdentifier 32 | SignedData SignedData `asn1:"tag:0,explicit,optional"` 33 | } 34 | 35 | // SignedData contains signed data and related info. 36 | // Refer to RFC 2315, Section 9.1 for definition of this type. 37 | type SignedData struct { 38 | Version int 39 | DigestAlgorithms []asn1.RawValue `asn1:"set"` 40 | ContentInfo asn1.RawValue 41 | Certificates []asn1.RawValue `asn1:"tag:0,optional,set"` 42 | RevocationLists []asn1.RawValue `asn1:"tag:1,optional,set"` 43 | SignerInfos []asn1.RawValue `asn1:"set"` 44 | } 45 | 46 | // ParseSignedData parses one (or more) signed data blocks from a byte array. 47 | func ParseSignedData(data []byte) ([]*SignedDataEnvelope, error) { 48 | var err error 49 | var block *SignedDataEnvelope 50 | var out []*SignedDataEnvelope 51 | 52 | for rest := data; len(rest) > 0; { 53 | block, rest, err = parseSignedData(rest) 54 | if err != nil { 55 | break 56 | } 57 | out = append(out, block) 58 | } 59 | 60 | return out, err 61 | } 62 | 63 | func parseSignedData(data []byte) (*SignedDataEnvelope, []byte, error) { 64 | var envelope SignedDataEnvelope 65 | rest, err := asn1.Unmarshal(data, &envelope) 66 | if err != nil { 67 | return nil, data, err 68 | } 69 | 70 | if !signedDataIdentifier.Equal(envelope.Type) { 71 | return nil, data, fmt.Errorf("unexpected object identifier (was %s, expecting %s)", envelope.Type.String(), signedDataIdentifier.String()) 72 | } 73 | 74 | if envelope.SignedData.Version != 1 { 75 | return nil, data, fmt.Errorf("unknown version number in signed data block (was %d, expecting 1)", envelope.SignedData.Version) 76 | } 77 | 78 | return &envelope, rest, nil 79 | } 80 | 81 | // ExtractCertificates reads a SignedData type and returns the embedded 82 | // certificates (if present in the structure). 83 | func ExtractCertificates(data []byte) ([]*x509.Certificate, error) { 84 | blocks, err := ParseSignedData(data) 85 | if err != nil { 86 | return nil, err 87 | } 88 | 89 | certs := []*x509.Certificate{} 90 | for _, block := range blocks { 91 | for _, raw := range block.SignedData.Certificates { 92 | cert, err := x509.ParseCertificate(raw.FullBytes) 93 | if err != nil { 94 | return nil, err 95 | } 96 | certs = append(certs, cert) 97 | } 98 | } 99 | 100 | return certs, nil 101 | } 102 | -------------------------------------------------------------------------------- /pkcs7/pkcs7_test.go: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2016 Square Inc. 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 | 17 | package pkcs7 18 | 19 | import ( 20 | "encoding/base64" 21 | "testing" 22 | ) 23 | 24 | var testBlock, _ = base64.StdEncoding.DecodeString(` 25 | MIICXAYJKoZIhvcNAQcCoIICTTCCAkkCAQExADALBgkqhkiG9w0BBwGgggIvMIIC 26 | KzCCAZQCCQDHlr/u+lfb8jANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzEL 27 | MAkGA1UECBMCQ0ExEDAOBgNVBAoTB2NlcnRpZ28xEDAOBgNVBAsTB2V4YW1wbGUx 28 | GjAYBgNVBAMTEWV4YW1wbGUtc21hbGwta2V5MB4XDTE2MDYxMDIyMTQxMloXDTIz 29 | MDQxNTIyMTQxMlowWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQK 30 | EwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRowGAYDVQQDExFleGFtcGxlLXNt 31 | YWxsLWtleTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAlzyCIeP1T87k1rHV 32 | MtbaGXIWpK/VQuvuXwig+e3ct1ajA4bw0BAInXZ37FEGGSCUix0k/CjH2NltGREt 33 | wbahE0k5oTkVbA5XS4xkNs0M0poAFN5OiFKEAqZ014hqhvKnEUQ2oTe9SVORWw49 34 | mLNg36AIEE2Fu2KQb/VT90cwwD0CAwEAATANBgkqhkiG9w0BAQsFAAOBgQBVsJ4V 35 | b2L1ywLVeAxNqY0PZqS7a8Q2GLhNr5V+3hOoWn7bwqQ7L06UJGSrcLOPZeIHIWM2 36 | 0aOFHSTWbocd4f+m6s3llyXwBBlK2BPZbWv0OeAHgjN9AVav4flAZ4oD2GxAaJkG 37 | AXmR9QzZNJLai5mv3L/B/p/NxeU3UGfaySxVv6EAMQA=`) 38 | 39 | func TestExtract(t *testing.T) { 40 | certs, err := ExtractCertificates(testBlock) 41 | if err != nil { 42 | t.Fatal(err) 43 | } 44 | if len(certs) != 1 { 45 | t.Fatalf("expected 1 certs, but found %d", len(certs)) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /starttls/dialer.go: -------------------------------------------------------------------------------- 1 | package starttls 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | "net" 7 | "net/url" 8 | "time" 9 | 10 | "github.com/mwitkow/go-http-dialer" 11 | ) 12 | 13 | type timeoutError struct{} 14 | 15 | func (timeoutError) Error() string { return "tls: DialWithDialer timed out" } 16 | func (timeoutError) Timeout() bool { return true } 17 | func (timeoutError) Temporary() bool { return true } 18 | 19 | // Dialer is an interface for dialers (either net.Dialer, or http_dialer.HttpTunnel) 20 | type Dialer interface { 21 | Dial(network, address string) (net.Conn, error) 22 | } 23 | 24 | // Internal copy of tls.DialWithDialer, adapter so it can work with HTTP CONNECT dialers. 25 | // See: https://golang.org/pkg/crypto/tls/#DialWithDialer 26 | func dialWithDialer(dialer Dialer, timeout time.Duration, network, addr string, config *tls.Config) (*tls.Conn, error) { 27 | var errChannel chan error 28 | if timeout != 0 { 29 | errChannel = make(chan error, 2) 30 | time.AfterFunc(timeout, func() { 31 | errChannel <- timeoutError{} 32 | }) 33 | } 34 | 35 | rawConn, err := dialer.Dial(network, addr) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | conn := tls.Client(rawConn, config) 41 | if timeout == 0 { 42 | err = conn.Handshake() 43 | } else { 44 | go func() { 45 | errChannel <- conn.Handshake() 46 | }() 47 | 48 | err = <-errChannel 49 | } 50 | 51 | if err != nil { 52 | rawConn.Close() 53 | return nil, err 54 | } 55 | 56 | return conn, nil 57 | } 58 | 59 | func wrapDialerWithProxy(dialer Dialer, connectProxy *url.URL, tlsConfig *tls.Config) (Dialer, error) { 60 | dialerOpt := http_dialer.WithDialer(dialer.(*net.Dialer)) 61 | tlsOpt := http_dialer.WithTls(tlsConfig) 62 | if connectProxy.User != nil { 63 | password, ok := connectProxy.User.Password() 64 | if !ok { 65 | return nil, fmt.Errorf("proxy username without password not currently supported") 66 | } 67 | auth := http_dialer.WithProxyAuth(http_dialer.AuthBasic(connectProxy.User.Username(), password)) 68 | dialer = http_dialer.New(connectProxy, dialerOpt, tlsOpt, auth) 69 | } else { 70 | dialer = http_dialer.New(connectProxy, dialerOpt, tlsOpt) 71 | } 72 | return dialer, nil 73 | } 74 | -------------------------------------------------------------------------------- /starttls/ftp.go: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2017 Square Inc. 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 | 17 | package starttls 18 | 19 | import ( 20 | "bufio" 21 | "crypto/tls" 22 | "fmt" 23 | "net" 24 | "strconv" 25 | ) 26 | 27 | func dumpTLSConnStateFromFTP(dialer Dialer, address string, config *tls.Config) (*tls.ConnectionState, error) { 28 | c, err := dialer.Dial("tcp", address) 29 | if err != nil { 30 | return nil, err 31 | } 32 | 33 | conn := c.(*net.TCPConn) 34 | status, err := readFTP(conn) 35 | if err != nil { 36 | return nil, err 37 | } 38 | if status != 220 { 39 | return nil, fmt.Errorf("FTP server responded with status %d, was expecting 220", status) 40 | } 41 | 42 | fmt.Fprintf(conn, "AUTH TLS\r\n") 43 | status, err = readFTP(conn) 44 | if err != nil { 45 | return nil, err 46 | } 47 | if status != 234 { 48 | return nil, fmt.Errorf("FTP server responded with status %d, was expecting 234", status) 49 | } 50 | 51 | tlsConn := tls.Client(conn, config) 52 | err = tlsConn.Handshake() 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | state := tlsConn.ConnectionState() 58 | return &state, nil 59 | } 60 | 61 | func readFTP(conn *net.TCPConn) (int, error) { 62 | reader := bufio.NewReader(conn) 63 | response, err := reader.ReadString('\n') 64 | if err != nil { 65 | return 0, err 66 | } 67 | if len(response) <= 3 { 68 | return 0, fmt.Errorf("Error parsing ftp protocol: Status code too short: '%s'", response) 69 | } 70 | return strconv.Atoi(response[:3]) 71 | } 72 | -------------------------------------------------------------------------------- /starttls/imap.go: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2017 Square Inc. 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 | 17 | package starttls 18 | 19 | import ( 20 | "bufio" 21 | "crypto/tls" 22 | "fmt" 23 | "net" 24 | ) 25 | 26 | func dumpTLSConnStateFromIMAP(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 := readIMAP(conn) 34 | if err != nil { 35 | return nil, err 36 | } 37 | if status != "OK" { 38 | return nil, fmt.Errorf("IMAP server responded with %s, was expecting OK", status) 39 | } 40 | 41 | fmt.Fprintf(conn, "1 STARTTLS\r\n") 42 | status, err = readIMAP(conn) 43 | if err != nil { 44 | return nil, err 45 | } 46 | if status != "OK" { 47 | return nil, fmt.Errorf("IMAP server responded with %s, was expecting OK", 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 readIMAP(conn *net.TCPConn) (string, error) { 61 | reader := bufio.NewReader(conn) 62 | response, err := reader.ReadString('\n') 63 | if err != nil { 64 | return "", err 65 | } 66 | return response[2:4], nil 67 | } 68 | -------------------------------------------------------------------------------- /starttls/ldap/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/starttls/ldap/.gitignore -------------------------------------------------------------------------------- /starttls/ldap/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | env: 3 | global: 4 | - VET_VERSIONS="1.6 1.7 tip" 5 | - LINT_VERSIONS="1.6 1.7 tip" 6 | go: 7 | - 1.2 8 | - 1.3 9 | - 1.4 10 | - 1.5 11 | - 1.6 12 | - 1.7 13 | - tip 14 | matrix: 15 | fast_finish: true 16 | allow_failures: 17 | - go: tip 18 | go_import_path: gopkg.in/ldap.v2 19 | install: 20 | - go get gopkg.in/asn1-ber.v1 21 | - go get gopkg.in/ldap.v2 22 | - go get code.google.com/p/go.tools/cmd/cover || go get golang.org/x/tools/cmd/cover 23 | - go get github.com/golang/lint/golint || true 24 | - go build -v ./... 25 | script: 26 | - make test 27 | - make fmt 28 | - if [[ "$VET_VERSIONS" == *"$TRAVIS_GO_VERSION"* ]]; then make vet; fi 29 | - if [[ "$LINT_VERSIONS" == *"$TRAVIS_GO_VERSION"* ]]; then make lint; fi 30 | -------------------------------------------------------------------------------- /starttls/ldap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com) 4 | Portions copyright (c) 2015-2016 go-ldap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /starttls/ldap/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: default install build test quicktest fmt vet lint 2 | 3 | GO_VERSION := $(shell go version | cut -d' ' -f3 | cut -d. -f2) 4 | 5 | # Only use the `-race` flag on newer versions of Go 6 | IS_OLD_GO := $(shell test $(GO_VERSION) -le 2 && echo true) 7 | ifeq ($(IS_OLD_GO),true) 8 | RACE_FLAG := 9 | else 10 | RACE_FLAG := -race 11 | endif 12 | 13 | default: fmt vet lint build quicktest 14 | 15 | install: 16 | go get -t -v ./... 17 | 18 | build: 19 | go build -v ./... 20 | 21 | test: 22 | go test -v $(RACE_FLAG) -cover ./... 23 | 24 | quicktest: 25 | go test ./... 26 | 27 | # Capture output and force failure when there is non-empty output 28 | fmt: 29 | @echo gofmt -l . 30 | @OUTPUT=`gofmt -l . 2>&1`; \ 31 | if [ "$$OUTPUT" ]; then \ 32 | echo "gofmt must be run on the following files:"; \ 33 | echo "$$OUTPUT"; \ 34 | exit 1; \ 35 | fi 36 | 37 | # Only run on go1.5+ 38 | vet: 39 | go tool vet -atomic -bool -copylocks -nilfunc -printf -shadow -rangeloops -unreachable -unsafeptr -unusedresult . 40 | 41 | # https://github.com/golang/lint 42 | # go get github.com/golang/lint/golint 43 | # Capture output and force failure when there is non-empty output 44 | # Only run on go1.5+ 45 | lint: 46 | @echo golint ./... 47 | @OUTPUT=`golint ./... 2>&1`; \ 48 | if [ "$$OUTPUT" ]; then \ 49 | echo "golint errors:"; \ 50 | echo "$$OUTPUT"; \ 51 | exit 1; \ 52 | fi 53 | -------------------------------------------------------------------------------- /starttls/ldap/README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](https://godoc.org/gopkg.in/ldap.v2?status.svg)](https://godoc.org/gopkg.in/ldap.v2) 2 | [![Build Status](https://travis-ci.org/go-ldap/ldap.svg)](https://travis-ci.org/go-ldap/ldap) 3 | 4 | # Basic LDAP v3 functionality for the GO programming language. 5 | 6 | ## Install 7 | 8 | For the latest version use: 9 | 10 | go get gopkg.in/ldap.v2 11 | 12 | Import the latest version with: 13 | 14 | import "gopkg.in/ldap.v2" 15 | 16 | ## Required Libraries: 17 | 18 | - gopkg.in/asn1-ber.v1 19 | 20 | ## Features: 21 | 22 | - Connecting to LDAP server (non-TLS, TLS, STARTTLS) 23 | - Binding to LDAP server 24 | - Searching for entries 25 | - Filter Compile / Decompile 26 | - Paging Search Results 27 | - Modify Requests / Responses 28 | - Add Requests / Responses 29 | - Delete Requests / Responses 30 | 31 | ## Examples: 32 | 33 | - search 34 | - modify 35 | 36 | ## Contributing: 37 | 38 | Bug reports and pull requests are welcome! 39 | 40 | Before submitting a pull request, please make sure tests and verification scripts pass: 41 | ``` 42 | make all 43 | ``` 44 | 45 | To set up a pre-push hook to run the tests and verify scripts before pushing: 46 | ``` 47 | ln -s ../../.githooks/pre-push .git/hooks/pre-push 48 | ``` 49 | 50 | --- 51 | The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/) 52 | The design is licensed under the Creative Commons 3.0 Attributions license. 53 | Read this article for more details: http://blog.golang.org/gopher 54 | -------------------------------------------------------------------------------- /starttls/ldap/add.go: -------------------------------------------------------------------------------- 1 | // 2 | // https://tools.ietf.org/html/rfc4511 3 | // 4 | // AddRequest ::= [APPLICATION 8] SEQUENCE { 5 | // entry LDAPDN, 6 | // attributes AttributeList } 7 | // 8 | // AttributeList ::= SEQUENCE OF attribute Attribute 9 | 10 | package ldap 11 | 12 | import ( 13 | "errors" 14 | "log" 15 | 16 | "gopkg.in/asn1-ber.v1" 17 | ) 18 | 19 | // Attribute represents an LDAP attribute 20 | type Attribute struct { 21 | // Type is the name of the LDAP attribute 22 | Type string 23 | // Vals are the LDAP attribute values 24 | Vals []string 25 | } 26 | 27 | func (a *Attribute) encode() *ber.Packet { 28 | seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attribute") 29 | seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.Type, "Type")) 30 | set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue") 31 | for _, value := range a.Vals { 32 | set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals")) 33 | } 34 | seq.AppendChild(set) 35 | return seq 36 | } 37 | 38 | // AddRequest represents an LDAP AddRequest operation 39 | type AddRequest struct { 40 | // DN identifies the entry being added 41 | DN string 42 | // Attributes list the attributes of the new entry 43 | Attributes []Attribute 44 | } 45 | 46 | func (a AddRequest) encode() *ber.Packet { 47 | request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationAddRequest, nil, "Add Request") 48 | request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.DN, "DN")) 49 | attributes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes") 50 | for _, attribute := range a.Attributes { 51 | attributes.AppendChild(attribute.encode()) 52 | } 53 | request.AppendChild(attributes) 54 | return request 55 | } 56 | 57 | // Attribute adds an attribute with the given type and values 58 | func (a *AddRequest) Attribute(attrType string, attrVals []string) { 59 | a.Attributes = append(a.Attributes, Attribute{Type: attrType, Vals: attrVals}) 60 | } 61 | 62 | // NewAddRequest returns an AddRequest for the given DN, with no attributes 63 | func NewAddRequest(dn string) *AddRequest { 64 | return &AddRequest{ 65 | DN: dn, 66 | } 67 | 68 | } 69 | 70 | // Add performs the given AddRequest 71 | func (l *Conn) Add(addRequest *AddRequest) error { 72 | packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") 73 | packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) 74 | packet.AppendChild(addRequest.encode()) 75 | 76 | l.Debug.PrintPacket(packet) 77 | 78 | msgCtx, err := l.sendMessage(packet) 79 | if err != nil { 80 | return err 81 | } 82 | defer l.finishMessage(msgCtx) 83 | 84 | l.Debug.Printf("%d: waiting for response", msgCtx.id) 85 | packetResponse, ok := <-msgCtx.responses 86 | if !ok { 87 | return NewError(ErrorNetwork, errors.New("ldap: response channel closed")) 88 | } 89 | packet, err = packetResponse.ReadPacket() 90 | l.Debug.Printf("%d: got response %p", msgCtx.id, packet) 91 | if err != nil { 92 | return err 93 | } 94 | 95 | if l.Debug { 96 | if err := addLDAPDescriptions(packet); err != nil { 97 | return err 98 | } 99 | ber.PrintPacket(packet) 100 | } 101 | 102 | if packet.Children[1].Tag == ApplicationAddResponse { 103 | resultCode, resultDescription := getLDAPResultCode(packet) 104 | if resultCode != 0 { 105 | return NewError(resultCode, errors.New(resultDescription)) 106 | } 107 | } else { 108 | log.Printf("Unexpected Response: %d", packet.Children[1].Tag) 109 | } 110 | 111 | l.Debug.Printf("%d: returning", msgCtx.id) 112 | return nil 113 | } 114 | -------------------------------------------------------------------------------- /starttls/ldap/atomic_value.go: -------------------------------------------------------------------------------- 1 | // +build go1.4 2 | 3 | package ldap 4 | 5 | import ( 6 | "sync/atomic" 7 | ) 8 | 9 | // For compilers that support it, we just use the underlying sync/atomic.Value 10 | // type. 11 | type atomicValue struct { 12 | atomic.Value 13 | } 14 | -------------------------------------------------------------------------------- /starttls/ldap/atomic_value_go13.go: -------------------------------------------------------------------------------- 1 | // +build !go1.4 2 | 3 | package ldap 4 | 5 | import ( 6 | "sync" 7 | ) 8 | 9 | // This is a helper type that emulates the use of the "sync/atomic.Value" 10 | // struct that's available in Go 1.4 and up. 11 | type atomicValue struct { 12 | value interface{} 13 | lock sync.RWMutex 14 | } 15 | 16 | func (av *atomicValue) Store(val interface{}) { 17 | av.lock.Lock() 18 | av.value = val 19 | av.lock.Unlock() 20 | } 21 | 22 | func (av *atomicValue) Load() interface{} { 23 | av.lock.RLock() 24 | ret := av.value 25 | av.lock.RUnlock() 26 | 27 | return ret 28 | } 29 | -------------------------------------------------------------------------------- /starttls/ldap/bind.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ldap 6 | 7 | import ( 8 | "errors" 9 | 10 | "gopkg.in/asn1-ber.v1" 11 | ) 12 | 13 | // SimpleBindRequest represents a username/password bind operation 14 | type SimpleBindRequest struct { 15 | // Username is the name of the Directory object that the client wishes to bind as 16 | Username string 17 | // Password is the credentials to bind with 18 | Password string 19 | // Controls are optional controls to send with the bind request 20 | Controls []Control 21 | } 22 | 23 | // SimpleBindResult contains the response from the server 24 | type SimpleBindResult struct { 25 | Controls []Control 26 | } 27 | 28 | // NewSimpleBindRequest returns a bind request 29 | func NewSimpleBindRequest(username string, password string, controls []Control) *SimpleBindRequest { 30 | return &SimpleBindRequest{ 31 | Username: username, 32 | Password: password, 33 | Controls: controls, 34 | } 35 | } 36 | 37 | func (bindRequest *SimpleBindRequest) encode() *ber.Packet { 38 | request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request") 39 | request.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version")) 40 | request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, bindRequest.Username, "User Name")) 41 | request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, bindRequest.Password, "Password")) 42 | 43 | request.AppendChild(encodeControls(bindRequest.Controls)) 44 | 45 | return request 46 | } 47 | 48 | // SimpleBind performs the simple bind operation defined in the given request 49 | func (l *Conn) SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error) { 50 | packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") 51 | packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) 52 | encodedBindRequest := simpleBindRequest.encode() 53 | packet.AppendChild(encodedBindRequest) 54 | 55 | if l.Debug { 56 | ber.PrintPacket(packet) 57 | } 58 | 59 | msgCtx, err := l.sendMessage(packet) 60 | if err != nil { 61 | return nil, err 62 | } 63 | defer l.finishMessage(msgCtx) 64 | 65 | packetResponse, ok := <-msgCtx.responses 66 | if !ok { 67 | return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed")) 68 | } 69 | packet, err = packetResponse.ReadPacket() 70 | l.Debug.Printf("%d: got response %p", msgCtx.id, packet) 71 | if err != nil { 72 | return nil, err 73 | } 74 | 75 | if l.Debug { 76 | if err := addLDAPDescriptions(packet); err != nil { 77 | return nil, err 78 | } 79 | ber.PrintPacket(packet) 80 | } 81 | 82 | result := &SimpleBindResult{ 83 | Controls: make([]Control, 0), 84 | } 85 | 86 | if len(packet.Children) == 3 { 87 | for _, child := range packet.Children[2].Children { 88 | result.Controls = append(result.Controls, DecodeControl(child)) 89 | } 90 | } 91 | 92 | resultCode, resultDescription := getLDAPResultCode(packet) 93 | if resultCode != 0 { 94 | return result, NewError(resultCode, errors.New(resultDescription)) 95 | } 96 | 97 | return result, nil 98 | } 99 | 100 | // Bind performs a bind with the given username and password 101 | func (l *Conn) Bind(username, password string) error { 102 | packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") 103 | packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) 104 | bindRequest := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationBindRequest, nil, "Bind Request") 105 | bindRequest.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 3, "Version")) 106 | bindRequest.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, username, "User Name")) 107 | bindRequest.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, password, "Password")) 108 | packet.AppendChild(bindRequest) 109 | 110 | if l.Debug { 111 | ber.PrintPacket(packet) 112 | } 113 | 114 | msgCtx, err := l.sendMessage(packet) 115 | if err != nil { 116 | return err 117 | } 118 | defer l.finishMessage(msgCtx) 119 | 120 | packetResponse, ok := <-msgCtx.responses 121 | if !ok { 122 | return NewError(ErrorNetwork, errors.New("ldap: response channel closed")) 123 | } 124 | packet, err = packetResponse.ReadPacket() 125 | l.Debug.Printf("%d: got response %p", msgCtx.id, packet) 126 | if err != nil { 127 | return err 128 | } 129 | 130 | if l.Debug { 131 | if err := addLDAPDescriptions(packet); err != nil { 132 | return err 133 | } 134 | ber.PrintPacket(packet) 135 | } 136 | 137 | resultCode, resultDescription := getLDAPResultCode(packet) 138 | if resultCode != 0 { 139 | return NewError(resultCode, errors.New(resultDescription)) 140 | } 141 | 142 | return nil 143 | } 144 | -------------------------------------------------------------------------------- /starttls/ldap/client.go: -------------------------------------------------------------------------------- 1 | package ldap 2 | 3 | import ( 4 | "crypto/tls" 5 | "time" 6 | ) 7 | 8 | // Client knows how to interact with an LDAP server 9 | type Client interface { 10 | Start() 11 | StartTLS(config *tls.Config) error 12 | Close() 13 | SetTimeout(time.Duration) 14 | 15 | Bind(username, password string) error 16 | SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error) 17 | 18 | Add(addRequest *AddRequest) error 19 | Del(delRequest *DelRequest) error 20 | Modify(modifyRequest *ModifyRequest) error 21 | 22 | Compare(dn, attribute, value string) (bool, error) 23 | PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error) 24 | 25 | Search(searchRequest *SearchRequest) (*SearchResult, error) 26 | SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error) 27 | } 28 | -------------------------------------------------------------------------------- /starttls/ldap/compare.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // 5 | // File contains Compare functionality 6 | // 7 | // https://tools.ietf.org/html/rfc4511 8 | // 9 | // CompareRequest ::= [APPLICATION 14] SEQUENCE { 10 | // entry LDAPDN, 11 | // ava AttributeValueAssertion } 12 | // 13 | // AttributeValueAssertion ::= SEQUENCE { 14 | // attributeDesc AttributeDescription, 15 | // assertionValue AssertionValue } 16 | // 17 | // AttributeDescription ::= LDAPString 18 | // -- Constrained to 19 | // -- [RFC4512] 20 | // 21 | // AttributeValue ::= OCTET STRING 22 | // 23 | 24 | package ldap 25 | 26 | import ( 27 | "errors" 28 | "fmt" 29 | 30 | "gopkg.in/asn1-ber.v1" 31 | ) 32 | 33 | // Compare checks to see if the attribute of the dn matches value. Returns true if it does otherwise 34 | // false with any error that occurs if any. 35 | func (l *Conn) Compare(dn, attribute, value string) (bool, error) { 36 | packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") 37 | packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) 38 | 39 | request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationCompareRequest, nil, "Compare Request") 40 | request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, dn, "DN")) 41 | 42 | ava := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "AttributeValueAssertion") 43 | ava.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "AttributeDesc")) 44 | ava.AppendChild(ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagOctetString, value, "AssertionValue")) 45 | request.AppendChild(ava) 46 | packet.AppendChild(request) 47 | 48 | l.Debug.PrintPacket(packet) 49 | 50 | msgCtx, err := l.sendMessage(packet) 51 | if err != nil { 52 | return false, err 53 | } 54 | defer l.finishMessage(msgCtx) 55 | 56 | l.Debug.Printf("%d: waiting for response", msgCtx.id) 57 | packetResponse, ok := <-msgCtx.responses 58 | if !ok { 59 | return false, NewError(ErrorNetwork, errors.New("ldap: response channel closed")) 60 | } 61 | packet, err = packetResponse.ReadPacket() 62 | l.Debug.Printf("%d: got response %p", msgCtx.id, packet) 63 | if err != nil { 64 | return false, err 65 | } 66 | 67 | if l.Debug { 68 | if err := addLDAPDescriptions(packet); err != nil { 69 | return false, err 70 | } 71 | ber.PrintPacket(packet) 72 | } 73 | 74 | if packet.Children[1].Tag == ApplicationCompareResponse { 75 | resultCode, resultDescription := getLDAPResultCode(packet) 76 | if resultCode == LDAPResultCompareTrue { 77 | return true, nil 78 | } else if resultCode == LDAPResultCompareFalse { 79 | return false, nil 80 | } else { 81 | return false, NewError(resultCode, errors.New(resultDescription)) 82 | } 83 | } 84 | return false, fmt.Errorf("Unexpected Response: %d", packet.Children[1].Tag) 85 | } 86 | -------------------------------------------------------------------------------- /starttls/ldap/debug.go: -------------------------------------------------------------------------------- 1 | package ldap 2 | 3 | import ( 4 | "log" 5 | 6 | "gopkg.in/asn1-ber.v1" 7 | ) 8 | 9 | // debbuging type 10 | // - has a Printf method to write the debug output 11 | type debugging bool 12 | 13 | // Printf writes debug output 14 | func (debug debugging) Printf(format string, args ...interface{}) { 15 | if debug { 16 | log.Printf(format, args...) 17 | } 18 | } 19 | 20 | func (debug debugging) PrintPacket(packet *ber.Packet) { 21 | if debug { 22 | ber.PrintPacket(packet) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /starttls/ldap/del.go: -------------------------------------------------------------------------------- 1 | // 2 | // https://tools.ietf.org/html/rfc4511 3 | // 4 | // DelRequest ::= [APPLICATION 10] LDAPDN 5 | 6 | package ldap 7 | 8 | import ( 9 | "errors" 10 | "log" 11 | 12 | "gopkg.in/asn1-ber.v1" 13 | ) 14 | 15 | // DelRequest implements an LDAP deletion request 16 | type DelRequest struct { 17 | // DN is the name of the directory entry to delete 18 | DN string 19 | // Controls hold optional controls to send with the request 20 | Controls []Control 21 | } 22 | 23 | func (d DelRequest) encode() *ber.Packet { 24 | request := ber.Encode(ber.ClassApplication, ber.TypePrimitive, ApplicationDelRequest, d.DN, "Del Request") 25 | request.Data.Write([]byte(d.DN)) 26 | return request 27 | } 28 | 29 | // NewDelRequest creates a delete request for the given DN and controls 30 | func NewDelRequest(DN string, 31 | Controls []Control) *DelRequest { 32 | return &DelRequest{ 33 | DN: DN, 34 | Controls: Controls, 35 | } 36 | } 37 | 38 | // Del executes the given delete request 39 | func (l *Conn) Del(delRequest *DelRequest) error { 40 | packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") 41 | packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) 42 | packet.AppendChild(delRequest.encode()) 43 | if delRequest.Controls != nil { 44 | packet.AppendChild(encodeControls(delRequest.Controls)) 45 | } 46 | 47 | l.Debug.PrintPacket(packet) 48 | 49 | msgCtx, err := l.sendMessage(packet) 50 | if err != nil { 51 | return err 52 | } 53 | defer l.finishMessage(msgCtx) 54 | 55 | l.Debug.Printf("%d: waiting for response", msgCtx.id) 56 | packetResponse, ok := <-msgCtx.responses 57 | if !ok { 58 | return NewError(ErrorNetwork, errors.New("ldap: response channel closed")) 59 | } 60 | packet, err = packetResponse.ReadPacket() 61 | l.Debug.Printf("%d: got response %p", msgCtx.id, packet) 62 | if err != nil { 63 | return err 64 | } 65 | 66 | if l.Debug { 67 | if err := addLDAPDescriptions(packet); err != nil { 68 | return err 69 | } 70 | ber.PrintPacket(packet) 71 | } 72 | 73 | if packet.Children[1].Tag == ApplicationDelResponse { 74 | resultCode, resultDescription := getLDAPResultCode(packet) 75 | if resultCode != 0 { 76 | return NewError(resultCode, errors.New(resultDescription)) 77 | } 78 | } else { 79 | log.Printf("Unexpected Response: %d", packet.Children[1].Tag) 80 | } 81 | 82 | l.Debug.Printf("%d: returning", msgCtx.id) 83 | return nil 84 | } 85 | -------------------------------------------------------------------------------- /starttls/ldap/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package ldap provides basic LDAP v3 functionality. 3 | */ 4 | package ldap 5 | -------------------------------------------------------------------------------- /starttls/ldap/error_test.go: -------------------------------------------------------------------------------- 1 | package ldap 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | "strings" 7 | "testing" 8 | "time" 9 | 10 | "gopkg.in/asn1-ber.v1" 11 | ) 12 | 13 | // TestNilPacket tests that nil packets don't cause a panic. 14 | func TestNilPacket(t *testing.T) { 15 | // Test for nil packet 16 | code, _ := getLDAPResultCode(nil) 17 | if code != ErrorUnexpectedResponse { 18 | t.Errorf("Should have an 'ErrorUnexpectedResponse' error in nil packets, got: %v", code) 19 | } 20 | 21 | // Test for nil result 22 | kids := []*ber.Packet{ 23 | {}, // Unused 24 | nil, // Can't be nil 25 | } 26 | pack := &ber.Packet{Children: kids} 27 | code, _ = getLDAPResultCode(pack) 28 | 29 | if code != ErrorUnexpectedResponse { 30 | t.Errorf("Should have an 'ErrorUnexpectedResponse' error in nil packets, got: %v", code) 31 | } 32 | } 33 | 34 | // TestConnReadErr tests that an unexpected error reading from underlying 35 | // connection bubbles up to the goroutine which makes a request. 36 | func TestConnReadErr(t *testing.T) { 37 | conn := &signalErrConn{ 38 | signals: make(chan error), 39 | } 40 | 41 | ldapConn := NewConn(conn, false) 42 | ldapConn.Start() 43 | 44 | // Make a dummy search request. 45 | searchReq := NewSearchRequest("dc=example,dc=com", ScopeWholeSubtree, DerefAlways, 0, 0, false, "(objectClass=*)", nil, nil) 46 | 47 | expectedError := errors.New("this is the error you are looking for") 48 | 49 | // Send the signal after a short amount of time. 50 | time.AfterFunc(10*time.Millisecond, func() { conn.signals <- expectedError }) 51 | 52 | // This should block until the underlyiny conn gets the error signal 53 | // which should bubble up through the reader() goroutine, close the 54 | // connection, and 55 | _, err := ldapConn.Search(searchReq) 56 | if err == nil || !strings.Contains(err.Error(), expectedError.Error()) { 57 | t.Errorf("not the expected error: %s", err) 58 | } 59 | } 60 | 61 | // signalErrConn is a helful type used with TestConnReadErr. It implements the 62 | // net.Conn interface to be used as a connection for the test. Most methods are 63 | // no-ops but the Read() method blocks until it receives a signal which it 64 | // returns as an error. 65 | type signalErrConn struct { 66 | signals chan error 67 | } 68 | 69 | // Read blocks until an error is sent on the internal signals channel. That 70 | // error is returned. 71 | func (c *signalErrConn) Read(b []byte) (n int, err error) { 72 | return 0, <-c.signals 73 | } 74 | 75 | func (c *signalErrConn) Write(b []byte) (n int, err error) { 76 | return len(b), nil 77 | } 78 | 79 | func (c *signalErrConn) Close() error { 80 | close(c.signals) 81 | return nil 82 | } 83 | 84 | func (c *signalErrConn) LocalAddr() net.Addr { 85 | return (*net.TCPAddr)(nil) 86 | } 87 | 88 | func (c *signalErrConn) RemoteAddr() net.Addr { 89 | return (*net.TCPAddr)(nil) 90 | } 91 | 92 | func (c *signalErrConn) SetDeadline(t time.Time) error { 93 | return nil 94 | } 95 | 96 | func (c *signalErrConn) SetReadDeadline(t time.Time) error { 97 | return nil 98 | } 99 | 100 | func (c *signalErrConn) SetWriteDeadline(t time.Time) error { 101 | return nil 102 | } 103 | -------------------------------------------------------------------------------- /starttls/ldap/passwdmodify.go: -------------------------------------------------------------------------------- 1 | // This file contains the password modify extended operation as specified in rfc 3062 2 | // 3 | // https://tools.ietf.org/html/rfc3062 4 | // 5 | 6 | package ldap 7 | 8 | import ( 9 | "errors" 10 | "fmt" 11 | 12 | "gopkg.in/asn1-ber.v1" 13 | ) 14 | 15 | const ( 16 | passwordModifyOID = "1.3.6.1.4.1.4203.1.11.1" 17 | ) 18 | 19 | // PasswordModifyRequest implements the Password Modify Extended Operation as defined in https://www.ietf.org/rfc/rfc3062.txt 20 | type PasswordModifyRequest struct { 21 | // UserIdentity is an optional string representation of the user associated with the request. 22 | // This string may or may not be an LDAPDN [RFC2253]. 23 | // If no UserIdentity field is present, the request acts up upon the password of the user currently associated with the LDAP session 24 | UserIdentity string 25 | // OldPassword, if present, contains the user's current password 26 | OldPassword string 27 | // NewPassword, if present, contains the desired password for this user 28 | NewPassword string 29 | } 30 | 31 | // PasswordModifyResult holds the server response to a PasswordModifyRequest 32 | type PasswordModifyResult struct { 33 | // GeneratedPassword holds a password generated by the server, if present 34 | GeneratedPassword string 35 | } 36 | 37 | func (r *PasswordModifyRequest) encode() (*ber.Packet, error) { 38 | request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Password Modify Extended Operation") 39 | request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, passwordModifyOID, "Extended Request Name: Password Modify OID")) 40 | extendedRequestValue := ber.Encode(ber.ClassContext, ber.TypePrimitive, 1, nil, "Extended Request Value: Password Modify Request") 41 | passwordModifyRequestValue := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Password Modify Request") 42 | if r.UserIdentity != "" { 43 | passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, r.UserIdentity, "User Identity")) 44 | } 45 | if r.OldPassword != "" { 46 | passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 1, r.OldPassword, "Old Password")) 47 | } 48 | if r.NewPassword != "" { 49 | passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 2, r.NewPassword, "New Password")) 50 | } 51 | 52 | extendedRequestValue.AppendChild(passwordModifyRequestValue) 53 | request.AppendChild(extendedRequestValue) 54 | 55 | return request, nil 56 | } 57 | 58 | // NewPasswordModifyRequest creates a new PasswordModifyRequest 59 | // 60 | // According to the RFC 3602: 61 | // userIdentity is a string representing the user associated with the request. 62 | // This string may or may not be an LDAPDN (RFC 2253). 63 | // If userIdentity is empty then the operation will act on the user associated 64 | // with the session. 65 | // 66 | // oldPassword is the current user's password, it can be empty or it can be 67 | // needed depending on the session user access rights (usually an administrator 68 | // can change a user's password without knowing the current one) and the 69 | // password policy (see pwdSafeModify password policy's attribute) 70 | // 71 | // newPassword is the desired user's password. If empty the server can return 72 | // an error or generate a new password that will be available in the 73 | // PasswordModifyResult.GeneratedPassword 74 | // 75 | func NewPasswordModifyRequest(userIdentity string, oldPassword string, newPassword string) *PasswordModifyRequest { 76 | return &PasswordModifyRequest{ 77 | UserIdentity: userIdentity, 78 | OldPassword: oldPassword, 79 | NewPassword: newPassword, 80 | } 81 | } 82 | 83 | // PasswordModify performs the modification request 84 | func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error) { 85 | packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") 86 | packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) 87 | 88 | encodedPasswordModifyRequest, err := passwordModifyRequest.encode() 89 | if err != nil { 90 | return nil, err 91 | } 92 | packet.AppendChild(encodedPasswordModifyRequest) 93 | 94 | l.Debug.PrintPacket(packet) 95 | 96 | msgCtx, err := l.sendMessage(packet) 97 | if err != nil { 98 | return nil, err 99 | } 100 | defer l.finishMessage(msgCtx) 101 | 102 | result := &PasswordModifyResult{} 103 | 104 | l.Debug.Printf("%d: waiting for response", msgCtx.id) 105 | packetResponse, ok := <-msgCtx.responses 106 | if !ok { 107 | return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed")) 108 | } 109 | packet, err = packetResponse.ReadPacket() 110 | l.Debug.Printf("%d: got response %p", msgCtx.id, packet) 111 | if err != nil { 112 | return nil, err 113 | } 114 | 115 | if packet == nil { 116 | return nil, NewError(ErrorNetwork, errors.New("ldap: could not retrieve message")) 117 | } 118 | 119 | if l.Debug { 120 | if err := addLDAPDescriptions(packet); err != nil { 121 | return nil, err 122 | } 123 | ber.PrintPacket(packet) 124 | } 125 | 126 | if packet.Children[1].Tag == ApplicationExtendedResponse { 127 | resultCode, resultDescription := getLDAPResultCode(packet) 128 | if resultCode != 0 { 129 | return nil, NewError(resultCode, errors.New(resultDescription)) 130 | } 131 | } else { 132 | return nil, NewError(ErrorUnexpectedResponse, fmt.Errorf("Unexpected Response: %d", packet.Children[1].Tag)) 133 | } 134 | 135 | extendedResponse := packet.Children[1] 136 | for _, child := range extendedResponse.Children { 137 | if child.Tag == 11 { 138 | passwordModifyReponseValue := ber.DecodePacket(child.Data.Bytes()) 139 | if len(passwordModifyReponseValue.Children) == 1 { 140 | if passwordModifyReponseValue.Children[0].Tag == 0 { 141 | result.GeneratedPassword = ber.DecodeString(passwordModifyReponseValue.Children[0].Data.Bytes()) 142 | } 143 | } 144 | } 145 | } 146 | 147 | return result, nil 148 | } 149 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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) func(net.Conn) net.Conn { 15 | verifyCaOnly := false 16 | tlsConf := tls.Config{} 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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_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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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-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-bad-serial.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-bad-serial.jceks -------------------------------------------------------------------------------- /test-certs/example-bad-serial.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-bad-serial.p12 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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-custom-oid.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-custom-oid.jceks -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test-certs/example-custom-oid.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-custom-oid.p12 -------------------------------------------------------------------------------- /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-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-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 | -------------------------------------------------------------------------------- /test-certs/example-elliptic-sha1.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-elliptic-sha1.jceks -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test-certs/example-elliptic-sha1.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-elliptic-sha1.p12 -------------------------------------------------------------------------------- /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-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 | -------------------------------------------------------------------------------- /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-expired.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-expired.jceks -------------------------------------------------------------------------------- /test-certs/example-expired.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-expired.p12 -------------------------------------------------------------------------------- /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-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-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-leaf.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-leaf.jceks -------------------------------------------------------------------------------- /test-certs/example-leaf.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-leaf.p12 -------------------------------------------------------------------------------- /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.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-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-md5.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-md5.jceks -------------------------------------------------------------------------------- /test-certs/example-md5.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-md5.p12 -------------------------------------------------------------------------------- /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-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.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 | -------------------------------------------------------------------------------- /test-certs/example-name-constraints.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-name-constraints.jceks -------------------------------------------------------------------------------- /test-certs/example-name-constraints.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-name-constraints.p12 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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-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-root-bad-ku.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-root-bad-ku.jceks -------------------------------------------------------------------------------- /test-certs/example-root-bad-ku.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-root-bad-ku.p12 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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-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-root.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-root.jceks -------------------------------------------------------------------------------- /test-certs/example-root.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-root.p12 -------------------------------------------------------------------------------- /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-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-sha1.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-sha1.jceks -------------------------------------------------------------------------------- /test-certs/example-sha1.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-sha1.p12 -------------------------------------------------------------------------------- /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-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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test-certs/example-small-key.jceks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-small-key.jceks -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test-certs/example-small-key.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/square/certigo/b78a846ab962134ea5c48b746e0e99199d55da28/test-certs/example-small-key.p12 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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":["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 | -------------------------------------------------------------------------------- /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 | \tWARN: [RFC5280] Sub certificates SHOULD include Subject Key Identifier in end entity certs (esc) 43 | 44 | -------------------------------------------------------------------------------- /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 | \tWARN: [RFC5280] Sub certificates SHOULD include Subject Key Identifier in end entity certs (esc) 56 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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":["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 | -------------------------------------------------------------------------------- /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 | \tWARN: [RFC5280] Sub certificates SHOULD include Subject Key Identifier in end entity certs (esc) 42 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------