initca_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. package initca
  2. import (
  3. "bytes"
  4. "crypto/ecdsa"
  5. "crypto/rsa"
  6. "os"
  7. "strings"
  8. "testing"
  9. "time"
  10. "github.com/cloudflare/cfssl/config"
  11. "github.com/cloudflare/cfssl/csr"
  12. "github.com/cloudflare/cfssl/helpers"
  13. "github.com/cloudflare/cfssl/signer"
  14. "github.com/cloudflare/cfssl/signer/local"
  15. )
  16. var validKeyParams = []csr.KeyRequest{
  17. {A: "rsa", S: 2048},
  18. {A: "rsa", S: 3072},
  19. {A: "rsa", S: 4096},
  20. {A: "ecdsa", S: 256},
  21. {A: "ecdsa", S: 384},
  22. {A: "ecdsa", S: 521},
  23. {A: "ed25519"},
  24. }
  25. var validCAConfigs = []csr.CAConfig{
  26. {PathLength: 0, PathLenZero: true},
  27. {PathLength: 0, PathLenZero: false},
  28. {PathLength: 2},
  29. {PathLength: 2, Expiry: "1h"},
  30. // invalid PathLenZero value will be ignored
  31. {PathLength: 2, PathLenZero: true},
  32. }
  33. var invalidCAConfig = csr.CAConfig{
  34. PathLength: 2,
  35. // Expiry must be a duration string
  36. Expiry: "2116/12/31",
  37. }
  38. var csrFiles = []string{
  39. "testdata/rsa2048.csr",
  40. "testdata/rsa3072.csr",
  41. "testdata/rsa4096.csr",
  42. "testdata/ecdsa256.csr",
  43. "testdata/ecdsa384.csr",
  44. "testdata/ecdsa521.csr",
  45. "testdata/ed25519.csr",
  46. }
  47. var testRSACAFile = "testdata/5min-rsa.pem"
  48. var testRSACAKeyFile = "testdata/5min-rsa-key.pem"
  49. var testECDSACAFile = "testdata/5min-ecdsa.pem"
  50. var testECDSACAKeyFile = "testdata/5min-ecdsa-key.pem"
  51. var testED25519CAFile = "testdata/5min-ed25519.pem"
  52. var testED25519CAKeyFile = "testdata/5min-ed25519-key.pem"
  53. var invalidCryptoParams = []csr.KeyRequest{
  54. // Weak Key
  55. {A: "rsa", S: 1024},
  56. // Bad param
  57. {A: "rsaCrypto", S: 2048},
  58. {A: "ecdsa", S: 2000},
  59. }
  60. func TestInitCA(t *testing.T) {
  61. var req *csr.CertificateRequest
  62. hostname := "cloudflare.com"
  63. crl := "http://crl.cloudflare.com/655c6a9b-01c6-4eea-bf21-be690cc315e0.crl" // cert_uuid.crl
  64. for _, param := range validKeyParams {
  65. for _, caconfig := range validCAConfigs {
  66. req = &csr.CertificateRequest{
  67. Names: []csr.Name{
  68. {
  69. C: "US",
  70. ST: "California",
  71. L: "San Francisco",
  72. O: "CloudFlare",
  73. OU: "Systems Engineering",
  74. },
  75. },
  76. CN: hostname,
  77. Hosts: []string{hostname, "www." + hostname},
  78. KeyRequest: &param,
  79. CA: &caconfig,
  80. CRL: crl,
  81. }
  82. certBytes, _, keyBytes, err := New(req)
  83. if err != nil {
  84. t.Fatal("InitCA failed:", err)
  85. }
  86. key, err := helpers.ParsePrivateKeyPEM(keyBytes)
  87. if err != nil {
  88. t.Fatal("InitCA private key parsing failed:", err)
  89. }
  90. cert, err := helpers.ParseCertificatePEM(certBytes)
  91. if err != nil {
  92. t.Fatal("InitCA cert parsing failed:", err)
  93. }
  94. // Verify if the CRL is set
  95. crlSet := false
  96. for _, certCrl := range cert.CRLDistributionPoints {
  97. if certCrl == crl {
  98. crlSet = true
  99. break
  100. }
  101. }
  102. if !crlSet {
  103. t.Fatal("Missing CRL on certificate")
  104. }
  105. // Verify key parameters.
  106. switch req.KeyRequest.Algo() {
  107. case "rsa":
  108. if cert.PublicKey.(*rsa.PublicKey).N.BitLen() != param.Size() {
  109. t.Fatal("Cert key length mismatch.")
  110. }
  111. if key.(*rsa.PrivateKey).N.BitLen() != param.Size() {
  112. t.Fatal("Private key length mismatch.")
  113. }
  114. case "ecdsa":
  115. if cert.PublicKey.(*ecdsa.PublicKey).Curve.Params().BitSize != param.Size() {
  116. t.Fatal("Cert key length mismatch.")
  117. }
  118. if key.(*ecdsa.PrivateKey).Curve.Params().BitSize != param.Size() {
  119. t.Fatal("Private key length mismatch.")
  120. }
  121. }
  122. // Verify CA MaxPathLen
  123. if caconfig.PathLength == 0 && cert.MaxPathLenZero != caconfig.PathLenZero {
  124. t.Fatalf("fail to init a CA cert with specified CA pathlen zero: expect %v, got %v", caconfig.PathLenZero, cert.MaxPathLenZero)
  125. }
  126. if caconfig.PathLength != 0 {
  127. if cert.MaxPathLen != caconfig.PathLength {
  128. t.Fatalf("fail to init a CA cert with specified CA pathlen: expect %d, got %d", caconfig.PathLength, cert.MaxPathLen)
  129. }
  130. if cert.MaxPathLenZero != false {
  131. t.Fatalf("fail to init a CA cert with specified CA pathlen zero: expect false, got %t", cert.MaxPathLenZero)
  132. }
  133. }
  134. // Replace the default CAPolicy with a test (short expiry) version and add a crl
  135. CAPolicy = func() *config.Signing {
  136. return &config.Signing{
  137. Default: &config.SigningProfile{
  138. Usage: []string{"cert sign", "crl sign"},
  139. ExpiryString: "300s",
  140. Expiry: 300 * time.Second,
  141. CAConstraint: config.CAConstraint{IsCA: true},
  142. CRL: crl,
  143. },
  144. }
  145. }
  146. // Start a signer
  147. s, err := local.NewSigner(key, cert, signer.DefaultSigAlgo(key), nil)
  148. if err != nil {
  149. t.Fatal("Signer Creation error:", err)
  150. }
  151. s.SetPolicy(CAPolicy())
  152. // Sign RSA, ECDSA and ed25519 customer CSRs.
  153. for _, csrFile := range csrFiles {
  154. csrBytes, err := os.ReadFile(csrFile)
  155. if err != nil {
  156. t.Fatal("CSR loading error:", err)
  157. }
  158. req := signer.SignRequest{
  159. Request: string(csrBytes),
  160. Hosts: signer.SplitHosts(hostname),
  161. Profile: "",
  162. Label: "",
  163. }
  164. bytes, err := s.Sign(req)
  165. if err != nil {
  166. t.Fatal(err)
  167. }
  168. customerCert, _ := helpers.ParseCertificatePEM(bytes)
  169. if customerCert.SignatureAlgorithm != s.SigAlgo() {
  170. t.Fatal("Signature Algorithm mismatch")
  171. }
  172. err = customerCert.CheckSignatureFrom(cert)
  173. if err != nil {
  174. t.Fatal("Signing CSR failed.", err)
  175. }
  176. }
  177. }
  178. }
  179. }
  180. func TestInvalidCAConfig(t *testing.T) {
  181. hostname := "example.com"
  182. req := &csr.CertificateRequest{
  183. Names: []csr.Name{
  184. {
  185. C: "US",
  186. ST: "California",
  187. L: "San Francisco",
  188. O: "CloudFlare",
  189. OU: "Systems Engineering",
  190. },
  191. },
  192. CN: hostname,
  193. Hosts: []string{hostname, "www." + hostname},
  194. KeyRequest: &validKeyParams[0],
  195. CA: &invalidCAConfig,
  196. }
  197. _, _, _, err := New(req)
  198. if err == nil {
  199. t.Fatalf("InitCA with bad CAConfig should fail: %v", invalidCAConfig)
  200. }
  201. }
  202. func TestInvalidCryptoParams(t *testing.T) {
  203. var req *csr.CertificateRequest
  204. hostname := "cloudflare.com"
  205. for _, invalidParam := range invalidCryptoParams {
  206. req = &csr.CertificateRequest{
  207. Names: []csr.Name{
  208. {
  209. C: "US",
  210. ST: "California",
  211. L: "San Francisco",
  212. O: "CloudFlare",
  213. OU: "Systems Engineering",
  214. },
  215. },
  216. CN: hostname,
  217. Hosts: []string{hostname, "www." + hostname},
  218. KeyRequest: &invalidParam,
  219. }
  220. _, _, _, err := New(req)
  221. if err == nil {
  222. t.Fatalf("InitCA with bad CAConfig should fail: %v", invalidCAConfig)
  223. }
  224. if !strings.Contains(err.Error(), `"code":2400`) {
  225. t.Fatal(err)
  226. }
  227. }
  228. }
  229. type validation struct {
  230. r *csr.CertificateRequest
  231. v bool
  232. }
  233. var testValidations = []validation{
  234. {&csr.CertificateRequest{}, false},
  235. {&csr.CertificateRequest{
  236. CN: "test CA",
  237. }, true},
  238. {&csr.CertificateRequest{
  239. Names: []csr.Name{{}},
  240. }, false},
  241. {&csr.CertificateRequest{
  242. Names: []csr.Name{
  243. {O: "Example CA"},
  244. },
  245. }, true},
  246. }
  247. func TestValidations(t *testing.T) {
  248. for i, tv := range testValidations {
  249. err := validator(tv.r)
  250. if tv.v && err != nil {
  251. t.Fatalf("%v", err)
  252. }
  253. if !tv.v && err == nil {
  254. t.Fatalf("%d: expected error, but no error was reported", i)
  255. }
  256. }
  257. }
  258. func TestRenewRSA(t *testing.T) {
  259. certPEM, err := RenewFromPEM(testRSACAFile, testRSACAKeyFile)
  260. if err != nil {
  261. t.Fatal(err)
  262. }
  263. // must parse ok
  264. cert, err := helpers.ParseCertificatePEM(certPEM)
  265. if err != nil {
  266. t.Fatal(err)
  267. }
  268. if !cert.IsCA {
  269. t.Fatal("renewed CA certificate is not CA")
  270. }
  271. // cert expiry must be 5 minutes
  272. expiry := cert.NotAfter.Sub(cert.NotBefore).Seconds()
  273. if expiry >= 301 || expiry <= 299 {
  274. t.Fatal("expiry is not correct:", expiry)
  275. }
  276. // check subject
  277. if cert.Subject.CommonName != "" {
  278. t.Fatal("Bad CommonName")
  279. }
  280. if len(cert.Subject.Country) != 1 || cert.Subject.Country[0] != "US" {
  281. t.Fatal("Bad Subject")
  282. }
  283. if len(cert.Subject.Organization) != 1 || cert.Subject.Organization[0] != "CloudFlare, Inc." {
  284. t.Fatal("Bad Subject")
  285. }
  286. }
  287. func TestRenewED25519(t *testing.T) {
  288. certPEM, err := RenewFromPEM(testED25519CAFile, testED25519CAKeyFile)
  289. if err != nil {
  290. t.Fatal(err)
  291. }
  292. // must parse ok
  293. cert, err := helpers.ParseCertificatePEM(certPEM)
  294. if err != nil {
  295. t.Fatal(err)
  296. }
  297. if !cert.IsCA {
  298. t.Fatal("renewed CA certificate is not CA")
  299. }
  300. // cert expiry must be 5 minutes
  301. expiry := cert.NotAfter.Sub(cert.NotBefore).Seconds()
  302. if expiry >= 301 || expiry <= 299 {
  303. t.Fatal("expiry is not correct:", expiry)
  304. }
  305. // check subject
  306. if cert.Subject.CommonName != "" {
  307. t.Fatal("Bad CommonName")
  308. }
  309. if len(cert.Subject.Country) != 1 || cert.Subject.Country[0] != "US" {
  310. t.Fatal("Bad Subject")
  311. }
  312. if len(cert.Subject.Organization) != 1 || cert.Subject.Organization[0] != "CloudFlare, Inc." {
  313. t.Fatal("Bad Subject")
  314. }
  315. }
  316. func TestRenewECDSA(t *testing.T) {
  317. certPEM, err := RenewFromPEM(testECDSACAFile, testECDSACAKeyFile)
  318. if err != nil {
  319. t.Fatal(err)
  320. }
  321. // must parse ok
  322. cert, err := helpers.ParseCertificatePEM(certPEM)
  323. if err != nil {
  324. t.Fatal(err)
  325. }
  326. if !cert.IsCA {
  327. t.Fatal("renewed CA certificate is not CA")
  328. }
  329. // cert expiry must be 5 minutes
  330. expiry := cert.NotAfter.Sub(cert.NotBefore).Seconds()
  331. if expiry >= 301 || expiry <= 299 {
  332. t.Fatal("expiry is not correct:", expiry)
  333. }
  334. // check subject
  335. if cert.Subject.CommonName != "" {
  336. t.Fatal("Bad CommonName")
  337. }
  338. if len(cert.Subject.Country) != 1 || cert.Subject.Country[0] != "US" {
  339. t.Fatal("Bad Subject")
  340. }
  341. if len(cert.Subject.Organization) != 1 || cert.Subject.Organization[0] != "CloudFlare, Inc." {
  342. t.Fatal("Bad Subject")
  343. }
  344. }
  345. func TestRenewMismatch(t *testing.T) {
  346. _, err := RenewFromPEM(testECDSACAFile, testRSACAKeyFile)
  347. if err == nil {
  348. t.Fatal("Fail to detect cert/key mismatch")
  349. }
  350. }
  351. func TestRenew(t *testing.T) {
  352. in, err := os.ReadFile(testECDSACAFile)
  353. if err != nil {
  354. t.Fatal(err)
  355. }
  356. cert, err := helpers.ParseCertificatePEM(in)
  357. if err != nil {
  358. t.Fatal(err)
  359. }
  360. in, err = os.ReadFile(testECDSACAKeyFile)
  361. if err != nil {
  362. t.Fatal(err)
  363. }
  364. priv, err := helpers.ParsePrivateKeyPEM(in)
  365. if err != nil {
  366. t.Fatal(err)
  367. }
  368. renewed, err := Update(cert, priv)
  369. if err != nil {
  370. t.Fatal(err)
  371. }
  372. newCert, err := helpers.ParseCertificatePEM(renewed)
  373. if err != nil {
  374. t.Fatal(err)
  375. }
  376. if !bytes.Equal(newCert.RawSubjectPublicKeyInfo, cert.RawSubjectPublicKeyInfo) {
  377. t.Fatal("Update returned a certificate with different subject public key info")
  378. }
  379. if !bytes.Equal(newCert.RawSubject, cert.RawSubject) {
  380. t.Fatal("Update returned a certificate with different subject info")
  381. }
  382. if !bytes.Equal(newCert.RawIssuer, cert.RawIssuer) {
  383. t.Fatal("Update returned a certificate with different issuer info")
  384. }
  385. }