You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

cert.go 6.2 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. package cmdline
  2. import (
  3. "crypto/ecdsa"
  4. "crypto/elliptic"
  5. "crypto/rand"
  6. "crypto/x509"
  7. "crypto/x509/pkix"
  8. "encoding/pem"
  9. "fmt"
  10. "math/big"
  11. "os"
  12. "path/filepath"
  13. "time"
  14. "github.com/spf13/cobra"
  15. "gitlink.org.cn/cloudream/jcs-pub/client/internal/http/auth"
  16. )
  17. func init() {
  18. certCmd := cobra.Command{
  19. Use: "cert",
  20. }
  21. RootCmd.AddCommand(&certCmd)
  22. certRoot := cobra.Command{
  23. Use: "root [outputDir]",
  24. Args: cobra.ExactArgs(1),
  25. Run: func(cmd *cobra.Command, args []string) {
  26. certRoot(args[0])
  27. },
  28. }
  29. certCmd.AddCommand(&certRoot)
  30. var certFilePath string
  31. var keyFilePath string
  32. certServer := cobra.Command{
  33. Use: "server [outputDir]",
  34. Args: cobra.ExactArgs(1),
  35. Run: func(cmd *cobra.Command, args []string) {
  36. certServer(certFilePath, keyFilePath, args[0])
  37. },
  38. }
  39. certServer.Flags().StringVar(&certFilePath, "cert", "", "CA certificate file path")
  40. certServer.Flags().StringVar(&keyFilePath, "key", "", "CA key file path")
  41. certCmd.AddCommand(&certServer)
  42. certClient := cobra.Command{
  43. Use: "client [outputDir]",
  44. Args: cobra.ExactArgs(1),
  45. Run: func(cmd *cobra.Command, args []string) {
  46. certClient(certFilePath, keyFilePath, args[0])
  47. },
  48. }
  49. certClient.Flags().StringVar(&certFilePath, "cert", "", "CA certificate file path")
  50. certClient.Flags().StringVar(&keyFilePath, "key", "", "CA key file path")
  51. certCmd.AddCommand(&certClient)
  52. }
  53. func certRoot(output string) {
  54. caPriv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  55. // 创建 CA 证书模板
  56. caTemplate := &x509.Certificate{
  57. SerialNumber: big.NewInt(1),
  58. Subject: pkix.Name{
  59. Organization: []string{"JCS"},
  60. },
  61. NotBefore: time.Now(),
  62. NotAfter: time.Now().AddDate(10, 0, 0), // 有效期10年
  63. KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature,
  64. BasicConstraintsValid: true,
  65. IsCA: true,
  66. }
  67. // 自签名 CA 证书
  68. caCertDER, _ := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, &caPriv.PublicKey, caPriv)
  69. // 保存 CA 证书和私钥
  70. writePem(filepath.Join(output, "ca_cert.pem"), "CERTIFICATE", caCertDER)
  71. privDER, _ := x509.MarshalECPrivateKey(caPriv)
  72. writePem(filepath.Join(output, "ca_key.pem"), "EC PRIVATE KEY", privDER)
  73. fmt.Println("CA certificate and key saved to", output)
  74. }
  75. func certServer(certFile string, keyFile string, output string) {
  76. // 读取 CA 证书和私钥
  77. caCertPEM, err := os.ReadFile(certFile)
  78. if err != nil {
  79. fmt.Println("Failed to read CA certificate:", err)
  80. return
  81. }
  82. caKeyPEM, err := os.ReadFile(keyFile)
  83. if err != nil {
  84. fmt.Println("Failed to read CA key:", err)
  85. return
  86. }
  87. caCertPEMBlock, _ := pem.Decode(caCertPEM)
  88. if caCertPEMBlock == nil {
  89. fmt.Println("Failed to decode CA certificate")
  90. return
  91. }
  92. caKeyPEMBlock, _ := pem.Decode(caKeyPEM)
  93. if caKeyPEMBlock == nil {
  94. fmt.Println("Failed to decode CA key")
  95. return
  96. }
  97. caCert, err := x509.ParseCertificate(caCertPEMBlock.Bytes)
  98. if err != nil {
  99. fmt.Println("Failed to parse CA certificate:", err)
  100. return
  101. }
  102. caKey, err := x509.ParseECPrivateKey(caKeyPEMBlock.Bytes)
  103. if err != nil {
  104. fmt.Println("Failed to parse CA key:", err)
  105. return
  106. }
  107. // 生成服务端私钥
  108. serverPriv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  109. // 服务端证书模板
  110. serverTemplate := &x509.Certificate{
  111. SerialNumber: big.NewInt(2),
  112. Subject: pkix.Name{
  113. CommonName: "localhost",
  114. },
  115. NotBefore: time.Now(),
  116. NotAfter: time.Now().AddDate(1, 0, 0), // 有效期1年
  117. KeyUsage: x509.KeyUsageDigitalSignature,
  118. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
  119. BasicConstraintsValid: true,
  120. }
  121. // 添加主机名/IP 到证书
  122. serverTemplate.DNSNames = []string{auth.ClientInternalSNI}
  123. // 用 CA 签发服务端证书
  124. serverCertDER, _ := x509.CreateCertificate(rand.Reader, serverTemplate, caCert, &serverPriv.PublicKey, caKey)
  125. // 保存服务端证书和私钥
  126. writePem(filepath.Join(output, "server_cert.pem"), "CERTIFICATE", serverCertDER)
  127. privPem, _ := x509.MarshalECPrivateKey(serverPriv)
  128. writePem(filepath.Join(output, "server_key.pem"), "EC PRIVATE KEY", privPem)
  129. fmt.Println("Server certificate and key saved to", output)
  130. }
  131. func certClient(certFile string, keyFile string, output string) {
  132. // 读取 CA 证书和私钥
  133. caCertPEM, err := os.ReadFile(certFile)
  134. if err != nil {
  135. fmt.Println("Failed to read CA certificate:", err)
  136. return
  137. }
  138. caKeyPEM, err := os.ReadFile(keyFile)
  139. if err != nil {
  140. fmt.Println("Failed to read CA key:", err)
  141. return
  142. }
  143. caCertPEMBlock, _ := pem.Decode(caCertPEM)
  144. if caCertPEMBlock == nil {
  145. fmt.Println("Failed to decode CA certificate")
  146. return
  147. }
  148. caKeyPEMBlock, _ := pem.Decode(caKeyPEM)
  149. if caKeyPEMBlock == nil {
  150. fmt.Println("Failed to decode CA key")
  151. return
  152. }
  153. caCert, err := x509.ParseCertificate(caCertPEMBlock.Bytes)
  154. if err != nil {
  155. fmt.Println("Failed to parse CA certificate:", err)
  156. return
  157. }
  158. caKey, err := x509.ParseECPrivateKey(caKeyPEMBlock.Bytes)
  159. if err != nil {
  160. fmt.Println("Failed to parse CA key:", err)
  161. return
  162. }
  163. // 生成客户端私钥
  164. clientPriv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  165. // 客户端证书模板
  166. clientTemplate := &x509.Certificate{
  167. SerialNumber: big.NewInt(3),
  168. Subject: pkix.Name{
  169. CommonName: "client",
  170. },
  171. NotBefore: time.Now(),
  172. NotAfter: time.Now().AddDate(1, 0, 0), // 有效期1年
  173. KeyUsage: x509.KeyUsageDigitalSignature,
  174. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
  175. BasicConstraintsValid: true,
  176. }
  177. // 用 CA 签发客户端证书
  178. clientCertDER, _ := x509.CreateCertificate(rand.Reader, clientTemplate, caCert, &clientPriv.PublicKey, caKey)
  179. // 保存客户端证书和私钥
  180. writePem(filepath.Join(output, "client_cert.pem"), "CERTIFICATE", clientCertDER)
  181. privPem, _ := x509.MarshalECPrivateKey(clientPriv)
  182. writePem(filepath.Join(output, "client_key.pem"), "EC PRIVATE KEY", privPem)
  183. fmt.Println("Client certificate and key saved to", output)
  184. }
  185. func writePem(filename, pemType string, bytes []byte) {
  186. f, _ := os.Create(filename)
  187. pem.Encode(f, &pem.Block{Type: pemType, Bytes: bytes})
  188. f.Close()
  189. }

本项目旨在将云际存储公共基础设施化,使个人及企业可低门槛使用高效的云际存储服务(安装开箱即用云际存储客户端即可,无需关注其他组件的部署),同时支持用户灵活便捷定制云际存储的功能细节。