1package connect 2 3import ( 4 "bytes" 5 "crypto" 6 "crypto/rand" 7 "crypto/rsa" 8 "crypto/x509" 9 "crypto/x509/pkix" 10 "encoding/asn1" 11 "encoding/pem" 12 "net" 13 "net/url" 14) 15 16// SigAlgoForKey returns the preferred x509.SignatureAlgorithm for a given key 17// based on it's type. If the key type is not supported we return 18// ECDSAWithSHA256 on the basis that it will fail anyway and we've already type 19// checked keys by the time we call this in general. 20func SigAlgoForKey(key crypto.Signer) x509.SignatureAlgorithm { 21 if _, ok := key.(*rsa.PrivateKey); ok { 22 return x509.SHA256WithRSA 23 } 24 // We default to ECDSA but don't bother detecting invalid key types as we do 25 // that in lots of other places and it will fail anyway if we try to sign with 26 // an incompatible type. 27 return x509.ECDSAWithSHA256 28} 29 30// SigAlgoForKeyType returns the preferred x509.SignatureAlgorithm for a given 31// key type string from configuration or an existing cert. If the key type is 32// not supported we return ECDSAWithSHA256 on the basis that it will fail anyway 33// and we've already type checked config by the time we call this in general. 34func SigAlgoForKeyType(keyType string) x509.SignatureAlgorithm { 35 switch keyType { 36 case "rsa": 37 return x509.SHA256WithRSA 38 case "ec": 39 fallthrough 40 default: 41 return x509.ECDSAWithSHA256 42 } 43} 44 45// CreateCSR returns a CSR to sign the given service with SAN entries 46// along with the PEM-encoded private key for this certificate. 47func CreateCSR(uri CertURI, commonName string, privateKey crypto.Signer, 48 dnsNames []string, ipAddresses []net.IP, extensions ...pkix.Extension) (string, error) { 49 template := &x509.CertificateRequest{ 50 URIs: []*url.URL{uri.URI()}, 51 SignatureAlgorithm: SigAlgoForKey(privateKey), 52 ExtraExtensions: extensions, 53 Subject: pkix.Name{CommonName: commonName}, 54 DNSNames: dnsNames, 55 IPAddresses: ipAddresses, 56 } 57 58 // Create the CSR itself 59 var csrBuf bytes.Buffer 60 bs, err := x509.CreateCertificateRequest(rand.Reader, template, privateKey) 61 if err != nil { 62 return "", err 63 } 64 65 err = pem.Encode(&csrBuf, &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: bs}) 66 if err != nil { 67 return "", err 68 } 69 70 return csrBuf.String(), nil 71} 72 73// CreateCSR returns a CA CSR to sign the given service along with the PEM-encoded 74// private key for this certificate. 75func CreateCACSR(uri CertURI, commonName string, privateKey crypto.Signer) (string, error) { 76 ext, err := CreateCAExtension() 77 if err != nil { 78 return "", err 79 } 80 81 return CreateCSR(uri, commonName, privateKey, nil, nil, ext) 82} 83 84// CreateCAExtension creates a pkix.Extension for the x509 Basic Constraints 85// IsCA field () 86func CreateCAExtension() (pkix.Extension, error) { 87 type basicConstraints struct { 88 IsCA bool `asn1:"optional"` 89 MaxPathLen int `asn1:"optional"` 90 } 91 basicCon := basicConstraints{IsCA: true, MaxPathLen: 0} 92 bitstr, err := asn1.Marshal(basicCon) 93 if err != nil { 94 return pkix.Extension{}, err 95 } 96 97 return pkix.Extension{ 98 Id: []int{2, 5, 29, 19}, // from x509 package 99 Critical: true, 100 Value: bitstr, 101 }, nil 102} 103