certs.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. package utils
  2. import (
  3. "crypto/rand"
  4. "crypto/rsa"
  5. "crypto/tls"
  6. "crypto/x509"
  7. "crypto/x509/pkix"
  8. "encoding/pem"
  9. "io/ioutil"
  10. "math/big"
  11. "net"
  12. "os"
  13. "time"
  14. )
  15. func getTLSConfig(caCert, cert, key []byte, allowInsecure bool) (*tls.Config, error) {
  16. // TLS config
  17. var tlsConfig tls.Config
  18. tlsConfig.InsecureSkipVerify = allowInsecure
  19. certPool := x509.NewCertPool()
  20. certPool.AppendCertsFromPEM(caCert)
  21. tlsConfig.RootCAs = certPool
  22. keypair, err := tls.X509KeyPair(cert, key)
  23. if err != nil {
  24. return &tlsConfig, err
  25. }
  26. tlsConfig.Certificates = []tls.Certificate{keypair}
  27. if allowInsecure {
  28. tlsConfig.InsecureSkipVerify = true
  29. }
  30. return &tlsConfig, nil
  31. }
  32. func newCertificate(org string) (*x509.Certificate, error) {
  33. now := time.Now()
  34. // need to set notBefore slightly in the past to account for time
  35. // skew in the VMs otherwise the certs sometimes are not yet valid
  36. notBefore := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute()-5, 0, 0, time.Local)
  37. notAfter := notBefore.Add(time.Hour * 24 * 1080)
  38. serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
  39. serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
  40. if err != nil {
  41. return nil, err
  42. }
  43. return &x509.Certificate{
  44. SerialNumber: serialNumber,
  45. Subject: pkix.Name{
  46. Organization: []string{org},
  47. },
  48. NotBefore: notBefore,
  49. NotAfter: notAfter,
  50. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageKeyAgreement,
  51. BasicConstraintsValid: true,
  52. }, nil
  53. }
  54. // GenerateCACertificate generates a new certificate authority from the specified org
  55. // and bit size and stores the resulting certificate and key file
  56. // in the arguments.
  57. func GenerateCACertificate(certFile, keyFile, org string, bits int) error {
  58. template, err := newCertificate(org)
  59. if err != nil {
  60. return err
  61. }
  62. template.IsCA = true
  63. template.KeyUsage |= x509.KeyUsageCertSign
  64. template.KeyUsage |= x509.KeyUsageKeyEncipherment
  65. template.KeyUsage |= x509.KeyUsageKeyAgreement
  66. priv, err := rsa.GenerateKey(rand.Reader, bits)
  67. if err != nil {
  68. return err
  69. }
  70. derBytes, err := x509.CreateCertificate(rand.Reader, template, template, &priv.PublicKey, priv)
  71. if err != nil {
  72. return err
  73. }
  74. certOut, err := os.Create(certFile)
  75. if err != nil {
  76. return err
  77. }
  78. pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
  79. certOut.Close()
  80. keyOut, err := os.OpenFile(keyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
  81. if err != nil {
  82. return err
  83. }
  84. pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
  85. keyOut.Close()
  86. return nil
  87. }
  88. // GenerateCert generates a new certificate signed using the provided
  89. // certificate authority files and stores the result in the certificate
  90. // file and key provided. The provided host names are set to the
  91. // appropriate certificate fields.
  92. func GenerateCert(hosts []string, certFile, keyFile, caFile, caKeyFile, org string, bits int) error {
  93. template, err := newCertificate(org)
  94. if err != nil {
  95. return err
  96. }
  97. // client
  98. if len(hosts) == 1 && hosts[0] == "" {
  99. template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
  100. template.KeyUsage = x509.KeyUsageDigitalSignature
  101. } else { // server
  102. template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
  103. for _, h := range hosts {
  104. if ip := net.ParseIP(h); ip != nil {
  105. template.IPAddresses = append(template.IPAddresses, ip)
  106. } else {
  107. template.DNSNames = append(template.DNSNames, h)
  108. }
  109. }
  110. }
  111. tlsCert, err := tls.LoadX509KeyPair(caFile, caKeyFile)
  112. if err != nil {
  113. return err
  114. }
  115. priv, err := rsa.GenerateKey(rand.Reader, bits)
  116. if err != nil {
  117. return err
  118. }
  119. x509Cert, err := x509.ParseCertificate(tlsCert.Certificate[0])
  120. if err != nil {
  121. return err
  122. }
  123. derBytes, err := x509.CreateCertificate(rand.Reader, template, x509Cert, &priv.PublicKey, tlsCert.PrivateKey)
  124. if err != nil {
  125. return err
  126. }
  127. certOut, err := os.Create(certFile)
  128. if err != nil {
  129. return err
  130. }
  131. pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
  132. certOut.Close()
  133. keyOut, err := os.OpenFile(keyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
  134. if err != nil {
  135. return err
  136. }
  137. pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
  138. keyOut.Close()
  139. return nil
  140. }
  141. func ValidateCertificate(addr, caCertPath, serverCertPath, serverKeyPath string) (bool, error) {
  142. caCert, err := ioutil.ReadFile(caCertPath)
  143. if err != nil {
  144. return false, err
  145. }
  146. serverCert, err := ioutil.ReadFile(serverCertPath)
  147. if err != nil {
  148. return false, err
  149. }
  150. serverKey, err := ioutil.ReadFile(serverKeyPath)
  151. if err != nil {
  152. return false, err
  153. }
  154. tlsConfig, err := getTLSConfig(caCert, serverCert, serverKey, false)
  155. if err != nil {
  156. return false, err
  157. }
  158. dialer := &net.Dialer{
  159. Timeout: time.Second * 2,
  160. }
  161. _, err = tls.DialWithDialer(dialer, "tcp", addr, tlsConfig)
  162. if err != nil {
  163. return false, nil
  164. }
  165. return true, nil
  166. }