cert.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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. NotBefore: notBefore,
  69. NotAfter: now.Add(duration365d * 10).UTC(),
  70. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
  71. BasicConstraintsValid: true,
  72. IsCA: true,
  73. }
  74. if len(cfg.CommonName) > 0 {
  75. tmpl.DNSNames = []string{cfg.CommonName}
  76. }
  77. certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &tmpl, &tmpl, key.Public(), key)
  78. if err != nil {
  79. return nil, err
  80. }
  81. return x509.ParseCertificate(certDERBytes)
  82. }
  83. // SelfSignedCertKeyOptions contains configuration parameters for generating self-signed certificates.
  84. type SelfSignedCertKeyOptions struct {
  85. // Host is required, and identifies the host of the serving certificate. Can be a DNS name or IP address.
  86. Host string
  87. // AlternateIPs is optional, and identifies additional IPs the serving certificate should be valid for.
  88. AlternateIPs []net.IP
  89. // AlternateDNS is optional, and identifies additional DNS names the serving certificate should be valid for.
  90. AlternateDNS []string
  91. // MaxAge controls the duration of the issued certificate.
  92. // Defaults to 1 year if unset.
  93. // Ignored if FixtureDirectory is set.
  94. MaxAge time.Duration
  95. // FixtureDirectory is intended for use in tests.
  96. // If non-empty, it is a directory path which can contain pre-generated certs. The format is:
  97. // <host>_<ip>-<ip>_<alternateDNS>-<alternateDNS>.crt
  98. // <host>_<ip>-<ip>_<alternateDNS>-<alternateDNS>.key
  99. // Certs/keys not existing in that directory are created with a duration of 100 years.
  100. FixtureDirectory string
  101. }
  102. // GenerateSelfSignedCertKey creates a self-signed certificate and key for the given host.
  103. // Host may be an IP or a DNS name
  104. // You may also specify additional subject alt names (either ip or dns names) for the certificate.
  105. func GenerateSelfSignedCertKey(host string, alternateIPs []net.IP, alternateDNS []string) ([]byte, []byte, error) {
  106. return GenerateSelfSignedCertKeyWithOptions(SelfSignedCertKeyOptions{
  107. Host: host,
  108. AlternateIPs: alternateIPs,
  109. AlternateDNS: alternateDNS,
  110. })
  111. }
  112. // GenerateSelfSignedCertKeyWithFixtures creates a self-signed certificate and key for the given host.
  113. // Host may be an IP or a DNS name. You may also specify additional subject alt names (either ip or dns names)
  114. // for the certificate.
  115. //
  116. // If fixtureDirectory is non-empty, it is a directory path which can contain pre-generated certs. The format is:
  117. // <host>_<ip>-<ip>_<alternateDNS>-<alternateDNS>.crt
  118. // <host>_<ip>-<ip>_<alternateDNS>-<alternateDNS>.key
  119. // Certs/keys not existing in that directory are created.
  120. func GenerateSelfSignedCertKeyWithFixtures(host string, alternateIPs []net.IP, alternateDNS []string, fixtureDirectory string) ([]byte, []byte, error) {
  121. return GenerateSelfSignedCertKeyWithOptions(SelfSignedCertKeyOptions{
  122. Host: host,
  123. AlternateIPs: alternateIPs,
  124. AlternateDNS: alternateDNS,
  125. FixtureDirectory: fixtureDirectory,
  126. })
  127. }
  128. // GenerateSelfSignedCertKeyWithOptions generates a self-signed certificate and key based on the provided options.
  129. func GenerateSelfSignedCertKeyWithOptions(opts SelfSignedCertKeyOptions) ([]byte, []byte, error) {
  130. host := opts.Host
  131. alternateIPs := opts.AlternateIPs
  132. alternateDNS := opts.AlternateDNS
  133. fixtureDirectory := opts.FixtureDirectory
  134. maxAge := opts.MaxAge
  135. if maxAge == 0 {
  136. maxAge = 365 * 24 * time.Hour
  137. }
  138. validFrom := time.Now().Add(-time.Hour) // valid an hour earlier to avoid flakes due to clock skew
  139. baseName := fmt.Sprintf("%s_%s_%s", host, strings.Join(ipsToStrings(alternateIPs), "-"), strings.Join(alternateDNS, "-"))
  140. certFixturePath := filepath.Join(fixtureDirectory, baseName+".crt")
  141. keyFixturePath := filepath.Join(fixtureDirectory, baseName+".key")
  142. if len(fixtureDirectory) > 0 {
  143. cert, err := os.ReadFile(certFixturePath)
  144. if err == nil {
  145. key, err := os.ReadFile(keyFixturePath)
  146. if err == nil {
  147. return cert, key, nil
  148. }
  149. return nil, nil, fmt.Errorf("cert %s can be read, but key %s cannot: %v", certFixturePath, keyFixturePath, err)
  150. }
  151. maxAge = 100 * time.Hour * 24 * 365 // 100 years fixtures
  152. }
  153. caKey, err := rsa.GenerateKey(cryptorand.Reader, 2048)
  154. if err != nil {
  155. return nil, nil, err
  156. }
  157. // returns a uniform random value in [0, max-1), then add 1 to serial to make it a uniform random value in [1, max).
  158. serial, err := cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64-1))
  159. if err != nil {
  160. return nil, nil, err
  161. }
  162. serial = new(big.Int).Add(serial, big.NewInt(1))
  163. caTemplate := x509.Certificate{
  164. SerialNumber: serial,
  165. Subject: pkix.Name{
  166. CommonName: fmt.Sprintf("%s-ca@%d", host, time.Now().Unix()),
  167. },
  168. NotBefore: validFrom,
  169. NotAfter: validFrom.Add(maxAge),
  170. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
  171. BasicConstraintsValid: true,
  172. IsCA: true,
  173. }
  174. caDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &caTemplate, &caTemplate, &caKey.PublicKey, caKey)
  175. if err != nil {
  176. return nil, nil, err
  177. }
  178. caCertificate, err := x509.ParseCertificate(caDERBytes)
  179. if err != nil {
  180. return nil, nil, err
  181. }
  182. priv, err := rsa.GenerateKey(cryptorand.Reader, 2048)
  183. if err != nil {
  184. return nil, nil, err
  185. }
  186. // returns a uniform random value in [0, max-1), then add 1 to serial to make it a uniform random value in [1, max).
  187. serial, err = cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64-1))
  188. if err != nil {
  189. return nil, nil, err
  190. }
  191. serial = new(big.Int).Add(serial, big.NewInt(1))
  192. template := x509.Certificate{
  193. SerialNumber: serial,
  194. Subject: pkix.Name{
  195. CommonName: fmt.Sprintf("%s@%d", host, time.Now().Unix()),
  196. },
  197. NotBefore: validFrom,
  198. NotAfter: validFrom.Add(maxAge),
  199. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
  200. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
  201. BasicConstraintsValid: true,
  202. }
  203. if ip := netutils.ParseIPSloppy(host); ip != nil {
  204. template.IPAddresses = append(template.IPAddresses, ip)
  205. } else {
  206. template.DNSNames = append(template.DNSNames, host)
  207. }
  208. template.IPAddresses = append(template.IPAddresses, alternateIPs...)
  209. template.DNSNames = append(template.DNSNames, alternateDNS...)
  210. derBytes, err := x509.CreateCertificate(cryptorand.Reader, &template, caCertificate, &priv.PublicKey, caKey)
  211. if err != nil {
  212. return nil, nil, err
  213. }
  214. // Generate cert, followed by ca
  215. certBuffer := bytes.Buffer{}
  216. if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: derBytes}); err != nil {
  217. return nil, nil, err
  218. }
  219. if err := pem.Encode(&certBuffer, &pem.Block{Type: CertificateBlockType, Bytes: caDERBytes}); err != nil {
  220. return nil, nil, err
  221. }
  222. // Generate key
  223. keyBuffer := bytes.Buffer{}
  224. if err := pem.Encode(&keyBuffer, &pem.Block{Type: keyutil.RSAPrivateKeyBlockType, Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil {
  225. return nil, nil, err
  226. }
  227. if len(fixtureDirectory) > 0 {
  228. if err := os.WriteFile(certFixturePath, certBuffer.Bytes(), 0644); err != nil {
  229. return nil, nil, fmt.Errorf("failed to write cert fixture to %s: %v", certFixturePath, err)
  230. }
  231. if err := os.WriteFile(keyFixturePath, keyBuffer.Bytes(), 0600); err != nil {
  232. return nil, nil, fmt.Errorf("failed to write key fixture to %s: %v", certFixturePath, err)
  233. }
  234. }
  235. return certBuffer.Bytes(), keyBuffer.Bytes(), nil
  236. }
  237. func ipsToStrings(ips []net.IP) []string {
  238. ss := make([]string, 0, len(ips))
  239. for _, ip := range ips {
  240. ss = append(ss, ip.String())
  241. }
  242. return ss
  243. }