pki_helpers.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. // Copyright 2019 Yunion
  2. //
  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. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /*
  15. Copyright 2018 The Kubernetes Authors.
  16. Licensed under the Apache License, Version 2.0 (the "License");
  17. you may not use this file except in compliance with the License.
  18. You may obtain a copy of the License at
  19. http://www.apache.org/licenses/LICENSE-2.0
  20. Unless required by applicable law or agreed to in writing, software
  21. distributed under the License is distributed on an "AS IS" BASIS,
  22. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  23. See the License for the specific language governing permissions and
  24. limitations under the License.
  25. */
  26. package pki
  27. import (
  28. "crypto"
  29. "crypto/ecdsa"
  30. "crypto/elliptic"
  31. cryptorand "crypto/rand"
  32. "crypto/rsa"
  33. "crypto/x509"
  34. "crypto/x509/pkix"
  35. "encoding/pem"
  36. "fmt"
  37. "io/ioutil"
  38. "math"
  39. "math/big"
  40. "net"
  41. "os"
  42. "path/filepath"
  43. "time"
  44. "yunion.io/x/pkg/errors"
  45. "yunion.io/x/pkg/util/sets"
  46. certutil "yunion.io/x/onecloud/pkg/util/tls/cert"
  47. keyutil "yunion.io/x/onecloud/pkg/util/tls/key"
  48. )
  49. const (
  50. // PrivateKeyBlockType is a possible value for pem.Block.Type.
  51. PrivateKeyBlockType = "PRIVATE KEY"
  52. // PublicKeyBlockType is a possible value for pem.Block.Type.
  53. PublicKeyBlockType = "PUBLIC KEY"
  54. // CertificateBlockType is a possible value for pem.Block.Type.
  55. CertificateBlockType = "CERTIFICATE"
  56. // RSAPrivateKeyBlockType is a possible value for pem.BlockType.
  57. RSAPrivateKeyBlockType = "RSA PRIVATE KEY"
  58. rsaKeySize = 2048
  59. )
  60. var (
  61. CertificateValidity = time.Hour * 24 * 365 * 100
  62. )
  63. // CertConfig is a wrapper around certutil.Config extending it with PublicKeyAlgorithm.
  64. type CertConfig struct {
  65. certutil.Config
  66. PublicKeyAlgorithm x509.PublicKeyAlgorithm
  67. }
  68. // NewCertificateAuthority creates new certificate and private key for the certificate authority
  69. func NewCertificateAuthority(config *CertConfig) (*x509.Certificate, crypto.Signer, error) {
  70. key, err := NewPrivateKey(config.PublicKeyAlgorithm)
  71. if err != nil {
  72. return nil, nil, errors.Wrap(err, "unable to create private key while generating CA certificate")
  73. }
  74. cert, err := certutil.NewSelfSignedCACert(config.Config, key)
  75. if err != nil {
  76. return nil, nil, errors.Wrap(err, "unable to create self-signed CA certificate")
  77. }
  78. return cert, key, nil
  79. }
  80. // NewCertAndKey creates new certificate and key by passing the certificate authority certificate and key
  81. func NewCertAndKey(caCert *x509.Certificate, caKey crypto.Signer, config *CertConfig) (*x509.Certificate, crypto.Signer, error) {
  82. key, err := NewPrivateKey(config.PublicKeyAlgorithm)
  83. if err != nil {
  84. return nil, nil, errors.Wrap(err, "unable to create private key")
  85. }
  86. cert, err := NewSignedCert(config, key, caCert, caKey)
  87. if err != nil {
  88. return nil, nil, errors.Wrap(err, "unable to sign certificate")
  89. }
  90. return cert, key, nil
  91. }
  92. // NewCSR creates a new CSR
  93. func NewCSR(cfg CertConfig, key crypto.Signer) (*x509.CertificateRequest, error) {
  94. template := &x509.CertificateRequest{
  95. Subject: pkix.Name{
  96. CommonName: cfg.CommonName,
  97. Organization: cfg.Organization,
  98. },
  99. DNSNames: cfg.AltNames.DNSNames,
  100. IPAddresses: cfg.AltNames.IPs,
  101. }
  102. csrBytes, err := x509.CreateCertificateRequest(cryptorand.Reader, template, key)
  103. if err != nil {
  104. return nil, errors.Wrap(err, "failed to create a CSR")
  105. }
  106. return x509.ParseCertificateRequest(csrBytes)
  107. }
  108. // NewCSRAndKey generates a new key and CSR and that could be signed to create the given certificate
  109. func NewCSRAndKey(config *CertConfig) (*x509.CertificateRequest, crypto.Signer, error) {
  110. key, err := NewPrivateKey(config.PublicKeyAlgorithm)
  111. if err != nil {
  112. return nil, nil, errors.Wrap(err, "unable to create private key")
  113. }
  114. csr, err := NewCSR(*config, key)
  115. if err != nil {
  116. return nil, nil, errors.Wrap(err, "unable to generate CSR")
  117. }
  118. return csr, key, nil
  119. }
  120. // HasServerAuth returns true if the given certificate is a ServerAuth
  121. func HasServerAuth(cert *x509.Certificate) bool {
  122. for i := range cert.ExtKeyUsage {
  123. if cert.ExtKeyUsage[i] == x509.ExtKeyUsageServerAuth {
  124. return true
  125. }
  126. }
  127. return false
  128. }
  129. // WriteCertAndKey stores certificate and key at the specified location
  130. func WriteCertAndKey(pkiPath string, name string, cert *x509.Certificate, key crypto.Signer) error {
  131. if err := WriteKey(pkiPath, name, key); err != nil {
  132. return errors.Wrap(err, "couldn't write key")
  133. }
  134. return WriteCert(pkiPath, name, cert)
  135. }
  136. // WriteCert stores the given certificate at the given location
  137. func WriteCert(pkiPath, name string, cert *x509.Certificate) error {
  138. if cert == nil {
  139. return errors.Error("certificate cannot be nil when writing to file")
  140. }
  141. certificatePath := pathForCert(pkiPath, name)
  142. if err := certutil.WriteCert(certificatePath, EncodeCertPEM(cert)); err != nil {
  143. return errors.Wrapf(err, "unable to write certificate to file %s", certificatePath)
  144. }
  145. return nil
  146. }
  147. // WriteKey stores the given key at the given location
  148. func WriteKey(pkiPath, name string, key crypto.Signer) error {
  149. if key == nil {
  150. return errors.Error("private key cannot be nil when writing to file")
  151. }
  152. privateKeyPath := pathForKey(pkiPath, name)
  153. encoded, err := keyutil.MarshalPrivateKeyToPEM(key)
  154. if err != nil {
  155. return errors.Wrapf(err, "unable to marshal private key to PEM")
  156. }
  157. if err := keyutil.WriteKey(privateKeyPath, encoded); err != nil {
  158. return errors.Wrapf(err, "unable to write private key to file %s", privateKeyPath)
  159. }
  160. return nil
  161. }
  162. // WriteCSR writes the pem-encoded CSR data to csrPath.
  163. // The CSR file will be created with file mode 0600.
  164. // If the CSR file already exists, it will be overwritten.
  165. // The parent directory of the csrPath will be created as needed with file mode 0700.
  166. func WriteCSR(csrDir, name string, csr *x509.CertificateRequest) error {
  167. if csr == nil {
  168. return errors.Error("certificate request cannot be nil when writing to file")
  169. }
  170. csrPath := pathForCSR(csrDir, name)
  171. if err := os.MkdirAll(filepath.Dir(csrPath), os.FileMode(0700)); err != nil {
  172. return errors.Wrapf(err, "failed to make directory %s", filepath.Dir(csrPath))
  173. }
  174. if err := ioutil.WriteFile(csrPath, EncodeCSRPEM(csr), os.FileMode(0600)); err != nil {
  175. return errors.Wrapf(err, "unable to write CSR to file %s", csrPath)
  176. }
  177. return nil
  178. }
  179. // WritePublicKey stores the given public key at the given location
  180. func WritePublicKey(pkiPath, name string, key crypto.PublicKey) error {
  181. if key == nil {
  182. return errors.Error("public key cannot be nil when writing to file")
  183. }
  184. publicKeyBytes, err := EncodePublicKeyPEM(key)
  185. if err != nil {
  186. return err
  187. }
  188. publicKeyPath := pathForPublicKey(pkiPath, name)
  189. if err := keyutil.WriteKey(publicKeyPath, publicKeyBytes); err != nil {
  190. return errors.Wrapf(err, "unable to write public key to file %s", publicKeyPath)
  191. }
  192. return nil
  193. }
  194. // TryLoadCertAndKeyFromDisk tries to load a cert and a key from the disk and validates that they are valid
  195. func TryLoadCertAndKeyFromDisk(pkiPath, name string) (*x509.Certificate, crypto.Signer, error) {
  196. cert, err := TryLoadCertFromDisk(pkiPath, name)
  197. if err != nil {
  198. return nil, nil, errors.Wrap(err, "failed to load certificate")
  199. }
  200. key, err := TryLoadKeyFromDisk(pkiPath, name)
  201. if err != nil {
  202. return nil, nil, errors.Wrap(err, "failed to load key")
  203. }
  204. return cert, key, nil
  205. }
  206. // TryLoadCertFromDisk tries to load the cert from the disk and validates that it is valid
  207. func TryLoadCertFromDisk(pkiPath, name string) (*x509.Certificate, error) {
  208. certificatePath := pathForCert(pkiPath, name)
  209. certs, err := certutil.CertsFromFile(certificatePath)
  210. if err != nil {
  211. return nil, errors.Wrapf(err, "couldn't load the certificate file %s", certificatePath)
  212. }
  213. // We are only putting one certificate in the certificate pem file, so it's safe to just pick the first one
  214. // TODO: Support multiple certs here in order to be able to rotate certs
  215. cert := certs[0]
  216. // Check so that the certificate is valid now
  217. now := time.Now()
  218. if now.Before(cert.NotBefore) {
  219. return nil, errors.Error("the certificate is not valid yet")
  220. }
  221. if now.After(cert.NotAfter) {
  222. return nil, errors.Error("the certificate has expired")
  223. }
  224. return cert, nil
  225. }
  226. // TryLoadKeyFromDisk tries to load the key from the disk and validates that it is valid
  227. func TryLoadKeyFromDisk(pkiPath, name string) (crypto.Signer, error) {
  228. privateKeyPath := pathForKey(pkiPath, name)
  229. // Parse the private key from a file
  230. privKey, err := keyutil.PrivateKeyFromFile(privateKeyPath)
  231. if err != nil {
  232. return nil, errors.Wrapf(err, "couldn't load the private key file %s", privateKeyPath)
  233. }
  234. // Allow RSA and ECDSA formats only
  235. var key crypto.Signer
  236. switch k := privKey.(type) {
  237. case *rsa.PrivateKey:
  238. key = k
  239. case *ecdsa.PrivateKey:
  240. key = k
  241. default:
  242. return nil, errors.Errorf("the private key file %s is neither in RSA nor ECDSA format", privateKeyPath)
  243. }
  244. return key, nil
  245. }
  246. // TryLoadCSRAndKeyFromDisk tries to load the CSR and key from the disk
  247. func TryLoadCSRAndKeyFromDisk(pkiPath, name string) (*x509.CertificateRequest, crypto.Signer, error) {
  248. csr, err := TryLoadCSRFromDisk(pkiPath, name)
  249. if err != nil {
  250. return nil, nil, errors.Wrap(err, "could not load CSR file")
  251. }
  252. key, err := TryLoadKeyFromDisk(pkiPath, name)
  253. if err != nil {
  254. return nil, nil, errors.Wrap(err, "could not load key file")
  255. }
  256. return csr, key, nil
  257. }
  258. // TryLoadPrivatePublicKeyFromDisk tries to load the key from the disk and validates that it is valid
  259. func TryLoadPrivatePublicKeyFromDisk(pkiPath, name string) (*rsa.PrivateKey, *rsa.PublicKey, error) {
  260. privateKeyPath := pathForKey(pkiPath, name)
  261. // Parse the private key from a file
  262. privKey, err := keyutil.PrivateKeyFromFile(privateKeyPath)
  263. if err != nil {
  264. return nil, nil, errors.Wrapf(err, "couldn't load the private key file %s", privateKeyPath)
  265. }
  266. publicKeyPath := pathForPublicKey(pkiPath, name)
  267. // Parse the public key from a file
  268. pubKeys, err := keyutil.PublicKeysFromFile(publicKeyPath)
  269. if err != nil {
  270. return nil, nil, errors.Wrapf(err, "couldn't load the public key file %s", publicKeyPath)
  271. }
  272. // Allow RSA format only
  273. k, ok := privKey.(*rsa.PrivateKey)
  274. if !ok {
  275. return nil, nil, errors.Errorf("the private key file %s isn't in RSA format", privateKeyPath)
  276. }
  277. p := pubKeys[0].(*rsa.PublicKey)
  278. return k, p, nil
  279. }
  280. // TryLoadCSRFromDisk tries to load the CSR from the disk
  281. func TryLoadCSRFromDisk(pkiPath, name string) (*x509.CertificateRequest, error) {
  282. csrPath := pathForCSR(pkiPath, name)
  283. csr, err := CertificateRequestFromFile(csrPath)
  284. if err != nil {
  285. return nil, errors.Wrapf(err, "could not load the CSR %s", csrPath)
  286. }
  287. return csr, nil
  288. }
  289. // CertificateRequestFromFile returns the CertificateRequest from a given PEM-encoded file.
  290. // Returns an error if the file could not be read or if the CSR could not be parsed.
  291. func CertificateRequestFromFile(file string) (*x509.CertificateRequest, error) {
  292. pemBlock, err := ioutil.ReadFile(file)
  293. if err != nil {
  294. return nil, errors.Wrap(err, "failed to read file")
  295. }
  296. csr, err := parseCSRPEM(pemBlock)
  297. if err != nil {
  298. return nil, errors.Wrapf(err, "error reading certificate request file %s", file)
  299. }
  300. return csr, nil
  301. }
  302. func parseCSRPEM(pemCSR []byte) (*x509.CertificateRequest, error) {
  303. block, _ := pem.Decode(pemCSR)
  304. if block == nil {
  305. return nil, errors.Error("data doesn't contain a valid certificate request")
  306. }
  307. if block.Type != certutil.CertificateRequestBlockType {
  308. return nil, errors.Errorf("expected block type %q, but PEM had type %q", certutil.CertificateRequestBlockType, block.Type)
  309. }
  310. return x509.ParseCertificateRequest(block.Bytes)
  311. }
  312. // CertOrKeyExist returns a boolean whether the cert or the key exists
  313. func CertOrKeyExist(pkiPath, name string) bool {
  314. certificatePath, privateKeyPath := PathsForCertAndKey(pkiPath, name)
  315. _, certErr := os.Stat(certificatePath)
  316. _, keyErr := os.Stat(privateKeyPath)
  317. if os.IsNotExist(certErr) && os.IsNotExist(keyErr) {
  318. // The cert and the key do not exist
  319. return false
  320. }
  321. // Both files exist or one of them
  322. return true
  323. }
  324. // CSROrKeyExist returns true if one of the CSR or key exists
  325. func CSROrKeyExist(csrDir, name string) bool {
  326. csrPath := pathForCSR(csrDir, name)
  327. keyPath := pathForKey(csrDir, name)
  328. _, csrErr := os.Stat(csrPath)
  329. _, keyErr := os.Stat(keyPath)
  330. return !(os.IsNotExist(csrErr) && os.IsNotExist(keyErr))
  331. }
  332. // EncodePublicKeyPEM returns PEM-encoded public data
  333. func EncodePublicKeyPEM(key crypto.PublicKey) ([]byte, error) {
  334. der, err := x509.MarshalPKIXPublicKey(key)
  335. if err != nil {
  336. return []byte{}, err
  337. }
  338. block := pem.Block{
  339. Type: PublicKeyBlockType,
  340. Bytes: der,
  341. }
  342. return pem.EncodeToMemory(&block), nil
  343. }
  344. // EncodeCSRPEM returns PEM-encoded CSR data
  345. func EncodeCSRPEM(csr *x509.CertificateRequest) []byte {
  346. block := pem.Block{
  347. Type: certutil.CertificateRequestBlockType,
  348. Bytes: csr.Raw,
  349. }
  350. return pem.EncodeToMemory(&block)
  351. }
  352. // EncodeCertPEM returns PEM-endcoded certificate data
  353. func EncodeCertPEM(cert *x509.Certificate) []byte {
  354. block := pem.Block{
  355. Type: CertificateBlockType,
  356. Bytes: cert.Raw,
  357. }
  358. return pem.EncodeToMemory(&block)
  359. }
  360. // NewPrivateKey creates an RSA private key
  361. func NewPrivateKey(keyType x509.PublicKeyAlgorithm) (crypto.Signer, error) {
  362. if keyType == x509.ECDSA {
  363. return ecdsa.GenerateKey(elliptic.P256(), cryptorand.Reader)
  364. }
  365. return rsa.GenerateKey(cryptorand.Reader, rsaKeySize)
  366. }
  367. // PathsForCertAndKey returns the paths for the certificate and key given the path and basename.
  368. func PathsForCertAndKey(pkiPath, name string) (string, string) {
  369. return pathForCert(pkiPath, name), pathForKey(pkiPath, name)
  370. }
  371. var (
  372. pathForCert = func(pkiPath, name string) string {
  373. return filepath.Join(pkiPath, fmt.Sprintf("%s.crt", name))
  374. }
  375. pathForKey = func(pkiPath, name string) string {
  376. return filepath.Join(pkiPath, fmt.Sprintf("%s.key", name))
  377. }
  378. pathForPublicKey = func(pkiPath, name string) string {
  379. return filepath.Join(pkiPath, fmt.Sprintf("%s.pub", name))
  380. }
  381. pathForCSR = func(pkiPath, name string) string {
  382. return filepath.Join(pkiPath, fmt.Sprintf("%s.csr", name))
  383. }
  384. )
  385. func SetPathForCert(sf func(pkiPath, name string) string) {
  386. pathForCert = sf
  387. }
  388. func SetPathForKey(sf func(pkiPath, name string) string) {
  389. pathForKey = sf
  390. }
  391. func SetPathForPublicKey(sf func(pkiPath, name string) string) {
  392. pathForPublicKey = sf
  393. }
  394. func SetPathForCSR(sf func(pkiPath, name string) string) {
  395. pathForCSR = sf
  396. }
  397. // NewSignedCert creates a signed certificate using the given CA certificate and key
  398. func NewSignedCert(cfg *CertConfig, key crypto.Signer, caCert *x509.Certificate, caKey crypto.Signer) (*x509.Certificate, error) {
  399. serial, err := cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64))
  400. if err != nil {
  401. return nil, err
  402. }
  403. if len(cfg.CommonName) == 0 {
  404. return nil, errors.Error("must specify a CommonName")
  405. }
  406. if len(cfg.Usages) == 0 {
  407. return nil, errors.Error("must specify at least one ExtKeyUsage")
  408. }
  409. RemoveDuplicateAltNames(&cfg.AltNames)
  410. certTmpl := x509.Certificate{
  411. Subject: pkix.Name{
  412. CommonName: cfg.CommonName,
  413. Organization: cfg.Organization,
  414. },
  415. DNSNames: cfg.AltNames.DNSNames,
  416. IPAddresses: cfg.AltNames.IPs,
  417. SerialNumber: serial,
  418. NotBefore: caCert.NotBefore,
  419. NotAfter: time.Now().Add(CertificateValidity).UTC(),
  420. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
  421. ExtKeyUsage: cfg.Usages,
  422. }
  423. certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &certTmpl, caCert, key.Public(), caKey)
  424. if err != nil {
  425. return nil, err
  426. }
  427. return x509.ParseCertificate(certDERBytes)
  428. }
  429. // RemoveDuplicateAltNames removes duplicate items in altNames.
  430. func RemoveDuplicateAltNames(altNames *certutil.AltNames) {
  431. if altNames == nil {
  432. return
  433. }
  434. if altNames.DNSNames != nil {
  435. altNames.DNSNames = sets.NewString(altNames.DNSNames...).List()
  436. }
  437. ipsKeys := make(map[string]struct{})
  438. var ips []net.IP
  439. for _, one := range altNames.IPs {
  440. if _, ok := ipsKeys[one.String()]; !ok {
  441. ipsKeys[one.String()] = struct{}{}
  442. ips = append(ips, one)
  443. }
  444. }
  445. altNames.IPs = ips
  446. }