github.com/brimstone/sbuca@v0.0.0-20151202175429-8691d9eba5c5/ca/ca.go (about)

     1  package ca
     2  
     3  import (
     4  	"strconv"
     5  	"time"
     6  	//"errors"
     7  	"crypto/rand"
     8  	"crypto/x509"
     9  	gopkix "crypto/x509/pkix"
    10  	"io/ioutil"
    11  	"math/big"
    12  	"os"
    13  	"strings"
    14  
    15  	"github.com/brimstone/sbuca/pkix"
    16  )
    17  
    18  type CA struct {
    19  	RootDir     string
    20  	CertStore   *CertStore
    21  	Certificate *pkix.Certificate
    22  	Key         *pkix.Key
    23  }
    24  
    25  func isPathNotExisted(path string) bool {
    26  	if _, err := os.Stat(path); os.IsNotExist(err) {
    27  		return true
    28  	}
    29  	return false
    30  }
    31  
    32  func NewCA(rootDir string) (*CA, error) {
    33  
    34  	// mkdir if needed
    35  	if isPathNotExisted(rootDir + "/ca") {
    36  		if err := os.MkdirAll(rootDir+"/ca", 0755); err != nil {
    37  			return nil, err
    38  		}
    39  	}
    40  
    41  	if isPathNotExisted(rootDir + "/certs") {
    42  		if err := os.MkdirAll(rootDir+"/certs", 0755); err != nil {
    43  			return nil, err
    44  		}
    45  	}
    46  
    47  	var key *pkix.Key
    48  	var certificate *pkix.Certificate
    49  	var err error
    50  	if isPathNotExisted(rootDir + "/ca/ca.key") {
    51  		// gen priv key
    52  		key, err = pkix.NewKey()
    53  		if err != nil {
    54  			return nil, err
    55  		}
    56  		if err := key.ToPEMFile(rootDir + "/ca/ca.key"); err != nil {
    57  			return nil, err
    58  		}
    59  
    60  		// gen self-signed cert
    61  		// should refactor, move to cert.go
    62  		notBefore := time.Now()
    63  		notAfter := notBefore.Add(time.Hour * 365 * 24)
    64  		keyUsage := x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature
    65  		extKeyUsage := []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
    66  		template := &x509.Certificate{
    67  			SerialNumber: big.NewInt(1),
    68  			Subject: gopkix.Name{
    69  				CommonName: "try.sbuca.com",
    70  			},
    71  			NotBefore:             notBefore,
    72  			NotAfter:              notAfter,
    73  			KeyUsage:              keyUsage,
    74  			ExtKeyUsage:           extKeyUsage,
    75  			BasicConstraintsValid: true,
    76  		}
    77  
    78  		derBytes, err := x509.CreateCertificate(rand.Reader, template, template, key.PublicKey, key.PrivateKey)
    79  		if err != nil {
    80  			return nil, err
    81  		}
    82  		certificate, err = pkix.NewCertificateFromDER(derBytes)
    83  		if err != nil {
    84  			return nil, err
    85  		}
    86  		if err := certificate.ToPEMFile(rootDir + "/ca/ca.crt"); err != nil {
    87  			return nil, err
    88  		}
    89  
    90  	} else {
    91  
    92  		certificate, err = pkix.NewCertificateFromPEMFile(rootDir + "/ca/ca.crt")
    93  		if err != nil {
    94  			return nil, err
    95  		}
    96  		key, err = pkix.NewKeyFromPrivateKeyPEMFile(rootDir + "/ca/ca.key")
    97  		if err != nil {
    98  			return nil, err
    99  		}
   100  
   101  	}
   102  
   103  	if isPathNotExisted(rootDir + "/ca/ca.srl") {
   104  		ioutil.WriteFile(rootDir+"/ca/ca.srl", []byte("2"), 0644)
   105  	}
   106  
   107  	certStore := NewCertStore(rootDir + "/certs")
   108  	newCA := &CA{
   109  		RootDir:     rootDir,
   110  		CertStore:   certStore,
   111  		Certificate: certificate,
   112  		Key:         key,
   113  	}
   114  
   115  	return newCA, nil
   116  }
   117  func (ca *CA) GetCertificate(id int64) (*pkix.Certificate, error) {
   118  	return ca.CertStore.Get(id)
   119  }
   120  func (ca *CA) PutCertificate(id int64, cert *pkix.Certificate) error {
   121  	return ca.CertStore.Put(id, cert)
   122  }
   123  func (ca *CA) GetSerialNumber() (*big.Int, error) {
   124  	snStr, err := ioutil.ReadFile(ca.RootDir + "/ca/ca.srl")
   125  	if err != nil {
   126  		panic(err)
   127  	}
   128  	snInt, err := strconv.Atoi(strings.Trim(string(snStr), "\n"))
   129  	if err != nil {
   130  		panic(err)
   131  	}
   132  	sn := big.NewInt(int64(snInt))
   133  
   134  	return sn, nil
   135  }
   136  func (ca *CA) IncreaseSerialNumber() error {
   137  	snStr, err := ioutil.ReadFile(ca.RootDir + "/ca/ca.srl")
   138  	if err != nil {
   139  		panic(err)
   140  	}
   141  	snInt, err := strconv.Atoi(strings.Trim(string(snStr), "\n"))
   142  	if err != nil {
   143  		panic(err)
   144  	}
   145  	nextSnInt := snInt + 1
   146  	nextSnStr := strconv.Itoa(nextSnInt) + "\n"
   147  	ioutil.WriteFile(ca.RootDir+"/ca/ca.srl", []byte(nextSnStr), 0600)
   148  
   149  	return nil
   150  }
   151  func (ca *CA) IssueCertificate(csr *pkix.CertificateRequest) (*pkix.Certificate, error) {
   152  
   153  	serialNumber, err := ca.GetSerialNumber()
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  	notBefore := time.Now()
   158  	notAfter := notBefore.Add(time.Hour * 365 * 24)
   159  	keyUsage := x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature
   160  	extKeyUsage := []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
   161  	template := &x509.Certificate{
   162  		SerialNumber:          serialNumber,
   163  		Subject:               csr.Csr.Subject,
   164  		NotBefore:             notBefore,
   165  		NotAfter:              notAfter,
   166  		KeyUsage:              keyUsage,
   167  		ExtKeyUsage:           extKeyUsage,
   168  		BasicConstraintsValid: true,
   169  	}
   170  
   171  	derBytes, err := x509.CreateCertificate(rand.Reader, template, ca.Certificate.Crt, ca.Key.PublicKey, ca.Key.PrivateKey)
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  
   176  	// increase sn
   177  	if err = ca.IncreaseSerialNumber(); err != nil {
   178  		return nil, err
   179  	}
   180  
   181  	// gen new cert
   182  	cert, err := pkix.NewCertificateFromDER(derBytes)
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  
   187  	// put in certstore
   188  	if err = ca.PutCertificate(serialNumber.Int64(), cert); err != nil {
   189  		return nil, err
   190  	}
   191  
   192  	return cert, nil
   193  }