diff --git a/.gitignore b/.gitignore index 76803fe..99adeb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ scm +go.sum diff --git a/api/create_cert.go b/api/create_cert.go new file mode 100644 index 0000000..07b3189 --- /dev/null +++ b/api/create_cert.go @@ -0,0 +1,181 @@ +package api + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/json" + "encoding/pem" + "errors" + "fmt" + "math/big" + "net/http" + "os" + "scm/model" + "time" + + "github.com/youmark/pkcs8" +) + +func CreateCert(w http.ResponseWriter, r *http.Request) { + var requestData model.CreateCertificateRequest + if err := json.NewDecoder(r.Body).Decode(&requestData); err != nil { + http.Error(w, "invalid JSON format", http.StatusBadRequest) + return + } + caCert, caKey, err := loadCertAndKey( + fmt.Sprintf( + "%s/%s/%s.crt", + os.Getenv("CERTS_PATH"), + "root", + "TolaMironcenkoCA", + ), + fmt.Sprintf( + "%s/%s/%s.key", + os.Getenv("CERTS_PATH"), + "root", + "TolaMironcenkoCA", + ), + requestData.CAKeyPassword, + ) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + fmt.Println("error loading certificate and key:", err) + return + } + // TODO: Implement create certificate endpoint + serverKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + fmt.Println("error generating server key:", err) + return + } + serverCert, err := createServerCert(caCert, caKey, serverKey, requestData) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + fmt.Println("error creating server certificate:", err) + return + } + _, err = os.Stat(fmt.Sprintf("%s/%s", os.Getenv("CERTS_PATH"), requestData.DNSNames[0])) + if err == nil { + http.Error(w, "certificate already exists", http.StatusConflict) + fmt.Println("certificate already exists") + return + } else if os.IsNotExist(err) { + err := os.Mkdir(fmt.Sprintf("%s/%s", os.Getenv("CERTS_PATH"), requestData.DNSNames[0]), 0755) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + fmt.Println("error creating directory:", err) + return + } + } else { + http.Error(w, err.Error(), http.StatusInternalServerError) + fmt.Println("error checking certificate directory:", err) + return + } + err = savePEM(fmt.Sprintf("%s/%s/%s.crt", os.Getenv("CERTS_PATH"), requestData.DNSNames[0], requestData.DNSNames[0]), "CERTIFICATE", serverCert) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + fmt.Println("error saving certificate:", err) + return + } + err = savePEM(fmt.Sprintf("%s/%s/%s.key", os.Getenv("CERTS_PATH"), requestData.DNSNames[0], requestData.DNSNames[0]), "RSA PRIVATE KEY", x509.MarshalPKCS1PrivateKey(serverKey)) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + fmt.Println("error saving private key:", err) + return + } + fmt.Fprintf(w, "%s", `{"status": "success", "message": "Certificate created successfully"}`) +} + +func createServerCert( + caCert *x509.Certificate, + caKey *rsa.PrivateKey, + serverKey *rsa.PrivateKey, + certdata model.CreateCertificateRequest, +) ([]byte, error) { + // Шаблон серверного сертификата + template := x509.Certificate{ + SerialNumber: big.NewInt(time.Now().Unix()), + Subject: pkix.Name{ + Organization: certdata.OrganizationName, + CommonName: certdata.CommonName, + OrganizationalUnit: certdata.OrganizationUnit, + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(1, 0, 0), // 1 год + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + DNSNames: certdata.DNSNames, + } + + // Создаем сертификат, подписанный CA + return x509.CreateCertificate( + rand.Reader, + &template, + caCert, + &serverKey.PublicKey, + caKey, + ) +} + +func loadCertAndKey(certFile, keyFile, password string) (*x509.Certificate, *rsa.PrivateKey, error) { + fmt.Println(certFile, keyFile) + // Загрузка корневого сертификата + caCertPEM, err := os.ReadFile(certFile) + if err != nil { + return nil, nil, err + } + + block, _ := pem.Decode(caCertPEM) + if block == nil { + return nil, nil, errors.New("failed to parse CA certificate PEM") + } + + caCert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, nil, err + } + + // Загрузка зашифрованного приватного ключа + keyPEM, err := os.ReadFile(keyFile) + if err != nil { + return nil, nil, err + } + + block, _ = pem.Decode(keyPEM) + if block == nil { + return nil, nil, errors.New("failed to parse CA key PEM") + } + fmt.Print(block.Type) + // Декодирование ключа с паролем + decryptedKey, err := pkcs8.ParsePKCS8PrivateKey(block.Bytes, []byte(password)) + if err != nil { + return nil, nil, fmt.Errorf("failed to decrypt CA key: %v", err) + } + + caKey, ok := decryptedKey.(*rsa.PrivateKey) + if !ok { + return nil, nil, err + } + + return caCert, caKey, nil +} + +func savePEM(filename, blockType string, data []byte) error { + file, err := os.Create(filename) + if err != nil { + return err + } + defer file.Close() + + err = pem.Encode(file, &pem.Block{ + Type: blockType, + Bytes: data, + }) + if err != nil { + return err + } + return nil +} diff --git a/api/delete_cert.go b/api/delete_cert.go new file mode 100644 index 0000000..e2b926a --- /dev/null +++ b/api/delete_cert.go @@ -0,0 +1,33 @@ +package api + +import ( + "encoding/json" + "fmt" + "net/http" + "os" + "scm/model" +) + +func DeleteCert(w http.ResponseWriter, r *http.Request) { + var requestData model.DeleteCertRequest + if err := json.NewDecoder(r.Body).Decode(&requestData); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + _, err := os.Stat(fmt.Sprintf("%s/%s", os.Getenv("CERTS_PATH"), requestData.Domain)) + if err == nil { + err = os.RemoveAll(fmt.Sprintf("%s/%s", os.Getenv("CERTS_PATH"), requestData.Domain)) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + fmt.Fprintf(w, "%s", `{"status": "success"}`) + } else if !os.IsNotExist(err) { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } else { + http.Error(w, "Certificate not found", http.StatusNotFound) + return + } + // TODO: Implement delete certificate functionality +} diff --git a/api/frontend/index.html b/api/frontend/index.html index 51e5909..0d5cfa6 100644 --- a/api/frontend/index.html +++ b/api/frontend/index.html @@ -7,6 +7,7 @@ + Scm