320 lines
8.5 KiB
Go
320 lines
8.5 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"github.com/stef/pkiapi/internal/pki"
|
|
"github.com/stef/pkiapi/internal/storage"
|
|
)
|
|
|
|
// CreateCARequest représente la requête de création d'une CA
|
|
type CreateCARequest struct {
|
|
Subject string `json:"subject" binding:"required"`
|
|
ValidityDays int `json:"validity_days" binding:"required,min=1,max=36500"`
|
|
}
|
|
|
|
// CAResponse représente la réponse avec les données de la CA
|
|
type CAResponse struct {
|
|
ID string `json:"id"`
|
|
Subject string `json:"subject"`
|
|
NotBefore string `json:"not_before"`
|
|
NotAfter string `json:"not_after"`
|
|
SerialNumber string `json:"serial_number"`
|
|
Certificate string `json:"certificate"` // Base64 encoded
|
|
PrivateKey string `json:"private_key,omitempty"` // Base64 encoded (optional)
|
|
IsCA bool `json:"is_ca"`
|
|
}
|
|
|
|
// caStore est un store global pour les CAs
|
|
var caStore storage.CertificateStore
|
|
|
|
// InitCAStore initialise le store pour les CAs
|
|
func InitCAStore(store storage.CertificateStore) {
|
|
caStore = store
|
|
}
|
|
|
|
// ListCAResponse représente une CA dans une liste
|
|
type ListCAResponse struct {
|
|
ID string `json:"id"`
|
|
Subject string `json:"subject"`
|
|
Issuer string `json:"issuer"`
|
|
NotBefore string `json:"not_before"`
|
|
NotAfter string `json:"not_after"`
|
|
SerialNumber string `json:"serial_number"`
|
|
}
|
|
|
|
// ListCAs retourne toutes les CAs
|
|
func ListCAs(c *gin.Context) {
|
|
cas := caStore.ListCertificates()
|
|
|
|
var responses []ListCAResponse
|
|
for _, ca := range cas {
|
|
if ca.IsCA {
|
|
response := ListCAResponse{
|
|
ID: ca.ID,
|
|
Subject: ca.Subject,
|
|
Issuer: ca.Issuer,
|
|
NotBefore: ca.NotBefore.Format("2006-01-02T15:04:05Z"),
|
|
NotAfter: ca.NotAfter.Format("2006-01-02T15:04:05Z"),
|
|
}
|
|
if ca.Cert != nil {
|
|
response.SerialNumber = ca.Cert.SerialNumber.String()
|
|
}
|
|
responses = append(responses, response)
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"cas": responses})
|
|
}
|
|
|
|
// CreateCA crée une nouvelle autorité de certification
|
|
func CreateCA(c *gin.Context) {
|
|
var req CreateCARequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// Générer la CA
|
|
ca, err := pki.GenerateCA(req.Subject, req.ValidityDays)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur génération CA"})
|
|
return
|
|
}
|
|
|
|
// Générer un ID unique
|
|
caID := uuid.New().String()
|
|
ca.ID = caID
|
|
|
|
// Sauvegarder la CA
|
|
if err := caStore.SaveCertificate(caID, ca); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur sauvegarde CA"})
|
|
return
|
|
}
|
|
|
|
// Récupérer l'utilisateur depuis le contexte JWT
|
|
userID, _ := c.Get("user_id")
|
|
|
|
// Retourner la réponse
|
|
response := CAResponse{
|
|
ID: caID,
|
|
Subject: ca.Subject,
|
|
NotBefore: ca.NotBefore.Format("2006-01-02T15:04:05Z"),
|
|
NotAfter: ca.NotAfter.Format("2006-01-02T15:04:05Z"),
|
|
IsCA: true,
|
|
}
|
|
|
|
if ca.Cert != nil {
|
|
response.SerialNumber = ca.Cert.SerialNumber.String()
|
|
response.Certificate = base64.StdEncoding.EncodeToString(ca.Cert.Raw)
|
|
}
|
|
|
|
// Ajouter la clé privée
|
|
if ca.PrivateKey != nil {
|
|
privKeyBase64, err := encodePrivateKey(ca.PrivateKey)
|
|
if err == nil {
|
|
response.PrivateKey = privKeyBase64
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, gin.H{
|
|
"ca": response,
|
|
"created_by": userID,
|
|
})
|
|
}
|
|
|
|
// GetCA retourne une CA par ID
|
|
func GetCA(c *gin.Context) {
|
|
id := c.Param("id")
|
|
|
|
ca, err := caStore.GetCertificate(id)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "CA non trouvée"})
|
|
return
|
|
}
|
|
|
|
if !ca.IsCA {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "ce certificat n'est pas une CA"})
|
|
return
|
|
}
|
|
|
|
response := CAResponse{
|
|
ID: ca.ID,
|
|
Subject: ca.Subject,
|
|
NotBefore: ca.NotBefore.Format("2006-01-02T15:04:05Z"),
|
|
NotAfter: ca.NotAfter.Format("2006-01-02T15:04:05Z"),
|
|
IsCA: true,
|
|
}
|
|
|
|
if ca.Cert != nil {
|
|
response.SerialNumber = ca.Cert.SerialNumber.String()
|
|
response.Certificate = base64.StdEncoding.EncodeToString(ca.Cert.Raw)
|
|
}
|
|
|
|
// Ajouter la clé privée
|
|
if ca.PrivateKey != nil {
|
|
privKeyBase64, err := encodePrivateKey(ca.PrivateKey)
|
|
if err == nil {
|
|
response.PrivateKey = privKeyBase64
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"ca": response})
|
|
}
|
|
|
|
// SignCertificateRequest représente une requête de signature de certificat par une CA
|
|
type SignCertificateRequest struct {
|
|
CAId string `json:"ca_id" binding:"required"`
|
|
Subject string `json:"subject" binding:"required"`
|
|
ValidityDays int `json:"validity_days" binding:"required,min=1,max=3650"`
|
|
}
|
|
|
|
// SignCertificateWithCA signe un certificat avec une CA
|
|
func SignCertificateWithCA(c *gin.Context) {
|
|
var req SignCertificateRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// Récupérer la CA
|
|
ca, err := caStore.GetCertificate(req.CAId)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "CA non trouvée"})
|
|
return
|
|
}
|
|
|
|
if !ca.IsCA {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "ce certificat n'est pas une CA"})
|
|
return
|
|
}
|
|
|
|
// Signer le certificat avec la CA
|
|
cert, err := pki.SignCertificate(ca, req.Subject, req.ValidityDays)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur signature certificat: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
// Générer un ID unique
|
|
certID := uuid.New().String()
|
|
cert.ID = certID
|
|
|
|
// Sauvegarder le certificat
|
|
if err := certificateStore.SaveCertificate(certID, cert); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur sauvegarde certificat"})
|
|
return
|
|
}
|
|
|
|
// Récupérer l'utilisateur depuis le contexte JWT
|
|
userID, _ := c.Get("user_id")
|
|
|
|
// Retourner la réponse
|
|
response := CertificateResponse{
|
|
ID: certID,
|
|
Subject: cert.Subject,
|
|
Issuer: cert.Issuer,
|
|
NotBefore: cert.NotBefore.Format("2006-01-02T15:04:05Z"),
|
|
NotAfter: cert.NotAfter.Format("2006-01-02T15:04:05Z"),
|
|
Revoked: false,
|
|
}
|
|
|
|
if cert.Cert != nil {
|
|
response.SerialNumber = cert.Cert.SerialNumber.String()
|
|
response.Certificate = base64.StdEncoding.EncodeToString(cert.Cert.Raw)
|
|
}
|
|
|
|
// Ajouter la clé privée
|
|
if cert.PrivateKey != nil {
|
|
privKeyBase64, err := encodePrivateKey(cert.PrivateKey)
|
|
if err == nil {
|
|
response.PrivateKey = privKeyBase64
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, gin.H{
|
|
"certificate": response,
|
|
"signed_by": req.CAId,
|
|
"created_by": userID,
|
|
})
|
|
}
|
|
|
|
// SignSubCARequest représente une requête de création de sub-CA
|
|
type SignSubCARequest struct {
|
|
ParentCAId string `json:"parent_ca_id" binding:"required"`
|
|
Subject string `json:"subject" binding:"required"`
|
|
ValidityDays int `json:"validity_days" binding:"required,min=1,max=36500"`
|
|
}
|
|
|
|
// SignSubCA crée une CA intermédiaire signée par une CA parent
|
|
func SignSubCA(c *gin.Context) {
|
|
var req SignSubCARequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// Récupérer la CA parent
|
|
parentCA, err := caStore.GetCertificate(req.ParentCAId)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "CA parent non trouvée"})
|
|
return
|
|
}
|
|
|
|
if !parentCA.IsCA {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "ce certificat n'est pas une CA"})
|
|
return
|
|
}
|
|
|
|
// Signer la sub-CA avec la CA parent
|
|
subCA, err := pki.SignSubCA(parentCA, req.Subject, req.ValidityDays)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur signature sub-CA: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
// Générer un ID unique
|
|
subCAId := uuid.New().String()
|
|
subCA.ID = subCAId
|
|
|
|
// Sauvegarder la sub-CA
|
|
if err := caStore.SaveCertificate(subCAId, subCA); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur sauvegarde sub-CA"})
|
|
return
|
|
}
|
|
|
|
// Récupérer l'utilisateur depuis le contexte JWT
|
|
userID, _ := c.Get("user_id")
|
|
|
|
// Retourner la réponse
|
|
response := CAResponse{
|
|
ID: subCAId,
|
|
Subject: subCA.Subject,
|
|
NotBefore: subCA.NotBefore.Format("2006-01-02T15:04:05Z"),
|
|
NotAfter: subCA.NotAfter.Format("2006-01-02T15:04:05Z"),
|
|
IsCA: true,
|
|
}
|
|
|
|
if subCA.Cert != nil {
|
|
response.SerialNumber = subCA.Cert.SerialNumber.String()
|
|
response.Certificate = base64.StdEncoding.EncodeToString(subCA.Cert.Raw)
|
|
}
|
|
|
|
// Ajouter la clé privée
|
|
if subCA.PrivateKey != nil {
|
|
privKeyBase64, err := encodePrivateKey(subCA.PrivateKey)
|
|
if err == nil {
|
|
response.PrivateKey = privKeyBase64
|
|
}
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, gin.H{
|
|
"ca": response,
|
|
"signed_by": req.ParentCAId,
|
|
"created_by": userID,
|
|
})
|
|
}
|