package acme import ( "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "encoding/base64" "encoding/pem" "errors" "io" "io/ioutil" "log" "os" ) const ( pemRSA = `RSA PRIVATE KEY` pemEC = `EC PRIVATE KEY` pemCRT = `CERTIFICATE` pemCSR = `CERTIFICATE REQUEST` ) var ( ErrKeyType = errors.New("unknown key type") ErrKeySize = errors.New("insufficient key size") ErrValues = errors.New("domain(s) and email required") ) func SaveCSR(w io.Writer, csr []byte) error { block := &pem.Block{Type: pemCSR, Bytes: csr} return pem.Encode(w, block) } func SaveKey(w io.Writer, key crypto.PrivateKey) error { var block *pem.Block switch k := key.(type) { case *rsa.PrivateKey: der := x509.MarshalPKCS1PrivateKey(k) block = &pem.Block{Type: pemRSA, Bytes: der} case *ecdsa.PrivateKey: der, err := x509.MarshalECPrivateKey(k) if err != nil { return err } block = &pem.Block{Type: pemEC, Bytes: der} } return pem.Encode(w, block) } func LoadKeyFile(fname string) (crypto.PrivateKey, error) { fd, err := os.Open(fname) if err != nil { return nil, err } defer fd.Close() return LoadKey(fd) } func LoadKey(r io.Reader) (crypto.PrivateKey, error) { der, err := ioutil.ReadAll(r) if err != nil { return nil, err } block, _ := pem.Decode(der) switch block.Type { case pemRSA: return x509.ParsePKCS1PrivateKey(block.Bytes) case pemEC: return x509.ParseECPrivateKey(block.Bytes) default: log.Println("LoadKey") return nil, ErrKeyType } } func SaveCert(w io.Writer, cert []byte) error { block := &pem.Block{Type: pemCRT, Bytes: cert} return pem.Encode(w, block) } func LoadCertFile(fname string) ([]*x509.Certificate, error) { fd, err := os.Open(fname) if err != nil { return nil, err } defer fd.Close() return LoadCerts(fd) } func LoadCerts(r io.Reader) ([]*x509.Certificate, error) { der, err := ioutil.ReadAll(r) if err != nil { return nil, err } block, _ := pem.Decode(der) return x509.ParseCertificates(block.Bytes) } // GetMail returns emailAddress embedded in certificate func GetMail(cert *x509.Certificate) string { for _, n := range cert.Subject.Names { if n.Type.Equal(oidMailAddress) { return n.Value.(string) } } return "" } // NewKey generates a new private key, supported keysizes are: // EC keys: 224, 256, 384, 521 // RSA keys: 1024, 1536, 2048, 4096, 8192 // Default key: 2048 RSA (when size of 0 is provided) func NewKey(size int) (crypto.PrivateKey, error) { switch size { case 224: return ecdsa.GenerateKey(elliptic.P224(), rand.Reader) case 256: return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) case 384: return ecdsa.GenerateKey(elliptic.P384(), rand.Reader) case 521: return ecdsa.GenerateKey(elliptic.P521(), rand.Reader) case 1024, 1536, 2048, 4096, 8192: return rsa.GenerateKey(rand.Reader, size) case 0: return rsa.GenerateKey(rand.Reader, 2048) default: log.Println("NewKey") return nil, ErrKeySize } } var oidMailAddress = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1} func NewCSR(key crypto.PrivateKey, altnames []string, email string) (string, error) { if len(altnames) < 1 || email == "" { return "", ErrValues } tmpl := x509.CertificateRequest{ Subject: pkix.Name{ ExtraNames: []pkix.AttributeTypeAndValue{ {Type: oidMailAddress, Value: email}, }, }, DNSNames: altnames, } der, err := x509.CreateCertificateRequest(rand.Reader, &tmpl, key) if err != nil { return "", err } return base64.RawURLEncoding.EncodeToString(der), nil }