func (c *OfflineCA) Sign(req *api.SignRequest) (*api.SignResponse, error) {
	ctx := provisioner.NewContextWithMethod(context.Background(), provisioner.SignMethod)
	opts, err := c.authority.Authorize(ctx, req.OTT)
	if err != nil {
		return nil, err
	}
	signOpts := provisioner.SignOptions{
		NotBefore: req.NotBefore,
		NotAfter:  req.NotAfter,
	}
	certChain, err := c.authority.Sign(req.CsrPEM.CertificateRequest, signOpts, opts...)
	if err != nil {
		return nil, err
	}
	certChainPEM := certChainToPEM(certChain)
	var caPEM api.Certificate
	if len(certChainPEM) > 1 {
		caPEM = certChainPEM[1]
	}
	return &api.SignResponse{
		ServerPEM:    certChainPEM[0],
		CaPEM:        caPEM,
		CertChainPEM: certChainPEM,
		TLSOptions:   c.authority.GetTLSOptions(),
	}, nil
}
Sign is a wrapper on top of certificates Authorize and Sign methods. It
returns an api.SignResponse with the requested certificate and the
intermediate.
package cautils
import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"net/http"
	"time"
	"github.com/pkg/errors"
	"github.com/smallstep/certificates/api"
	"github.com/smallstep/certificates/authority"
	"github.com/smallstep/certificates/authority/config"
	"github.com/smallstep/certificates/authority/provisioner"
	"github.com/smallstep/cli/crypto/pemutil"
	"github.com/smallstep/cli/crypto/x509util"
	"github.com/smallstep/cli/utils"
	"github.com/urfave/cli"
	"golang.org/x/crypto/ssh"
)
type OfflineCA struct {
	authority  *authority.Authority
	config     config.Config
	configFile string
}
var offlineInstance *OfflineCA
func NewOfflineCA(ctx *cli.Context, configFile string) (*OfflineCA, error) {
	if offlineInstance != nil {
		return offlineInstance, nil
	}
	b, err := utils.ReadFile(configFile)
	if err != nil {
		return nil, err
	}
	var cfg config.Config
	if err = json.Unmarshal(b, &cfg); err != nil {
		return nil, errors.Wrapf(err, "error reading %s", configFile)
	}
	if cfg.AuthorityConfig == nil || len(cfg.AuthorityConfig.Provisioners) == 0 {
		return nil, errors.Errorf("error parsing %s: no provisioners found", configFile)
	}
	if ctx.String("password-file") != "" {
		passFile := ctx.String("password-file")
		pass, err := utils.ReadPasswordFromFile(passFile)
		if err != nil {
			return nil, errors.Wrapf(err, "error reading %s", passFile)
		}
		cfg.Password = string(pass)
	}
	auth, err := authority.New(&cfg)
	if err != nil {
		return nil, err
	}
	offlineInstance = &OfflineCA{
		authority:  auth,
		config:     cfg,
		configFile: configFile,
	}
	return offlineInstance, nil
}
func (c *OfflineCA) GetRootCAs() *x509.CertPool {
	return nil
}
func (c *OfflineCA) VerifyClientCert(certFile, keyFile string) error {
	cert, err := pemutil.ReadCertificate(certFile, pemutil.WithFirstBlock())
	if err != nil {
		return err
	}
	key, err := pemutil.Read(keyFile)
	if err != nil {
		return err
	}
	certPem, err := pemutil.Serialize(cert)
	if err != nil {
		return err
	}
	keyPem, err := pemutil.Serialize(key)
	if err != nil {
		return err
	}
	if _, err = tls.X509KeyPair(pem.EncodeToMemory(certPem), pem.EncodeToMemory(keyPem)); err != nil {
		return errors.Wrap(err, "error loading x509 key pair")
	}
	rootPool, err := x509util.ReadCertPool(c.Root())
	if err != nil {
		return err
	}
	intermediatePool, err := x509util.ReadCertPool(c.config.IntermediateCert)
	if err != nil {
		return err
	}
	opts := x509.VerifyOptions{
		Roots:         rootPool,
		Intermediates: intermediatePool,
	}
	if _, err = cert.Verify(opts); err != nil {
		return errors.Wrapf(err, "failed to verify certificate")
	}
	return nil
}
func (c *OfflineCA) Audience(tokType int) string {
	switch tokType {
	case RevokeType:
		return fmt.Sprintf("https://%s/revoke", c.config.DNSNames[0])
	case SSHUserSignType, SSHHostSignType:
		return fmt.Sprintf("https://%s/ssh/sign", c.config.DNSNames[0])
	case SSHRenewType:
		return fmt.Sprintf("https://%s/ssh/renew", c.config.DNSNames[0])
	case SSHRevokeType:
		return fmt.Sprintf("https://%s/ssh/revoke", c.config.DNSNames[0])
	case SSHRekeyType:
		return fmt.Sprintf("https://%s/ssh/rekey", c.config.DNSNames[0])
	default:
		return fmt.Sprintf("https://%s/sign", c.config.DNSNames[0])
	}
}
func (c *OfflineCA) CaURL() string {
	return fmt.Sprintf("https://%s", c.config.DNSNames[0])
}
func (c *OfflineCA) Root() string {
	return c.config.Root.First()
}
func (c *OfflineCA) Provisioners() provisioner.List {
	return c.config.AuthorityConfig.Provisioners
}
func certChainToPEM(certChain []*x509.Certificate) []api.Certificate {
	certChainPEM := make([]api.Certificate, 0, len(certChain))
	for _, c := range certChain {
		certChainPEM = append(certChainPEM, api.Certificate{Certificate: c})
	}
	return certChainPEM
}
func (c *OfflineCA) Version() (*api.VersionResponse, error) {
	v := c.authority.Version()
	return &api.VersionResponse{
		Version:                     v.Version,
		RequireClientAuthentication: v.RequireClientAuthentication,
	}, nil
}