1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
|
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"io/ioutil"
"os"
"github.com/micromdm/scep/v2/cryptoutil/x509util"
)
const (
csrPEMBlockType = "CERTIFICATE REQUEST"
)
type csrOptions struct {
cn, org, country, ou, locality, province, dnsName, challenge string
key *rsa.PrivateKey
}
func loadOrMakeCSR(path string, opts *csrOptions) (*x509.CertificateRequest, error) {
file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
if err != nil {
if os.IsExist(err) {
return loadCSRfromFile(path)
}
return nil, err
}
defer file.Close()
subject := pkix.Name{
CommonName: opts.cn,
Organization: subjOrNil(opts.org),
OrganizationalUnit: subjOrNil(opts.ou),
Province: subjOrNil(opts.province),
Locality: subjOrNil(opts.locality),
Country: subjOrNil(opts.country),
}
template := x509util.CertificateRequest{
CertificateRequest: x509.CertificateRequest{
Subject: subject,
SignatureAlgorithm: x509.SHA256WithRSA,
DNSNames: subjOrNil(opts.dnsName),
},
}
if opts.challenge != "" {
template.ChallengePassword = opts.challenge
}
derBytes, err := x509util.CreateCertificateRequest(rand.Reader, &template, opts.key)
pemBlock := &pem.Block{
Type: csrPEMBlockType,
Bytes: derBytes,
}
if err := pem.Encode(file, pemBlock); err != nil {
return nil, err
}
return x509.ParseCertificateRequest(derBytes)
}
// returns nil or []string{input} to populate pkix.Name.Subject
func subjOrNil(input string) []string {
if input == "" {
return nil
}
return []string{input}
}
// convert DER to PEM format
func pemCSR(derBytes []byte) []byte {
pemBlock := &pem.Block{
Type: csrPEMBlockType,
Headers: nil,
Bytes: derBytes,
}
return pem.EncodeToMemory(pemBlock)
}
// load PEM encoded CSR from file
func loadCSRfromFile(path string) (*x509.CertificateRequest, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
pemBlock, _ := pem.Decode(data)
if pemBlock == nil {
return nil, errors.New("cannot find the next PEM formatted block")
}
if pemBlock.Type != csrPEMBlockType || len(pemBlock.Headers) != 0 {
return nil, errors.New("unmatched type or headers")
}
return x509.ParseCertificateRequest(pemBlock.Bytes)
}
|