diff --git a/utils/cert/cert.go b/utils/cert/cert.go new file mode 100644 index 00000000..58b23561 --- /dev/null +++ b/utils/cert/cert.go @@ -0,0 +1,199 @@ +package cert + +import ( + "bytes" + cryptorand "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "io/ioutil" + "math/big" + "math/rand" + "net" + "strconv" + "time" +) + +func CreateSignCertToFile(rootCa *x509.Certificate, rootKey *rsa.PrivateKey, domainOrIP string, expireDays int, name string) (err error) { + cert, key, err := CreateSignCert(rootCa, rootKey, domainOrIP, expireDays) + if err != nil { + return + } + err = ioutil.WriteFile(name+".crt", cert, 0755) + if err != nil { + return + } + err = ioutil.WriteFile(name+".key", key, 0755) + return +} +func CreateSignCert(rootCa *x509.Certificate, rootKey *rsa.PrivateKey, domainOrIP string, expireDays int) (certBytes []byte, keyBytes []byte, err error) { + cer := &x509.Certificate{ + SerialNumber: big.NewInt(rand.Int63()), //证书序列号 + Subject: pkix.Name{ + Country: []string{getCountry()}, + Organization: []string{domainOrIP}, + OrganizationalUnit: []string{domainOrIP}, + CommonName: domainOrIP, + }, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(time.Hour * 24 * time.Duration(expireDays)), + BasicConstraintsValid: true, + IsCA: false, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + //KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageDataEncipherment, + EmailAddresses: []string{}, + IPAddresses: []net.IP{}, + } + if ip := net.ParseIP(domainOrIP); ip != nil { + cer.IPAddresses = append(cer.IPAddresses, ip) + } else { + cer.DNSNames = append(cer.DNSNames, domainOrIP) + } + + // cer.IPAddresses = append(cer.IPAddresses, alternateIPs...) + // cer.DNSNames = append(cer.DNSNames, alternateDNS...) + + //生成公钥私钥对 + priKey, err := rsa.GenerateKey(cryptorand.Reader, 2048) + if err != nil { + return + } + certBytes, err = x509.CreateCertificate(cryptorand.Reader, cer, rootCa, &priKey.PublicKey, rootKey) + if err != nil { + return + } + + //编码证书文件和私钥文件 + caPem := &pem.Block{ + Type: "CERTIFICATE", + Bytes: certBytes, + } + certBytes = pem.EncodeToMemory(caPem) + + buf := x509.MarshalPKCS1PrivateKey(priKey) + keyPem := &pem.Block{ + Type: "PRIVATE KEY", + Bytes: buf, + } + keyBytes = pem.EncodeToMemory(keyPem) + return +} +func CreateCaToFile(name, domainOrIP string, expireDays int) (err error) { + ca, key, err := CreateCa(domainOrIP, expireDays) + if err != nil { + return + } + err = ioutil.WriteFile(name+".crt", ca, 0755) + if err != nil { + return + } + err = ioutil.WriteFile(name+".key", key, 0755) + return +} +func CreateCa(organization string, expireDays int) (certBytes []byte, keyBytes []byte, err error) { + priv, err := rsa.GenerateKey(cryptorand.Reader, 2048) + if err != nil { + return nil, nil, err + } + + template := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: organization, + Organization: []string{organization}, + OrganizationalUnit: []string{organization}, + Country: []string{getCountry()}, + }, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(time.Hour * 24 * time.Duration(expireDays)), + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + IsCA: true, + } + + derBytes, err := x509.CreateCertificate(cryptorand.Reader, &template, &template, &priv.PublicKey, priv) + if err != nil { + return nil, nil, err + } + + // Generate cert + certBuffer := bytes.Buffer{} + if err := pem.Encode(&certBuffer, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil { + return nil, nil, err + } + + // Generate key + keyBuffer := bytes.Buffer{} + if err := pem.Encode(&keyBuffer, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil { + return nil, nil, err + } + + return certBuffer.Bytes(), keyBuffer.Bytes(), nil +} +func ParseCertAndKeyBytes(certPemFileByes, keyFileBytes []byte) (cert *x509.Certificate, privateKey *rsa.PrivateKey, err error) { + //解析根证书 + cert, err = ParseCertBytes(certPemFileByes) + if err != nil { + return + } + //解析私钥 + privateKey, err = ParseKeyBytes(keyFileBytes) + return +} +func ParseCertAndKey(certPemFile, keyFile string) (cert *x509.Certificate, privateKey *rsa.PrivateKey, err error) { + //解析根证书 + cert, err = ParseCert(certPemFile) + if err != nil { + return + } + //解析私钥 + privateKey, err = ParseKey(keyFile) + return +} +func ParseCert(certPemFile string) (cert *x509.Certificate, err error) { + //解析证书 + certFile_, err := ioutil.ReadFile(certPemFile) + if err != nil { + return + } + cert, err = ParseCertBytes(certFile_) + return +} +func ParseKey(keyFile string) (key *rsa.PrivateKey, err error) { + //解析证书 + keyFile_, err := ioutil.ReadFile(keyFile) + if err != nil { + return + } + key, err = ParseKeyBytes(keyFile_) + return +} +func ParseCertBytes(certPemFileBytes []byte) (cert *x509.Certificate, err error) { + caBlock, _ := pem.Decode(certPemFileBytes) + cert, err = x509.ParseCertificate(caBlock.Bytes) + return +} +func ParseKeyBytes(keyFileBytes []byte) (praKey *rsa.PrivateKey, err error) { + keyBlock, _ := pem.Decode(keyFileBytes) + praKey, err = x509.ParsePKCS1PrivateKey(keyBlock.Bytes) + return +} +func getCountry() string { + CList := []string{"AD", "AE", "AF", "AG", "AI", "AL", "AM", "AO", "AR", "AT", "AU", "AZ", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BL", "BM", "BN", "BO", "BR", "BS", "BW", "BY", "BZ", "CA", "CF", "CG", "CH", "CK", "CL", "CM", "CN", "CO", "CR", "CS", "CU", "CY", "CZ", "DE", "DJ", "DK", "DO", "DZ", "EC", "EE", "EG", "ES", "ET", "FI", "FJ", "FR", "GA", "GB", "GD", "GE", "GF", "GH", "GI", "GM", "GN", "GR", "GT", "GU", "GY", "HK", "HN", "HT", "HU", "ID", "IE", "IL", "IN", "IQ", "IR", "IS", "IT", "JM", "JO", "JP", "KE", "KG", "KH", "KP", "KR", "KT", "KW", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "MG", "ML", "MM", "MN", "MO", "MS", "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", "NE", "NG", "NI", "NL", "NO", "NP", "NR", "NZ", "OM", "PA", "PE", "PF", "PG", "PH", "PK", "PL", "PR", "PT", "PY", "QA", "RO", "RU", "SA", "SB", "SC", "SD", "SE", "SG", "SI", "SK", "SL", "SM", "SN", "SO", "SR", "ST", "SV", "SY", "SZ", "TD", "TG", "TH", "TJ", "TM", "TN", "TO", "TR", "TT", "TW", "TZ", "UA", "UG", "US", "UY", "UZ", "VC", "VE", "VN", "YE", "YU", "ZA", "ZM", "ZR", "ZW"} + return CList[int(randInt(4))%len(CList)] +} +func randInt(strLen int) int64 { + codes := "123456789" + codeLen := len(codes) + data := make([]byte, strLen) + rand.Seed(time.Now().UnixNano() + rand.Int63() + rand.Int63() + rand.Int63() + rand.Int63()) + for i := 0; i < strLen; i++ { + idx := rand.Intn(codeLen) + data[i] = byte(codes[idx]) + } + i, _ := strconv.ParseInt(string(data), 10, 64) + return i +} diff --git a/utils/cert/cert_test.go b/utils/cert/cert_test.go new file mode 100644 index 00000000..a01b4311 --- /dev/null +++ b/utils/cert/cert_test.go @@ -0,0 +1,71 @@ +package cert + +import ( + "os" + "testing" +) + +func TestCaGen(t *testing.T) { + err := CreateCaToFile("ca", "test", 365) + if err != nil { + t.Fatal(err) + return + } + ca, key, err := ParseCertAndKey("ca.crt", "ca.key") + if err != nil { + t.Fatal(err) + return + } + if ca.Subject.Organization[0] != "test" { + t.Fatalf("Organization %s not match test", ca.Subject.Organization[0]) + return + } + err = key.Validate() + if err != nil { + t.Fatal(err) + return + } + os.Remove("ca.crt") + os.Remove("ca.key") + +} +func TestSign(t *testing.T) { + err := CreateCaToFile("ca", "test", 365) + if err != nil { + t.Fatal(err) + return + } + ca, key, err := ParseCertAndKey("ca.crt", "ca.key") + if err != nil { + t.Fatal(err) + return + } + err = CreateCaToFile("ca", "test", 365) + if err != nil { + t.Fatal(err) + return + } + err = CreateSignCertToFile(ca, key, "server.com", 365, "server") + if err != nil { + t.Fatal(err) + return + } + servercrt, serverkey, err := ParseCertAndKey("server.crt", "server.key") + if err != nil { + t.Fatal(err) + return + } + if servercrt.Subject.CommonName != "server.com" { + t.Fatalf("CommonName %s not match server.com", ca.Subject.CommonName) + return + } + err = serverkey.Validate() + if err != nil { + t.Fatal(err) + return + } + os.Remove("ca.crt") + os.Remove("ca.key") + os.Remove("server.crt") + os.Remove("server.key") +}