revoke_test.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. package revoke
  2. import (
  3. "bytes"
  4. "crypto/rand"
  5. "crypto/rsa"
  6. "crypto/x509"
  7. "crypto/x509/pkix"
  8. "encoding/hex"
  9. "encoding/json"
  10. "encoding/pem"
  11. "io/ioutil"
  12. "math/big"
  13. "net/http"
  14. "net/http/httptest"
  15. "testing"
  16. "time"
  17. "github.com/cloudflare/cfssl/api"
  18. "github.com/cloudflare/cfssl/certdb"
  19. "github.com/cloudflare/cfssl/certdb/sql"
  20. "github.com/cloudflare/cfssl/certdb/testdb"
  21. "github.com/cloudflare/cfssl/ocsp"
  22. stdocsp "golang.org/x/crypto/ocsp"
  23. )
  24. const (
  25. fakeAKI = "fake aki"
  26. )
  27. func prepDB() (certdb.Accessor, error) {
  28. db := testdb.SQLiteDB("../../certdb/testdb/certstore_development.db")
  29. expirationTime := time.Now().AddDate(1, 0, 0)
  30. var cert = certdb.CertificateRecord{
  31. Serial: "1",
  32. AKI: fakeAKI,
  33. Expiry: expirationTime,
  34. PEM: "unexpired cert",
  35. }
  36. dbAccessor := sql.NewAccessor(db)
  37. err := dbAccessor.InsertCertificate(cert)
  38. if err != nil {
  39. return nil, err
  40. }
  41. return dbAccessor, nil
  42. }
  43. func testRevokeCert(t *testing.T, dbAccessor certdb.Accessor, serial, aki, reason string) (resp *http.Response, body []byte) {
  44. ts := httptest.NewServer(NewHandler(dbAccessor))
  45. defer ts.Close()
  46. obj := map[string]interface{}{}
  47. obj["serial"] = serial
  48. obj["authority_key_id"] = aki
  49. if reason != "" {
  50. obj["reason"] = reason
  51. }
  52. blob, err := json.Marshal(obj)
  53. if err != nil {
  54. t.Fatal(err)
  55. }
  56. resp, err = http.Post(ts.URL, "application/json", bytes.NewReader(blob))
  57. if err != nil {
  58. t.Fatal(err)
  59. }
  60. body, err = ioutil.ReadAll(resp.Body)
  61. if err != nil {
  62. t.Fatal(err)
  63. }
  64. return
  65. }
  66. func TestInvalidRevocation(t *testing.T) {
  67. dbAccessor, err := prepDB()
  68. if err != nil {
  69. t.Fatal(err)
  70. }
  71. resp, _ := testRevokeCert(t, dbAccessor, "", "", "")
  72. if resp.StatusCode != http.StatusBadRequest {
  73. t.Fatal("expected bad request response")
  74. }
  75. }
  76. func TestRevocation(t *testing.T) {
  77. dbAccessor, err := prepDB()
  78. if err != nil {
  79. t.Fatal(err)
  80. }
  81. resp, body := testRevokeCert(t, dbAccessor, "1", fakeAKI, "5")
  82. if resp.StatusCode != http.StatusOK {
  83. t.Fatal("unexpected HTTP status code; expected OK", string(body))
  84. }
  85. message := new(api.Response)
  86. err = json.Unmarshal(body, message)
  87. if err != nil {
  88. t.Fatalf("failed to read response body: %v", err)
  89. }
  90. certs, err := dbAccessor.GetCertificate("1", fakeAKI)
  91. if err != nil {
  92. t.Fatal("failed to get certificate ", err)
  93. }
  94. if len(certs) != 1 {
  95. t.Fatal("failed to get one certificate")
  96. }
  97. cert := certs[0]
  98. if cert.Status != "revoked" || cert.Reason != 5 {
  99. t.Fatal("cert was not correctly revoked")
  100. }
  101. }
  102. // TestOCSPGeneration tests that revoking a certificate (when the
  103. // request handler has an OCSP response signer) generates an
  104. // appropriate OCSP response in the certdb.
  105. func TestOCSPGeneration(t *testing.T) {
  106. privKey, err := rsa.GenerateKey(rand.Reader, 2048)
  107. if err != nil {
  108. t.Fatal(err)
  109. }
  110. serialNumberRange := new(big.Int).Lsh(big.NewInt(1), 128)
  111. // 1. Generate a CA certificate to serve as the signing certificate.
  112. issuerSerial, err := rand.Int(rand.Reader, serialNumberRange)
  113. if err != nil {
  114. t.Fatal(err)
  115. }
  116. issuerTemplate := x509.Certificate{
  117. SerialNumber: issuerSerial,
  118. Subject: pkix.Name{
  119. Organization: []string{"cfssl unit test"},
  120. },
  121. AuthorityKeyId: []byte{42, 42, 42, 42},
  122. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
  123. IsCA: true,
  124. BasicConstraintsValid: true,
  125. }
  126. issuerBytes, err := x509.CreateCertificate(rand.Reader, &issuerTemplate, &issuerTemplate, &privKey.PublicKey, privKey)
  127. if err != nil {
  128. t.Fatal(err)
  129. }
  130. issuer, err := x509.ParseCertificate(issuerBytes)
  131. if err != nil {
  132. t.Fatal(err)
  133. }
  134. // 2. Generate a certificate signed by the CA certificate to revoke.
  135. revokedSerial, err := rand.Int(rand.Reader, serialNumberRange)
  136. if err != nil {
  137. t.Fatal(err)
  138. }
  139. revokedTemplate := x509.Certificate{
  140. SerialNumber: revokedSerial,
  141. Subject: pkix.Name{
  142. Organization: []string{"Cornell CS 5152"},
  143. },
  144. AuthorityKeyId: []byte{42, 42, 42, 42},
  145. }
  146. revokedBytes, err := x509.CreateCertificate(rand.Reader, &revokedTemplate, issuer, &privKey.PublicKey, privKey)
  147. if err != nil {
  148. t.Fatal(err)
  149. }
  150. revoked := pem.EncodeToMemory(&pem.Block{
  151. Type: "CERTIFICATE",
  152. Bytes: revokedBytes,
  153. })
  154. revokedAKI := hex.EncodeToString(revokedTemplate.AuthorityKeyId)
  155. revokedSerialStr := revokedSerial.Text(16)
  156. // 3. Generate a certificate to use as the responder certificate.
  157. responderSerial, err := rand.Int(rand.Reader, serialNumberRange)
  158. if err != nil {
  159. t.Fatal(err)
  160. }
  161. responderTemplate := x509.Certificate{
  162. SerialNumber: responderSerial,
  163. Subject: pkix.Name{
  164. Organization: []string{"Cornell CS 5152 Responder"},
  165. },
  166. AuthorityKeyId: []byte{42, 42, 42, 43},
  167. }
  168. responderBytes, err := x509.CreateCertificate(rand.Reader, &responderTemplate, &responderTemplate, &privKey.PublicKey, privKey)
  169. if err != nil {
  170. t.Fatal(err)
  171. }
  172. responder, err := x509.ParseCertificate(responderBytes)
  173. if err != nil {
  174. t.Fatal(err)
  175. }
  176. // 4. Create the OCSP signer
  177. signer, err := ocsp.NewSigner(issuer, responder, privKey, time.Hour)
  178. if err != nil {
  179. t.Fatal(err)
  180. }
  181. // 5. Spin up the test server
  182. // 5a. Prepare the DB
  183. dbAccessor, err := prepDB()
  184. if err != nil {
  185. t.Fatal(err)
  186. }
  187. expirationTime := time.Now().AddDate(1, 0, 0)
  188. cr := certdb.CertificateRecord{
  189. Serial: revokedSerialStr,
  190. AKI: revokedAKI,
  191. Expiry: expirationTime,
  192. PEM: string(revoked),
  193. }
  194. if err := dbAccessor.InsertCertificate(cr); err != nil {
  195. t.Fatal(err)
  196. }
  197. // 5b. Start the test server
  198. ts := httptest.NewServer(NewOCSPHandler(dbAccessor, signer))
  199. defer ts.Close()
  200. // 6. Prepare the revocation request
  201. obj := map[string]interface{}{}
  202. obj["serial"] = revokedSerialStr
  203. obj["authority_key_id"] = revokedAKI
  204. obj["reason"] = "unspecified"
  205. blob, err := json.Marshal(obj)
  206. if err != nil {
  207. t.Fatal(err)
  208. }
  209. // Get the original number of OCSP responses
  210. ocspsBefore, _ := dbAccessor.GetOCSP(revokedSerialStr, revokedAKI)
  211. ocspCountBefore := len(ocspsBefore)
  212. // 7. Send the revocation request
  213. resp, err := http.Post(ts.URL, "application/json", bytes.NewReader(blob))
  214. if err != nil {
  215. t.Fatal(err)
  216. }
  217. body, err := ioutil.ReadAll(resp.Body)
  218. if err != nil {
  219. t.Fatal(err)
  220. }
  221. if resp.StatusCode != http.StatusOK {
  222. t.Fatal("unexpected HTTP status code; expected OK", string(body))
  223. }
  224. message := new(api.Response)
  225. err = json.Unmarshal(body, message)
  226. if err != nil {
  227. t.Fatalf("failed to read response body: %v", err)
  228. }
  229. // 8. Make sure the certificate record was updated
  230. certs, err := dbAccessor.GetCertificate(revokedSerialStr, revokedAKI)
  231. if err != nil {
  232. t.Fatal("failed to get certificate ", err)
  233. }
  234. if len(certs) != 1 {
  235. t.Fatal("failed to get one certificate")
  236. }
  237. cert := certs[0]
  238. if cert.Status != "revoked" || cert.Reason != stdocsp.Unspecified {
  239. t.Fatal("cert was not correctly revoked")
  240. }
  241. // 9. Make sure there is an OCSP record
  242. ocsps, err := dbAccessor.GetOCSP(revokedSerialStr, revokedAKI)
  243. if err != nil {
  244. t.Fatal("failed to get OCSP responses ", err)
  245. }
  246. if len(ocsps) == 0 {
  247. t.Fatal("No OCSP response generated")
  248. }
  249. if len(ocsps) <= ocspCountBefore {
  250. t.Fatal("No new OCSP response found")
  251. }
  252. }