pem_test.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. /*
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package template
  13. import (
  14. "os"
  15. "testing"
  16. )
  17. const (
  18. certData = `-----BEGIN CERTIFICATE-----
  19. MIIDHTCCAgWgAwIBAgIRAKC4yxy9QGocND+6avTf7BgwDQYJKoZIhvcNAQELBQAw
  20. EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0yMTAzMjAyMDA4MDhaFw0yMTAzMjAyMDM4
  21. MDhaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
  22. ggEKAoIBAQC3o6/JdZEqNbqNRkopHhJtJG5c4qS5d0tQ/kZYpfD/v/izAYum4Nzj
  23. aG15owr92/11W0pxPUliRLti3y6iScTs+ofm2D7p4UXj/Fnho/2xoWSOoWAodgvW
  24. Y8jh8A0LQALZiV/9QsrJdXZdS47DYZLsQ3z9yFC/CdXkg1l7AQ3fIVGKdrQBr9kE
  25. 1gEDqnKfRxXI8DEQKXr+CKPUwCAytegmy0SHp53zNAvY+kopHytzmJpXLoEhxq4e
  26. ugHe52vXHdh/HJ9VjNp0xOH1waAgAGxHlltCW0PVd5AJ0SXROBS/a3V9sZCbCrJa
  27. YOOonQSEswveSv6PcG9AHvpNPot2Xs6hAgMBAAGjbjBsMA4GA1UdDwEB/wQEAwIC
  28. pDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
  29. BBR00805mrpoonp95RmC3B6oLl+cGTAVBgNVHREEDjAMggpnb29ibGUuY29tMA0G
  30. CSqGSIb3DQEBCwUAA4IBAQAipc1b6JrEDayPjpz5GM5krcI8dCWVd8re0a9bGjjN
  31. ioWGlu/eTr5El0ffwCNZ2WLmL9rewfHf/bMvYz3ioFZJ2OTxfazqYXNggQz6cMfa
  32. lbedDCdt5XLVX2TyerGvFram+9Uyvk3l0uM7rZnwAmdirG4Tv94QRaD3q4xTj/c0
  33. mv+AggtK0aRFb9o47z/BypLdk5mhbf3Mmr88C8XBzEnfdYyf4JpTlZrYLBmDCu5d
  34. 9RLLsjXxhag8xqMtd1uLUM8XOTGzVWacw8iGY+CTtBKqyA+AE6/bDwZvEwVtsKtC
  35. QJ85ioEpy00NioqcF0WyMZH80uMsPycfpnl5uF7RkW8u
  36. -----END CERTIFICATE-----
  37. `
  38. otherCert = `-----BEGIN CERTIFICATE-----
  39. MIIBqjCCAU+gAwIBAgIRAPnGGsBUMbZhmh5QdnYdBmUwCgYIKoZIzj0EAwIwGjEY
  40. MBYGA1UEAxMPaW50ZXJtZWRpYXRlLWNhMB4XDTIyMDIwOTEwMjUzMVoXDTIyMDIx
  41. MDEwMjUzMVowDjEMMAoGA1UEAxMDZm9vMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
  42. QgAEqnxdeInykx8JZsLi13rZLekoG2cosQ3F+2InVNy7hCQ7soMqdaJsGQ6LFtov
  43. ogUFtOOTRWrunblqNWGZsowHbKOBgTB/MA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUE
  44. FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFLtundVbuKd73OWzo6SY
  45. by0Ajeb2MB8GA1UdIwQYMBaAFCLg80J/bZBbOd+Y8+V94l5xM2zEMA4GA1UdEQQH
  46. MAWCA2ZvbzAKBggqhkjOPQQDAgNJADBGAiEA4K4SbVNqrEtl7RfwBfJFMnWI+X8D
  47. zMPMc4Xqzp2qTxcCIQDsySgtiakypZfWakpB49zJph0kLwGK8xhWvGMUw1N1/w==
  48. -----END CERTIFICATE-----
  49. `
  50. keyData = `-----BEGIN PRIVATE KEY-----
  51. MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC3o6/JdZEqNbqN
  52. RkopHhJtJG5c4qS5d0tQ/kZYpfD/v/izAYum4NzjaG15owr92/11W0pxPUliRLti
  53. 3y6iScTs+ofm2D7p4UXj/Fnho/2xoWSOoWAodgvWY8jh8A0LQALZiV/9QsrJdXZd
  54. S47DYZLsQ3z9yFC/CdXkg1l7AQ3fIVGKdrQBr9kE1gEDqnKfRxXI8DEQKXr+CKPU
  55. wCAytegmy0SHp53zNAvY+kopHytzmJpXLoEhxq4eugHe52vXHdh/HJ9VjNp0xOH1
  56. waAgAGxHlltCW0PVd5AJ0SXROBS/a3V9sZCbCrJaYOOonQSEswveSv6PcG9AHvpN
  57. Pot2Xs6hAgMBAAECggEACTGPrmVNZDCWa1Y2hkJ0J7SoNcw+9O4M/jwMp4l/PD6P
  58. I98S78LYLCZhPLK17SmjUcnFO1AXKW1JeFS2D/fjfP256guvcqQNjLFoioxcOhVb
  59. ZGyd1Mi8JPqP5wfOj16gBeYDwTkjz9wqldcfiZaL9XoXetkZecbzR2JwC2FtIVuC
  60. 0njTjMNYpaBKnoLb8OTR0EQz7lYEo2MkQiWryz8wseONnFmdfh18p+p10YgCbuCH
  61. qesrWfDLLxaxZelNtDhDngg9LoCLmarYy7BgShacmUEgJTZ/x3xFC75thK3ln0OY
  62. +ktTgvVotYYaZi7qAjQiEsTvkTAPg5RMpQLd2UIWsQKBgQDCBp+1vURbwGzmTNUg
  63. HMipD6WDFdLc9DCacx6+ZqsEPTMWQbCpVZrDKiY0Rjt5F+xOCyMr00J5RDJXRC0G
  64. +L7NcJdywOFutT7vB+cmETg7l/6PHweNYBnE66706eTL/KVYZMi4tEinarPWhHmL
  65. jasfdLANtpDjdWkRt299TkPRbQKBgQDyS8Rr7KZdv04Csqkf+ASmiJpT5R6Y72kc
  66. 3XYpKETyB2FyPZkuh/zInMut9SkkSI9O/jA3zf956jj6sF1DHvp7T8KkIp5OAQeD
  67. J9AF65m2MnZfHFUeJ6ZQsggwMWqrD0ycIWP7YWtiBHH+D1wGkjYrssq+bvG/yNpA
  68. LtqdKq9lhQKBgQCZA2hIhy61vRckuEsLvCdzTGeW7UsR/XGnHEqOlaEhArKbRsrv
  69. gBdA+qiOaSTV5svw8E+YbE7sG6AnuhhYeyreEYEeeoZOLJmpIG5mUwYp2UBj1nC6
  70. SaOI7OVZOGu7g09SWokBQQxbG4cgEfFY4Sym7fs5lVTGTP3Dfwppo6NQMQKBgQCo
  71. J5NDP3Lafwk58BpV+H/pv8YzUUDh7M2rXbtCpxLqUdr8OOnVlEUISWFF8m5CIyVq
  72. MhjuscWLK9Wtjba7/YTjDaDM3sW05xv6lyfU5ATCoNTr/zLHgcb4HAZ4w+L+otiN
  73. RtMnxB2NYf5mzuwUF2cG/secUEzwyAlIH/xStSwTLQKBgQCRvqF+rqxnegoOgwVW
  74. qrWPv06wXD8dW2FlPpY5GXqA0l6erSK3YsQQToRmbem9ibPD7bd5P4gNbWfxwK4C
  75. Wt+1Rcb8OrDhDJbYz85bXBnPecKp4EN0b9SHO0/dsCqn2w30emc+9T/4m1ZDkpBd
  76. BixHvI/EJ8YK3ta5WdJWKC6hnA==
  77. -----END PRIVATE KEY-----
  78. `
  79. )
  80. const (
  81. filterPrivateKey = "private key"
  82. filterCert = "certificate"
  83. )
  84. func TestFilterPEM(t *testing.T) {
  85. type args struct {
  86. input string
  87. pemType string
  88. }
  89. tests := []struct {
  90. name string
  91. args args
  92. want string
  93. wantErr bool
  94. }{
  95. {
  96. name: "extract cert / cert first",
  97. args: args{
  98. input: certData + keyData,
  99. pemType: filterCert,
  100. },
  101. want: certData,
  102. },
  103. {
  104. name: "extract cert / key first",
  105. args: args{
  106. input: keyData + certData,
  107. pemType: filterCert,
  108. },
  109. want: certData,
  110. },
  111. {
  112. name: "extract multiple certs",
  113. args: args{
  114. input: keyData + certData + keyData + otherCert,
  115. pemType: filterCert,
  116. },
  117. want: certData + otherCert,
  118. },
  119. {
  120. name: "extract key",
  121. args: args{
  122. input: keyData + certData,
  123. pemType: filterPrivateKey,
  124. },
  125. want: keyData,
  126. },
  127. {
  128. name: "key with junk",
  129. args: args{
  130. input: certData + keyData + "some ---junk---",
  131. pemType: filterPrivateKey,
  132. },
  133. want: keyData,
  134. },
  135. {
  136. name: "begin/end with junk",
  137. args: args{
  138. // pem.Decode trims junk from the beginning of the input
  139. // so we are able to decode both cert & key
  140. input: "some junk" + certData + keyData + "some ---junk---",
  141. pemType: filterPrivateKey,
  142. },
  143. want: keyData,
  144. },
  145. {
  146. name: "interleaved junk",
  147. args: args{
  148. // can parse cert but not key due to junk
  149. input: certData + "some junk" + keyData,
  150. pemType: filterPrivateKey,
  151. },
  152. wantErr: true,
  153. },
  154. {
  155. name: "err when junk",
  156. args: args{
  157. input: "---junk---",
  158. pemType: filterPrivateKey,
  159. },
  160. wantErr: true,
  161. },
  162. }
  163. for _, tt := range tests {
  164. t.Run(tt.name, func(t *testing.T) {
  165. got, err := filterPEM(tt.args.pemType, tt.args.input)
  166. if (err != nil) != tt.wantErr {
  167. t.Errorf("filterPEM() error = %v, wantErr %v", err, tt.wantErr)
  168. return
  169. }
  170. if got != tt.want {
  171. t.Errorf("filterPEM() = %v, want %v", got, tt.want)
  172. }
  173. })
  174. }
  175. }
  176. type filterCertChainTestArgs struct {
  177. input []string
  178. certType string
  179. }
  180. type filterCertChainTest struct {
  181. name string
  182. args filterCertChainTestArgs
  183. want string
  184. wantErr bool
  185. }
  186. func TestFilterCertChain(t *testing.T) {
  187. const (
  188. leafCertPath = "_testdata/foo.crt"
  189. intermediateCertPath = "_testdata/intermediate-ca.crt"
  190. rootCertPath = "_testdata/root-ca.crt"
  191. rootKeyPath = "_testdata/root-ca.key"
  192. )
  193. tests := []filterCertChainTest{
  194. {
  195. name: "extract leaf cert / empty cert chain",
  196. args: filterCertChainTestArgs{
  197. input: []string{},
  198. certType: certTypeLeaf,
  199. },
  200. wantErr: true,
  201. },
  202. {
  203. name: "extract leaf cert / cert chain with pkey",
  204. args: filterCertChainTestArgs{
  205. input: []string{
  206. leafCertPath,
  207. rootKeyPath,
  208. },
  209. certType: certTypeLeaf,
  210. },
  211. wantErr: true,
  212. },
  213. {
  214. name: "extract leaf cert / leaf cert only",
  215. args: filterCertChainTestArgs{
  216. input: []string{
  217. leafCertPath,
  218. },
  219. certType: certTypeLeaf,
  220. },
  221. want: leafCertPath,
  222. },
  223. {
  224. name: "extract leaf cert / cert chain without root",
  225. args: filterCertChainTestArgs{
  226. input: []string{
  227. leafCertPath,
  228. intermediateCertPath,
  229. },
  230. certType: certTypeLeaf,
  231. },
  232. want: leafCertPath,
  233. },
  234. {
  235. name: "extract leaf cert / root cert only",
  236. args: filterCertChainTestArgs{
  237. input: []string{
  238. rootCertPath,
  239. },
  240. certType: certTypeLeaf,
  241. },
  242. want: "",
  243. },
  244. {
  245. name: "extract leaf cert / full cert chain",
  246. args: filterCertChainTestArgs{
  247. input: []string{
  248. leafCertPath,
  249. intermediateCertPath,
  250. rootCertPath,
  251. },
  252. certType: certTypeLeaf,
  253. },
  254. want: leafCertPath,
  255. },
  256. {
  257. name: "extract intermediate cert / leaf cert only",
  258. args: filterCertChainTestArgs{
  259. input: []string{
  260. leafCertPath,
  261. },
  262. certType: certTypeIntermediate,
  263. },
  264. want: "",
  265. },
  266. {
  267. name: "extract intermediate cert / cert chain without root",
  268. args: filterCertChainTestArgs{
  269. input: []string{
  270. leafCertPath,
  271. intermediateCertPath,
  272. },
  273. certType: certTypeIntermediate,
  274. },
  275. want: intermediateCertPath,
  276. },
  277. {
  278. name: "extract intermediate cert / full cert chain",
  279. args: filterCertChainTestArgs{
  280. input: []string{
  281. leafCertPath,
  282. intermediateCertPath,
  283. rootCertPath,
  284. },
  285. certType: certTypeIntermediate,
  286. },
  287. want: intermediateCertPath,
  288. },
  289. {
  290. name: "extract root cert / leaf cert only",
  291. args: filterCertChainTestArgs{
  292. input: []string{
  293. leafCertPath,
  294. },
  295. certType: certTypeRoot,
  296. },
  297. want: "",
  298. },
  299. {
  300. name: "extract root cert / root cert only",
  301. args: filterCertChainTestArgs{
  302. input: []string{
  303. rootCertPath,
  304. },
  305. certType: certTypeRoot,
  306. },
  307. want: rootCertPath,
  308. },
  309. {
  310. name: "extract root cert / full cert chain",
  311. args: filterCertChainTestArgs{
  312. input: []string{
  313. leafCertPath,
  314. intermediateCertPath,
  315. rootCertPath,
  316. },
  317. certType: certTypeRoot,
  318. },
  319. want: rootCertPath,
  320. },
  321. }
  322. for _, tt := range tests {
  323. runFilterCertChainTest(t, tt)
  324. }
  325. }
  326. func runFilterCertChainTest(t *testing.T, tt filterCertChainTest) {
  327. t.Run(tt.name, func(t *testing.T) {
  328. chainIn, err := readCertificates(tt.args.input)
  329. if err != nil {
  330. t.Error(err)
  331. }
  332. var expOut []byte
  333. if tt.want != "" {
  334. var err error
  335. expOut, err = os.ReadFile(tt.want)
  336. if err != nil {
  337. t.Error(err)
  338. }
  339. }
  340. got, err := filterCertChain(tt.args.certType, string(chainIn))
  341. if (err != nil) != tt.wantErr {
  342. t.Errorf("filterCertChain() error = %v, wantErr %v", err, tt.wantErr)
  343. return
  344. }
  345. if got != string(expOut) {
  346. t.Errorf("filterCertChain() = %v, want %v", got, string(expOut))
  347. }
  348. })
  349. }
  350. func readCertificates(certFiles []string) ([]byte, error) {
  351. var certificates []byte
  352. for _, f := range certFiles {
  353. c, err := os.ReadFile(f)
  354. if err != nil {
  355. return nil, err
  356. }
  357. certificates = append(certificates, c...)
  358. }
  359. return certificates, nil
  360. }