cert.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /*
  2. Copyright 2014 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package cert
  14. import (
  15. "bytes"
  16. "crypto"
  17. cryptorand "crypto/rand"
  18. "crypto/rsa"
  19. "crypto/x509"
  20. "crypto/x509/pkix"
  21. "encoding/pem"
  22. "fmt"
  23. "math"
  24. "math/big"
  25. "net"
  26. "os"
  27. "path/filepath"
  28. "strings"
  29. "time"
  30. "k8s.io/client-go/util/keyutil"
  31. netutils "k8s.io/utils/net"
  32. )
  33. const duration365d = time.Hour * 24 * 365
  34. // Config contains the basic fields required for creating a certificate
  35. type Config struct {
  36. CommonName string
  37. Organization []string
  38. AltNames AltNames
  39. Usages []x509.ExtKeyUsage
  40. NotBefore time.Time
  41. }
  42. // AltNames contains the domain names and IP addresses that will be added
  43. // to the API Server's x509 certificate SubAltNames field. The values will
  44. // be passed directly to the x509.Certificate object.
  45. type AltNames struct {
  46. DNSNames []string
  47. IPs []net.IP
  48. }
  49. // NewSelfSignedCACert creates a CA certificate
  50. func NewSelfSignedCACert(cfg Config, key crypto.Signer) (*x509.Certificate, error) {
  51. now := time.Now()
  52. // returns a uniform random value in [0, max-1), then add 1 to serial to make it a uniform random value in [1, max).
  53. serial, err := cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64-1))
  54. if err != nil {
  55. return nil, err
  56. }
  57. serial = new(big.Int).Add(serial, big.NewInt(1))
  58. notBefore := now.UTC()
  59. if !cfg.NotBefore.IsZero() {
  60. notBefore = cfg.NotBefore.UTC()
  61. }
  62. tmpl := x509.Certificate{
  63. SerialNumber: serial,
  64. Subject: pkix.Name{
  65. CommonName: cfg.CommonName,
  66. Organization: cfg.Organization,
  67. },
  68. DNSNames: []string{cfg.CommonName},
  69. NotBefore: notBefore,
  70. NotAfter: now.Add(duration365d * 10).UTC(),
  71. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
  72. BasicConstraintsValid: true,
  73. IsCA: true,
  74. }
  75. certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &tmpl, &tmpl, key.Public(), key)
  76. if err != nil {
  77. return nil, err
  78. }
  79. return x509.ParseCertificate(certDERBytes)
  80. }
  81. // GenerateSelfSignedCertKey creates a self-signed certificate and key for the given host.
  82. // Host may be an IP or a DNS name
  83. // You may also specify additional subject alt names (either ip or dns names) for the certificate.
  84. func GenerateSelfSignedCertKey(host string, alternateIPs []net.IP, alternateDNS []string) ([]byte, []byte, error) {
  85. return GenerateSelfSignedCertKeyWithFixtures(host, alternateIPs, alternateDNS, "")
  86. }
  87. // GenerateSelfSignedCertKeyWithFixtures creates a self-signed certificate and key for the given host.
  88. // Host may be an IP or a DNS name. You may also specify additional subject alt names (either ip or dns names)
  89. // for the certificate.
  90. //
  91. // If fixtureDirectory is non-empty, it is a directory path which can contain pre-generated certs. The format is:
  92. // <host>_<ip>-<ip>_<alternateDNS>-<alternateDNS>.crt
  93. // <host>_<ip>-<ip>_<alternateDNS>-<alternateDNS>.key
  94. // Certs/keys not existing in that directory are created.
  95. func GenerateSelfSignedCertKeyWithFixtures(host string, alternateIPs []net.IP, alternateDNS []string, fixtureDirectory string) ([]byte, []byte, error) {
  96. validFrom := time.Now().Add(-time.Hour) // valid an hour earlier to avoid flakes due to clock skew
  97. maxAge := time.Hour * 24 * 365 // one year self-signed certs
  98. baseName := fmt.Sprintf("%s_%s_%s", host, strings.Join(ipsToStrings(alternateIPs), "-"), strings.Join(alternateDNS, "-"))
  99. certFixturePath := filepath.Join(fixtureDirectory, baseName+".crt")
  100. keyFixturePath := filepath.Join(fixtureDirectory, baseName+".key")
  101. if len(fixtureDirectory) > 0 {
  102. cert, err := os.ReadFile(certFixturePath)
  103. if err == nil {
  104. key, err := os.ReadFile(keyFixturePath)
  105. if err == nil {
  106. return cert, key, nil
  107. }
  108. return nil, nil, fmt.Errorf("cert %s can be read, but key %s cannot: %v", certFixturePath, keyFixturePath, err)
  109. }
  110. maxAge = 100 * time.Hour * 24 * 365 // 100 years fixtures
  111. }
  112. caKey, err := rsa.GenerateKey(cryptorand.Reader, 2048)
  113. if err != nil {
  114. return nil, nil, err
  115. }
  116. // returns a uniform random value in [0, max-1), then add 1 to serial to make it a uniform random value in [1, max).
  117. serial, err := cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64-1))
  118. if err != nil {
  119. return nil, nil, err
  120. }
  121. serial = new(big.Int).Add(serial, big.NewInt(1))
  122. caTemplate := x509.Certificate{
  123. SerialNumber: serial,
  124. Subject: pkix.Name{
  125. CommonName: fmt.Sprintf("%s-ca@%d", host, time.Now().Unix()),
  126. },
  127. NotBefore: validFrom,
  128. NotAfter: validFrom.Add(maxAge),
  129. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
  130. BasicConstraintsValid: true,
  131. IsCA: true,
  132. }
  133. caDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &caTemplate, &caTemplate, &caKey.PublicKey, caKey)
  134. if err != nil {
  135. return nil, nil, err
  136. }
  137. caCertificate, err := x509.ParseCertificate(caDERBytes)
  138. if err != nil {
  139. return nil, nil, err
  140. }
  141. priv, err := rsa.GenerateKey(cryptorand.Reader, 2048)
  142. if err != nil {
  143. return nil, nil, err
  144. }
  145. // returns a uniform random value in [0, max-1), then add 1 to serial to make it a uniform random value in [1, max).
  146. serial, err = cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64-1))
  147. if err != nil {
  148. return nil, nil, err
  149. }
  150. serial = new(big.Int).Add(serial, big.NewInt(1))
  151. template := x509.Certificate{
  152. SerialNumber: serial,
  153. Subject: pkix.Name{
  154. CommonName: fmt.Sprintf("%s@%d", host, time.Now().Unix()),
  155. },
  156. NotBefore: validFrom,
  157. NotAfter: validFrom.Add(maxAge),
  158. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
  159. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
  160. BasicConstraintsValid: true,
  161. }
  162. if ip := netutils.ParseIPSloppy(host); ip != nil {
  163. template.IPAddresses = append(template.IPAddresses, ip)
  164. } else {
  165. template.DNSNames = append(template.DNSNames, host)
  166. }
  167. template.IPAddresses = append(template.IPAddresses, alternateIPs...)
  168. template.DNSNames = append(template.DNSNames, alternateDNS...)
  169. derBytes, err := x509.CreateCertificate(cryptorand.Reader, &template, caCertificate, &priv.PublicKey, caKey)
  170. if err != nil {
  171. return nil, nil, err
  172. }
  173. // Generate cert, followed by ca
  174. certBuffer := bytes.Buffer{}
  175. if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: derBytes}); err != nil {
  176. return nil, nil, err
  177. }
  178. if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: caDERBytes}); err != nil {
  179. return nil, nil, err
  180. }
  181. // Generate key
  182. keyBuffer := bytes.Buffer{}
  183. if err := pem.Encode(&keyBuffer, &pem.Block{Type: keyutil.RSAPrivateKeyBlockType, Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil {
  184. return nil, nil, err
  185. }
  186. if len(fixtureDirectory) > 0 {
  187. if err := os.WriteFile(certFixturePath, certBuffer.Bytes(), 0644); err != nil {
  188. return nil, nil, fmt.Errorf("failed to write cert fixture to %s: %v", certFixturePath, err)
  189. }
  190. if err := os.WriteFile(keyFixturePath, keyBuffer.Bytes(), 0600); err != nil {
  191. return nil, nil, fmt.Errorf("failed to write key fixture to %s: %v", certFixturePath, err)
  192. }
  193. }
  194. return certBuffer.Bytes(), keyBuffer.Bytes(), nil
  195. }
  196. func ipsToStrings(ips []net.IP) []string {
  197. ss := make([]string, 0, len(ips))
  198. for _, ip := range ips {
  199. ss = append(ss, ip.String())
  200. }
  201. return ss
  202. }