github.com/hxx258456/fabric-ca-gm@v0.0.3-0.20221111064038-a268ad7e3a37/test/integration/default/default_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package defserver
     8  
     9  import (
    10  	"bytes"
    11  	"crypto/rand"
    12  	"crypto/x509/pkix"
    13  	"encoding/pem"
    14  	"fmt"
    15  	"io"
    16  	"math/big"
    17  	"os"
    18  	"path/filepath"
    19  	"testing"
    20  	"time"
    21  
    22  	log "gitee.com/zhaochuninhefei/zcgolog/zclog"
    23  	"github.com/hxx258456/ccgo/sm2"
    24  	"github.com/hxx258456/ccgo/x509"
    25  	"github.com/hxx258456/cfssl-gm/certdb"
    26  	"github.com/hxx258456/cfssl-gm/config"
    27  	"github.com/hxx258456/fabric-ca-gm/cmd/fabric-ca-client/command"
    28  	"github.com/hxx258456/fabric-ca-gm/internal/pkg/util"
    29  	"github.com/hxx258456/fabric-ca-gm/lib"
    30  	"github.com/hxx258456/fabric-ca-gm/lib/metadata"
    31  	"github.com/hxx258456/fabric-ca-gm/lib/server/db"
    32  	"github.com/pkg/errors"
    33  	"github.com/stretchr/testify/assert"
    34  )
    35  
    36  const (
    37  	cmdName = "fabric-ca-client"
    38  )
    39  
    40  var (
    41  	defaultServer          *lib.Server
    42  	defaultServerPort      = 7054
    43  	defaultServerEnrollURL = fmt.Sprintf("http://admin:adminpw@localhost:%d", defaultServerPort)
    44  	defaultServerHomeDir   = "defaultServerDir"
    45  	storeCertsDir          = "/tmp/testCerts"
    46  )
    47  
    48  func TestMain(m *testing.M) {
    49  	var err error
    50  
    51  	metadata.Version = "1.1.0"
    52  
    53  	os.RemoveAll(defaultServerHomeDir)
    54  	defaultServer, err = getDefaultServer()
    55  	if err != nil {
    56  		log.Errorf("Failed to get instance of server: %s", err)
    57  		os.Exit(1)
    58  	}
    59  
    60  	err = defaultServer.Start()
    61  	if err != nil {
    62  		log.Errorf("Failed to start server: %s", err)
    63  		os.Exit(1)
    64  	}
    65  
    66  	rc := m.Run()
    67  
    68  	err = defaultServer.Stop()
    69  	if err != nil {
    70  		log.Errorf("Failed to stop server: %s, integration test results: %d", err, rc)
    71  		os.Exit(1)
    72  	}
    73  
    74  	os.RemoveAll(defaultServerHomeDir)
    75  	os.RemoveAll(storeCertsDir)
    76  	os.Exit(rc)
    77  }
    78  
    79  func TestListCertificateCmdNegative(t *testing.T) {
    80  	var err error
    81  	// Remove default client home location to remove any existing enrollment information
    82  	os.RemoveAll(filepath.Dir(util.GetDefaultConfigFile("fabric-ca-client")))
    83  
    84  	// Command should fail if caller has not yet enrolled
    85  	err = command.RunMain([]string{cmdName, "certificate", "list", "-d"})
    86  	util.ErrorContains(t, err, "Enrollment information does not exist", "Should have failed to call command, if caller has not yet enrolled")
    87  
    88  	// Enroll a user that will be used for subsequent certificate commands
    89  	err = command.RunMain([]string{cmdName, "enroll", "-u", defaultServerEnrollURL, "-d"})
    90  	util.FatalError(t, err, "Failed to enroll user")
    91  
    92  	// Test with --revocation flag
    93  	err = command.RunMain([]string{cmdName, "certificate", "list", "-d", "--id", "admin", "--revocation", "-30d:-15d"})
    94  	t.Log("Error: ", err)
    95  	assert.Error(t, err, "Should fail, only one ':' specified need to specify two '::'")
    96  
    97  	err = command.RunMain([]string{cmdName, "certificate", "list", "-d", "--id", "admin", "--revocation", "30d::-15d"})
    98  	t.Log("Error: ", err)
    99  	assert.Error(t, err, "Should fail, missing +/- on starting duration")
   100  
   101  	err = command.RunMain([]string{cmdName, "certificate", "list", "-d", "--id", "admin", "--revocation", "+30d::15d"})
   102  	t.Log("Error: ", err)
   103  	assert.Error(t, err, "Should fail, missing +/- on ending duration")
   104  
   105  	err = command.RunMain([]string{cmdName, "certificate", "list", "-d", "--id", "admin", "--revocation", "+30d::+15y"})
   106  	t.Log("Error: ", err)
   107  	assert.Error(t, err, "Should fail, invalid duration type (y)")
   108  
   109  	// Test with --expiration flag
   110  	err = command.RunMain([]string{cmdName, "certificate", "list", "-d", "--id", "admin", "--expiration", "-30d:-15d"})
   111  	t.Log("Error: ", err)
   112  	assert.Error(t, err, "Should fail, only one ':' specified need to specify two '::'")
   113  
   114  	err = command.RunMain([]string{cmdName, "certificate", "list", "-d", "--id", "admin", "--expiration", "30d::-15d"})
   115  	t.Log("Error: ", err)
   116  	assert.Error(t, err, "Should fail, missing +/- on starting duration")
   117  
   118  	err = command.RunMain([]string{cmdName, "certificate", "list", "-d", "--id", "admin", "--expiration", "+30d::15d"})
   119  	t.Log("Error: ", err)
   120  	assert.Error(t, err, "Should fail, missing +/- on ending duration")
   121  
   122  	err = command.RunMain([]string{cmdName, "certificate", "list", "-d", "--id", "admin", "--expiration", "1/30/18::2/14/2018"})
   123  	t.Log("Error: ", err)
   124  	assert.Error(t, err, "Should fail, using slashes instead of dashes in time format")
   125  }
   126  
   127  func TestListCertificateCmdPositive(t *testing.T) {
   128  	populateCertificatesTable(t, defaultServer)
   129  
   130  	var err error
   131  	err = command.RunMain([]string{cmdName, "certificate", "list", "-d", "--id", "admin", "--expiration", "+30d::+15d", "--notexpired"})
   132  	t.Log("Error: ", err)
   133  	assert.Error(t, err, "Should fail, --expiration and --notexpired together")
   134  
   135  	err = command.RunMain([]string{cmdName, "certificate", "list", "-d", "--id", "admin", "--revocation", "-30d::-10d", "--notrevoked"})
   136  	t.Log("Error: ", err)
   137  	assert.Error(t, err, "Should fail, --revocation and --notrevoked together")
   138  
   139  	// Enroll a user that will be used for subsequent certificate commands
   140  	err = command.RunMain([]string{cmdName, "enroll", "-u", defaultServerEnrollURL, "-d"})
   141  	util.FatalError(t, err, "Failed to enroll user")
   142  
   143  	err = command.RunMain([]string{cmdName, "certificate", "list", "-d"})
   144  	assert.NoError(t, err, "Failed to get certificates")
   145  
   146  	result, err := captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--id", "expire1"})
   147  	assert.NoError(t, err, "Failed to get certificate for an id")
   148  	assert.Contains(t, result, "expire1")
   149  
   150  	result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--serial", "1111"})
   151  	assert.NoError(t, err, "Failed to parse a correctly formatted revocation duration")
   152  	assert.Contains(t, result, "Serial Number: 1111")
   153  
   154  	result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--aki", "9876"})
   155  	assert.NoError(t, err, "Failed to parse a correctly formatted revocation duration")
   156  	assert.Contains(t, result, "Serial Number: 1111")
   157  	assert.Contains(t, result, "Serial Number: 1112")
   158  
   159  	result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--serial", "1112", "--aki", "9876"})
   160  	assert.NoError(t, err, "Failed to parse a correctly formatted revocation duration")
   161  	assert.Contains(t, result, "Serial Number: 1112")
   162  	assert.NotContains(t, result, "Serial Number: 1111")
   163  
   164  	result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--id", "testCertificate3", "--aki", "9876AB"})
   165  	assert.NoError(t, err, "Failed to parse a correctly formatted revocation duration")
   166  	assert.Contains(t, result, "1113")
   167  	assert.Contains(t, result, "testCertificate3")
   168  	assert.NotContains(t, result, "testCertificate1")
   169  
   170  	result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--id", "expire1", "--expiration", "2018-01-01::2018-03-05"})
   171  	assert.NoError(t, err, "Failed to parse a correctly formatted expiration date range")
   172  	assert.Contains(t, result, "expire1")
   173  	assert.NotContains(t, result, "expire3")
   174  
   175  	result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--id", "revoked2", "--revocation", "2017-01-01::2017-12-31"})
   176  	assert.NoError(t, err, "Failed to parse a correctly formatted revocation date range")
   177  	assert.Contains(t, result, "revoked2")
   178  	assert.NotContains(t, result, "revoked3")
   179  
   180  	result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--expiration", "2018-03-01T00:00:00Z::2018-03-03T23:00:00Z"})
   181  	assert.NoError(t, err, "Failed to parse a correctly formatted expiration date range")
   182  	assert.Contains(t, result, "Serial Number: 1121")
   183  	assert.NotContains(t, result, "Serial Number: 1123")
   184  
   185  	result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--revocation", "2017-02-01T01:00:00Z::2017-02-20T23:00:00Z"})
   186  	assert.NoError(t, err, "Failed to parse a correctly formatted revocation date range")
   187  	assert.Contains(t, result, "Serial Number: 1132")
   188  	assert.NotContains(t, result, "Serial Number: 1131")
   189  
   190  	result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--expiration", "now::+101h"})
   191  	assert.NoError(t, err, "Failed to parse a expiration date range using 'now'")
   192  	assert.Contains(t, result, "Serial Number: 1123")
   193  	assert.NotContains(t, result, "Serial Number: 1121")
   194  
   195  	result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--revocation", "-15d::now"})
   196  	assert.NoError(t, err, "Failed to parse a revocation date range using 'now'")
   197  	assert.Contains(t, result, "Serial Number: 1131")
   198  	assert.NotContains(t, result, "Serial Number: 1111")
   199  
   200  	result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--expiration", "now::"})
   201  	assert.NoError(t, err, "Failed to parse a expiration date range using 'now' and empty end date")
   202  	assert.NotContains(t, result, "Serial Number: 1121")
   203  
   204  	result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--expiration", "::now"})
   205  	assert.NoError(t, err, "Failed to parse a expiration date range using 'now' and empty start date")
   206  	assert.Contains(t, result, "Serial Number: 1121")
   207  	assert.Contains(t, result, "Serial Number: 1122")
   208  	assert.Contains(t, result, "Serial Number: 1124")
   209  
   210  	result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--expiration", "::now", "--notrevoked"})
   211  	assert.NoError(t, err, "Failed to parse a expiration date range using 'now' and empty start date")
   212  	assert.Contains(t, result, "Serial Number: 1121")
   213  	assert.Contains(t, result, "Serial Number: 1122")
   214  	assert.NotContains(t, result, "Serial Number: 1124")
   215  
   216  	result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--revocation", "2018-02-01T01:00:00Z::"})
   217  	assert.NoError(t, err, "Failed to parse a revocation date range using 'now' and empty end date")
   218  	assert.Contains(t, result, "1131")
   219  
   220  	result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--revocation", "::now"})
   221  	assert.NoError(t, err, "Failed to parse a revocation date range using 'now' and empty start date")
   222  	assert.Contains(t, result, "Serial Number: 1131")
   223  	assert.Contains(t, result, "Serial Number: 1132")
   224  	assert.Contains(t, result, "Serial Number: 1124")
   225  
   226  	result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--revocation", "::now", "--notexpired"})
   227  	assert.NoError(t, err, "Failed to parse a revocation date range using 'now' and empty start date")
   228  	assert.Contains(t, result, "Serial Number: 1131")
   229  	assert.Contains(t, result, "Serial Number: 1132")
   230  	assert.NotContains(t, result, "Serial Number: 1124")
   231  
   232  	result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--notrevoked", "--notexpired"})
   233  	assert.NoError(t, err, "Failed to parse a revocation date range using 'now' and empty start date")
   234  	assert.Contains(t, result, "Serial Number: 1111")
   235  	assert.Contains(t, result, "Serial Number: 1112")
   236  	assert.Contains(t, result, "Serial Number: 1113")
   237  	assert.Contains(t, result, "Serial Number: 1123")
   238  	assert.NotContains(t, result, "Serial Number: 1121")
   239  	assert.NotContains(t, result, "Serial Number: 1122")
   240  	assert.NotContains(t, result, "Serial Number: 1124")
   241  	assert.NotContains(t, result, "Serial Number: 1131")
   242  	assert.NotContains(t, result, "Serial Number: 1132")
   243  	assert.NotContains(t, result, "Serial Number: 1133")
   244  
   245  	result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "-d", "--id", "fakeID"})
   246  	assert.NoError(t, err, "Should not error if the ID does not exist")
   247  	assert.Contains(t, result, "No results returned")
   248  
   249  	result, err = captureCLICertificatesOutput(command.RunMain, []string{cmdName, "certificate", "list", "--id", "expire1", "--store", storeCertsDir})
   250  	assert.NoError(t, err, "Should not error if the ID does not exist")
   251  	assert.Equal(t, true, util.FileExists(filepath.Join(storeCertsDir, "expire1-1.pem")))
   252  	assert.Equal(t, true, util.FileExists(filepath.Join(storeCertsDir, "expire1-2.pem")))
   253  	assert.Contains(t, result, "Serial Number: 1121")
   254  	assert.Contains(t, result, "Serial Number: 1124")
   255  }
   256  
   257  func populateCertificatesTable(t *testing.T, srv *lib.Server) {
   258  	var err error
   259  
   260  	dur, err := time.ParseDuration("+100h")
   261  	util.FatalError(t, err, "Failed to parse duration '+100h'")
   262  	futureTime := time.Now().Add(dur).UTC()
   263  
   264  	dur, err = time.ParseDuration("-72h")
   265  	util.FatalError(t, err, "Failed to parse duration '-72h'")
   266  	pastTime := time.Now().Add(dur).UTC()
   267  
   268  	// Active Certs
   269  	err = testInsertCertificate(&certdb.CertificateRecord{
   270  		Serial: "1111",
   271  		AKI:    "9876",
   272  		Expiry: futureTime,
   273  	}, "testCertificate1", srv)
   274  	util.FatalError(t, err, "Failed to insert certificate with serial/AKI")
   275  
   276  	err = testInsertCertificate(&certdb.CertificateRecord{
   277  		Serial: "1112",
   278  		AKI:    "9876",
   279  		Expiry: futureTime,
   280  	}, "testCertificate2", srv)
   281  	util.FatalError(t, err, "Failed to insert certificate with serial/AKI")
   282  
   283  	err = testInsertCertificate(&certdb.CertificateRecord{
   284  		Serial: "1113",
   285  		AKI:    "9876ab",
   286  		Expiry: futureTime,
   287  	}, "testCertificate3", srv)
   288  	util.FatalError(t, err, "Failed to insert certificate with serial/AKI")
   289  
   290  	// Expired
   291  	err = testInsertCertificate(&certdb.CertificateRecord{
   292  		Serial: "1121",
   293  		AKI:    "98765",
   294  		Expiry: time.Date(2018, time.March, 1, 0, 0, 0, 0, time.UTC),
   295  	}, "expire1", srv)
   296  	util.FatalError(t, err, "Failed to insert certificate with expiration date")
   297  
   298  	err = testInsertCertificate(&certdb.CertificateRecord{
   299  		Serial: "1122",
   300  		AKI:    "98765",
   301  		Expiry: time.Date(2018, time.March, 1, 0, 0, 0, 0, time.UTC),
   302  	}, "expire3", srv)
   303  	util.FatalError(t, err, "Failed to insert certificate with expiration date")
   304  
   305  	// Not Expired
   306  	err = testInsertCertificate(&certdb.CertificateRecord{
   307  		Serial: "1123",
   308  		AKI:    "98765",
   309  		Expiry: futureTime,
   310  	}, "expire2", srv)
   311  	util.FatalError(t, err, "Failed to insert certificate with expiration date")
   312  
   313  	// Expired and Revoked
   314  	err = testInsertCertificate(&certdb.CertificateRecord{
   315  		Serial:    "1124",
   316  		AKI:       "98765",
   317  		Expiry:    time.Date(2018, time.March, 1, 0, 0, 0, 0, time.UTC),
   318  		RevokedAt: pastTime,
   319  	}, "expire1", srv)
   320  	util.FatalError(t, err, "Failed to insert certificate with expiration date")
   321  
   322  	// Revoked
   323  	err = testInsertCertificate(&certdb.CertificateRecord{
   324  		Serial:    "1131",
   325  		AKI:       "98765",
   326  		Expiry:    futureTime,
   327  		RevokedAt: pastTime,
   328  	}, "revoked1", srv)
   329  	util.FatalError(t, err, "Failed to insert certificate with revocation date")
   330  
   331  	err = testInsertCertificate(&certdb.CertificateRecord{
   332  		Serial:    "1132",
   333  		AKI:       "98765",
   334  		Expiry:    futureTime,
   335  		RevokedAt: time.Date(2017, time.February, 15, 0, 0, 0, 0, time.UTC),
   336  	}, "revoked2", srv)
   337  	util.FatalError(t, err, "Failed to insert certificate with revocation date")
   338  
   339  	err = testInsertCertificate(&certdb.CertificateRecord{
   340  		Serial:    "1133",
   341  		AKI:       "98765",
   342  		Expiry:    futureTime,
   343  		RevokedAt: time.Date(2017, time.February, 15, 0, 0, 0, 0, time.UTC),
   344  	}, "revoked3", srv)
   345  	util.FatalError(t, err, "Failed to insert certificate with revocation date")
   346  }
   347  
   348  func captureCLICertificatesOutput(f func(args []string) error, args []string) (string, error) {
   349  	old := os.Stdout
   350  	r, w, err := os.Pipe()
   351  	if err != nil {
   352  		panic(err)
   353  	}
   354  	os.Stdout = w
   355  	err = f(args)
   356  	if err != nil {
   357  		return "", err
   358  	}
   359  	w.Close()
   360  	os.Stdout = old
   361  	var buf bytes.Buffer
   362  	io.Copy(&buf, r)
   363  	return buf.String(), nil
   364  }
   365  
   366  func TestRevokeWithColons(t *testing.T) {
   367  	var err error
   368  
   369  	err = testInsertCertificate(&certdb.CertificateRecord{
   370  		Serial: "11aa22bb",
   371  		AKI:    "33cc44dd",
   372  	}, "testingRevoke", defaultServer)
   373  	util.FatalError(t, err, "Failed to insert certificate with serial/AKI")
   374  
   375  	// Enroll a user that will be used for subsequent revoke commands
   376  	err = command.RunMain([]string{cmdName, "enroll", "-u", defaultServerEnrollURL, "-d"})
   377  	util.FatalError(t, err, "Failed to enroll user")
   378  
   379  	err = command.RunMain([]string{cmdName, "register", "-u", defaultServerEnrollURL, "--id.name", "testingRevoke", "-d"})
   380  	util.FatalError(t, err, "Failed to enroll user")
   381  
   382  	err = command.RunMain([]string{cmdName, "revoke", "-s", "11:AA:22:bb", "-a", "33:Cc:44:DD", "-d"})
   383  	assert.NoError(t, err, "Failed to revoke certificate, when serial number and AKI contained colons")
   384  }
   385  
   386  func getDefaultServer() (*lib.Server, error) {
   387  	affiliations := map[string]interface{}{
   388  		"hyperledger": map[string]interface{}{
   389  			"fabric":    []string{"ledger", "orderer", "security"},
   390  			"fabric-ca": nil,
   391  			"sdk":       nil,
   392  		},
   393  		"org2":      []string{"dept1"},
   394  		"org1":      nil,
   395  		"org2dept1": nil,
   396  	}
   397  	profiles := map[string]*config.SigningProfile{
   398  		"tls": &config.SigningProfile{
   399  			Usage:        []string{"signing", "key encipherment", "server auth", "client auth", "key agreement"},
   400  			ExpiryString: "8760h",
   401  		},
   402  		"ca": &config.SigningProfile{
   403  			Usage:        []string{"cert sign", "crl sign"},
   404  			ExpiryString: "8760h",
   405  			CAConstraint: config.CAConstraint{
   406  				IsCA:       true,
   407  				MaxPathLen: 0,
   408  			},
   409  		},
   410  	}
   411  	defaultProfile := &config.SigningProfile{
   412  		Usage:        []string{"cert sign"},
   413  		ExpiryString: "8760h",
   414  	}
   415  	srv := &lib.Server{
   416  		Config: &lib.ServerConfig{
   417  			Port:  defaultServerPort,
   418  			Debug: true,
   419  		},
   420  		CA: lib.CA{
   421  			Config: &lib.CAConfig{
   422  				Intermediate: lib.IntermediateCA{
   423  					ParentServer: lib.ParentServer{
   424  						URL: "",
   425  					},
   426  				},
   427  				Affiliations: affiliations,
   428  				Registry: lib.CAConfigRegistry{
   429  					MaxEnrollments: -1,
   430  				},
   431  				Signing: &config.Signing{
   432  					Profiles: profiles,
   433  					Default:  defaultProfile,
   434  				},
   435  				Version: "1.1.0", // The default test server/ca should use the latest version
   436  			},
   437  		},
   438  		HomeDir: defaultServerHomeDir,
   439  	}
   440  	// The bootstrap user's affiliation is the empty string, which
   441  	// means the user is at the affiliation root
   442  	err := srv.RegisterBootstrapUser("admin", "adminpw", "")
   443  	if err != nil {
   444  		return nil, err
   445  	}
   446  	return srv, nil
   447  }
   448  
   449  func testInsertCertificate(req *certdb.CertificateRecord, id string, srv *lib.Server) error {
   450  	priv, err := sm2.GenerateKey(rand.Reader)
   451  	if err != nil {
   452  		return errors.Errorf("Failed to generate private key: %s", err)
   453  	}
   454  
   455  	serial := new(big.Int)
   456  	serial.SetString(req.Serial, 10) //base 10
   457  	template := x509.Certificate{
   458  		Subject: pkix.Name{
   459  			CommonName: id,
   460  		},
   461  		SerialNumber:   serial,
   462  		AuthorityKeyId: []byte(req.AKI),
   463  		KeyUsage:       x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
   464  	}
   465  
   466  	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
   467  	if err != nil {
   468  		return err
   469  	}
   470  
   471  	cert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
   472  	record := &db.CertRecord{
   473  		ID: id,
   474  		CertificateRecord: certdb.CertificateRecord{
   475  			Serial:    req.Serial,
   476  			AKI:       req.AKI,
   477  			CALabel:   req.CALabel,
   478  			Status:    req.Status,
   479  			Reason:    req.Reason,
   480  			Expiry:    req.Expiry.UTC(),
   481  			RevokedAt: req.RevokedAt.UTC(),
   482  			PEM:       string(cert),
   483  		},
   484  	}
   485  
   486  	db := srv.CA.GetDB()
   487  	res, err := db.NamedExec("", `INSERT INTO certificates (id, serial_number, authority_key_identifier, ca_label, status, reason, expiry, revoked_at, pem, level)
   488  	VALUES (:id, :serial_number, :authority_key_identifier, :ca_label, :status, :reason, :expiry, :revoked_at, :pem, :level);`, record)
   489  
   490  	if err != nil {
   491  		return errors.Wrap(err, "Failed to insert record into database")
   492  	}
   493  
   494  	numRowsAffected, err := res.RowsAffected()
   495  
   496  	if numRowsAffected == 0 {
   497  		return errors.New("Failed to insert the certificate record; no rows affected")
   498  	}
   499  
   500  	if numRowsAffected != 1 {
   501  		return errors.Errorf("Expected to affect 1 entry in certificate database but affected %d",
   502  			numRowsAffected)
   503  	}
   504  
   505  	return err
   506  }