initca_test.go 9.0 KB

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