| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- /*
- Copyright © The ESO Authors
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- https://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package template
- import (
- "os"
- "slices"
- "testing"
- )
- const (
- certData = `-----BEGIN CERTIFICATE-----
- MIIDHTCCAgWgAwIBAgIRAKC4yxy9QGocND+6avTf7BgwDQYJKoZIhvcNAQELBQAw
- EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0yMTAzMjAyMDA4MDhaFw0yMTAzMjAyMDM4
- MDhaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
- ggEKAoIBAQC3o6/JdZEqNbqNRkopHhJtJG5c4qS5d0tQ/kZYpfD/v/izAYum4Nzj
- aG15owr92/11W0pxPUliRLti3y6iScTs+ofm2D7p4UXj/Fnho/2xoWSOoWAodgvW
- Y8jh8A0LQALZiV/9QsrJdXZdS47DYZLsQ3z9yFC/CdXkg1l7AQ3fIVGKdrQBr9kE
- 1gEDqnKfRxXI8DEQKXr+CKPUwCAytegmy0SHp53zNAvY+kopHytzmJpXLoEhxq4e
- ugHe52vXHdh/HJ9VjNp0xOH1waAgAGxHlltCW0PVd5AJ0SXROBS/a3V9sZCbCrJa
- YOOonQSEswveSv6PcG9AHvpNPot2Xs6hAgMBAAGjbjBsMA4GA1UdDwEB/wQEAwIC
- pDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
- BBR00805mrpoonp95RmC3B6oLl+cGTAVBgNVHREEDjAMggpnb29ibGUuY29tMA0G
- CSqGSIb3DQEBCwUAA4IBAQAipc1b6JrEDayPjpz5GM5krcI8dCWVd8re0a9bGjjN
- ioWGlu/eTr5El0ffwCNZ2WLmL9rewfHf/bMvYz3ioFZJ2OTxfazqYXNggQz6cMfa
- lbedDCdt5XLVX2TyerGvFram+9Uyvk3l0uM7rZnwAmdirG4Tv94QRaD3q4xTj/c0
- mv+AggtK0aRFb9o47z/BypLdk5mhbf3Mmr88C8XBzEnfdYyf4JpTlZrYLBmDCu5d
- 9RLLsjXxhag8xqMtd1uLUM8XOTGzVWacw8iGY+CTtBKqyA+AE6/bDwZvEwVtsKtC
- QJ85ioEpy00NioqcF0WyMZH80uMsPycfpnl5uF7RkW8u
- -----END CERTIFICATE-----
- `
- otherCert = `-----BEGIN CERTIFICATE-----
- MIIBqjCCAU+gAwIBAgIRAPnGGsBUMbZhmh5QdnYdBmUwCgYIKoZIzj0EAwIwGjEY
- MBYGA1UEAxMPaW50ZXJtZWRpYXRlLWNhMB4XDTIyMDIwOTEwMjUzMVoXDTIyMDIx
- MDEwMjUzMVowDjEMMAoGA1UEAxMDZm9vMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
- QgAEqnxdeInykx8JZsLi13rZLekoG2cosQ3F+2InVNy7hCQ7soMqdaJsGQ6LFtov
- ogUFtOOTRWrunblqNWGZsowHbKOBgTB/MA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUE
- FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFLtundVbuKd73OWzo6SY
- by0Ajeb2MB8GA1UdIwQYMBaAFCLg80J/bZBbOd+Y8+V94l5xM2zEMA4GA1UdEQQH
- MAWCA2ZvbzAKBggqhkjOPQQDAgNJADBGAiEA4K4SbVNqrEtl7RfwBfJFMnWI+X8D
- zMPMc4Xqzp2qTxcCIQDsySgtiakypZfWakpB49zJph0kLwGK8xhWvGMUw1N1/w==
- -----END CERTIFICATE-----
- `
- keyData = `-----BEGIN PRIVATE KEY-----
- MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC3o6/JdZEqNbqN
- RkopHhJtJG5c4qS5d0tQ/kZYpfD/v/izAYum4NzjaG15owr92/11W0pxPUliRLti
- 3y6iScTs+ofm2D7p4UXj/Fnho/2xoWSOoWAodgvWY8jh8A0LQALZiV/9QsrJdXZd
- S47DYZLsQ3z9yFC/CdXkg1l7AQ3fIVGKdrQBr9kE1gEDqnKfRxXI8DEQKXr+CKPU
- wCAytegmy0SHp53zNAvY+kopHytzmJpXLoEhxq4eugHe52vXHdh/HJ9VjNp0xOH1
- waAgAGxHlltCW0PVd5AJ0SXROBS/a3V9sZCbCrJaYOOonQSEswveSv6PcG9AHvpN
- Pot2Xs6hAgMBAAECggEACTGPrmVNZDCWa1Y2hkJ0J7SoNcw+9O4M/jwMp4l/PD6P
- I98S78LYLCZhPLK17SmjUcnFO1AXKW1JeFS2D/fjfP256guvcqQNjLFoioxcOhVb
- ZGyd1Mi8JPqP5wfOj16gBeYDwTkjz9wqldcfiZaL9XoXetkZecbzR2JwC2FtIVuC
- 0njTjMNYpaBKnoLb8OTR0EQz7lYEo2MkQiWryz8wseONnFmdfh18p+p10YgCbuCH
- qesrWfDLLxaxZelNtDhDngg9LoCLmarYy7BgShacmUEgJTZ/x3xFC75thK3ln0OY
- +ktTgvVotYYaZi7qAjQiEsTvkTAPg5RMpQLd2UIWsQKBgQDCBp+1vURbwGzmTNUg
- HMipD6WDFdLc9DCacx6+ZqsEPTMWQbCpVZrDKiY0Rjt5F+xOCyMr00J5RDJXRC0G
- +L7NcJdywOFutT7vB+cmETg7l/6PHweNYBnE66706eTL/KVYZMi4tEinarPWhHmL
- jasfdLANtpDjdWkRt299TkPRbQKBgQDyS8Rr7KZdv04Csqkf+ASmiJpT5R6Y72kc
- 3XYpKETyB2FyPZkuh/zInMut9SkkSI9O/jA3zf956jj6sF1DHvp7T8KkIp5OAQeD
- J9AF65m2MnZfHFUeJ6ZQsggwMWqrD0ycIWP7YWtiBHH+D1wGkjYrssq+bvG/yNpA
- LtqdKq9lhQKBgQCZA2hIhy61vRckuEsLvCdzTGeW7UsR/XGnHEqOlaEhArKbRsrv
- gBdA+qiOaSTV5svw8E+YbE7sG6AnuhhYeyreEYEeeoZOLJmpIG5mUwYp2UBj1nC6
- SaOI7OVZOGu7g09SWokBQQxbG4cgEfFY4Sym7fs5lVTGTP3Dfwppo6NQMQKBgQCo
- J5NDP3Lafwk58BpV+H/pv8YzUUDh7M2rXbtCpxLqUdr8OOnVlEUISWFF8m5CIyVq
- MhjuscWLK9Wtjba7/YTjDaDM3sW05xv6lyfU5ATCoNTr/zLHgcb4HAZ4w+L+otiN
- RtMnxB2NYf5mzuwUF2cG/secUEzwyAlIH/xStSwTLQKBgQCRvqF+rqxnegoOgwVW
- qrWPv06wXD8dW2FlPpY5GXqA0l6erSK3YsQQToRmbem9ibPD7bd5P4gNbWfxwK4C
- Wt+1Rcb8OrDhDJbYz85bXBnPecKp4EN0b9SHO0/dsCqn2w30emc+9T/4m1ZDkpBd
- BixHvI/EJ8YK3ta5WdJWKC6hnA==
- -----END PRIVATE KEY-----
- `
- )
- const (
- filterPrivateKey = "private key"
- filterCert = "certificate"
- )
- func TestFilterPEM(t *testing.T) {
- type args struct {
- input string
- pemType string
- }
- tests := []struct {
- name string
- args args
- want string
- wantErr bool
- }{
- {
- name: "extract cert / cert first",
- args: args{
- input: certData + keyData,
- pemType: filterCert,
- },
- want: certData,
- },
- {
- name: "extract cert / key first",
- args: args{
- input: keyData + certData,
- pemType: filterCert,
- },
- want: certData,
- },
- {
- name: "extract multiple certs",
- args: args{
- input: keyData + certData + keyData + otherCert,
- pemType: filterCert,
- },
- want: certData + otherCert,
- },
- {
- name: "extract key",
- args: args{
- input: keyData + certData,
- pemType: filterPrivateKey,
- },
- want: keyData,
- },
- {
- name: "key with junk",
- args: args{
- input: certData + keyData + "some ---junk---",
- pemType: filterPrivateKey,
- },
- want: keyData,
- },
- {
- name: "begin/end with junk",
- args: args{
- // pem.Decode trims junk from the beginning of the input
- // so we are able to decode both cert & key
- input: "some junk" + certData + keyData + "some ---junk---",
- pemType: filterPrivateKey,
- },
- want: keyData,
- },
- {
- name: "interleaved junk",
- args: args{
- // can parse cert but not key due to junk
- input: certData + "some junk" + keyData,
- pemType: filterPrivateKey,
- },
- wantErr: true,
- },
- {
- name: "err when junk",
- args: args{
- input: "---junk---",
- pemType: filterPrivateKey,
- },
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := filterPEM(tt.args.pemType, tt.args.input)
- if (err != nil) != tt.wantErr {
- t.Errorf("filterPEM() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if got != tt.want {
- t.Errorf("filterPEM() = %v, want %v", got, tt.want)
- }
- })
- }
- }
- type filterCertChainTestArgs struct {
- input []string
- certType string
- }
- type filterCertChainTest struct {
- name string
- args filterCertChainTestArgs
- want string
- wantErr bool
- }
- func TestFilterCertChain(t *testing.T) {
- const (
- leafCertPath = "_testdata/foo.crt"
- intermediateCertPath = "_testdata/intermediate-ca.crt"
- rootCertPath = "_testdata/root-ca.crt"
- rootKeyPath = "_testdata/root-ca.key"
- )
- tests := []filterCertChainTest{
- {
- name: "extract leaf cert / empty cert chain",
- args: filterCertChainTestArgs{
- input: []string{},
- certType: certTypeLeaf,
- },
- wantErr: true,
- },
- {
- name: "extract leaf cert / cert chain with pkey",
- args: filterCertChainTestArgs{
- input: []string{
- leafCertPath,
- rootKeyPath,
- },
- certType: certTypeLeaf,
- },
- wantErr: true,
- },
- {
- name: "extract leaf cert / leaf cert only",
- args: filterCertChainTestArgs{
- input: []string{
- leafCertPath,
- },
- certType: certTypeLeaf,
- },
- want: leafCertPath,
- },
- {
- name: "extract leaf cert / cert chain without root",
- args: filterCertChainTestArgs{
- input: []string{
- leafCertPath,
- intermediateCertPath,
- },
- certType: certTypeLeaf,
- },
- want: leafCertPath,
- },
- {
- name: "extract leaf cert / root cert only",
- args: filterCertChainTestArgs{
- input: []string{
- rootCertPath,
- },
- certType: certTypeLeaf,
- },
- want: "",
- },
- {
- name: "extract leaf cert / full cert chain",
- args: filterCertChainTestArgs{
- input: []string{
- leafCertPath,
- intermediateCertPath,
- rootCertPath,
- },
- certType: certTypeLeaf,
- },
- want: leafCertPath,
- },
- {
- name: "extract intermediate cert / leaf cert only",
- args: filterCertChainTestArgs{
- input: []string{
- leafCertPath,
- },
- certType: certTypeIntermediate,
- },
- want: "",
- },
- {
- name: "extract intermediate cert / cert chain without root",
- args: filterCertChainTestArgs{
- input: []string{
- leafCertPath,
- intermediateCertPath,
- },
- certType: certTypeIntermediate,
- },
- want: intermediateCertPath,
- },
- {
- name: "extract intermediate cert / full cert chain",
- args: filterCertChainTestArgs{
- input: []string{
- leafCertPath,
- intermediateCertPath,
- rootCertPath,
- },
- certType: certTypeIntermediate,
- },
- want: intermediateCertPath,
- },
- {
- name: "extract root cert / leaf cert only",
- args: filterCertChainTestArgs{
- input: []string{
- leafCertPath,
- },
- certType: certTypeRoot,
- },
- want: "",
- },
- {
- name: "extract root cert / root cert only",
- args: filterCertChainTestArgs{
- input: []string{
- rootCertPath,
- },
- certType: certTypeRoot,
- },
- want: rootCertPath,
- },
- {
- name: "extract root cert / full cert chain",
- args: filterCertChainTestArgs{
- input: []string{
- leafCertPath,
- intermediateCertPath,
- rootCertPath,
- },
- certType: certTypeRoot,
- },
- want: rootCertPath,
- },
- }
- for _, tt := range tests {
- runFilterCertChainTest(t, tt)
- }
- }
- func runFilterCertChainTest(t *testing.T, tt filterCertChainTest) {
- t.Run(tt.name, func(t *testing.T) {
- chainIn, err := readCertificates(tt.args.input)
- if err != nil {
- t.Error(err)
- }
- var expOut []byte
- if tt.want != "" {
- var err error
- expOut, err = os.ReadFile(tt.want)
- if err != nil {
- t.Error(err)
- }
- }
- got, err := filterCertChain(tt.args.certType, string(chainIn))
- if (err != nil) != tt.wantErr {
- t.Errorf("filterCertChain() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if got != string(expOut) {
- t.Errorf("filterCertChain() = %v, want %v", got, string(expOut))
- }
- })
- }
- func readCertificates(certFiles []string) ([]byte, error) {
- var certificates []byte
- for _, f := range certFiles {
- c, err := os.ReadFile(f)
- if err != nil {
- return nil, err
- }
- certificates = append(certificates, c...)
- }
- return certificates, nil
- }
- func TestCertSANs(t *testing.T) {
- tests := []struct {
- name string
- input string
- want []string
- wantErr bool
- }{
- {
- name: "extract DNS SANs from cert",
- input: certData,
- want: []string{"gooble.com"},
- },
- {
- name: "invalid PEM input",
- input: "not a pem",
- wantErr: true,
- },
- {
- name: "empty input",
- input: "",
- wantErr: true,
- },
- {
- name: "cert with junk before PEM",
- input: "some junk\n" + certData,
- want: []string{"gooble.com"},
- },
- {
- name: "cert from file with all types of SANs",
- input: func() string {
- b, err := os.ReadFile("_testdata/sans.crt")
- if err != nil {
- panic("test setup failed: " + err.Error())
- }
- return string(b)
- }(),
- want: []string{"example.com", "www.example.com", "192.168.1.10", "10.0.0.1", "admin@example.com", "https://example.com"},
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := certSANs(tt.input)
- if (err != nil) != tt.wantErr {
- t.Errorf("certSANs() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !tt.wantErr && !slices.Equal(got, tt.want) {
- t.Errorf("certSANs() = %v, want %v", got, tt.want)
- }
- })
- }
- }
|