feat: add certificate export functionality (PEM, DER, with private key, chain)
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -182,3 +184,153 @@ func GetCRL(c *gin.Context) {
|
||||
"version": 1,
|
||||
})
|
||||
}
|
||||
|
||||
// ExportCertificatePEM retourne un certificat au format PEM
|
||||
func ExportCertificatePEM(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
|
||||
cert, err := certificateStore.GetCertificate(id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "certificat non trouvé"})
|
||||
return
|
||||
}
|
||||
|
||||
if cert.Cert == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "certificat non disponible"})
|
||||
return
|
||||
}
|
||||
|
||||
// Convertir le certificat en format PEM
|
||||
pemCert := &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: cert.Cert.Raw,
|
||||
}
|
||||
pemData := pem.EncodeToMemory(pemCert)
|
||||
|
||||
// Retourner en tant que fichier téléchargeable
|
||||
filename := "certificate_" + id + ".pem"
|
||||
c.Header("Content-Type", "application/x-pem-file")
|
||||
c.Header("Content-Disposition", "attachment; filename="+filename)
|
||||
c.String(http.StatusOK, string(pemData))
|
||||
}
|
||||
|
||||
// ExportCertificateDER retourne un certificat au format DER
|
||||
func ExportCertificateDER(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
|
||||
cert, err := certificateStore.GetCertificate(id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "certificat non trouvé"})
|
||||
return
|
||||
}
|
||||
|
||||
if cert.Cert == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "certificat non disponible"})
|
||||
return
|
||||
}
|
||||
|
||||
// Retourner en tant que fichier binaire DER
|
||||
filename := "certificate_" + id + ".der"
|
||||
c.Header("Content-Type", "application/pkix-cert")
|
||||
c.Header("Content-Disposition", "attachment; filename="+filename)
|
||||
c.Data(http.StatusOK, "application/pkix-cert", cert.Cert.Raw)
|
||||
}
|
||||
|
||||
// ExportCertificateWithPrivateKeyPEM retourne un certificat avec sa clé privée au format PEM
|
||||
func ExportCertificateWithPrivateKeyPEM(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
|
||||
cert, err := certificateStore.GetCertificate(id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "certificat non trouvé"})
|
||||
return
|
||||
}
|
||||
|
||||
if cert.Cert == nil || cert.PrivateKey == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "certificat ou clé privée non disponible"})
|
||||
return
|
||||
}
|
||||
|
||||
// Convertir le certificat en PEM
|
||||
pemCert := &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: cert.Cert.Raw,
|
||||
}
|
||||
certPEM := pem.EncodeToMemory(pemCert)
|
||||
|
||||
// Convertir la clé privée en PKCS#8 DER
|
||||
privateKeyDER, err := x509.MarshalPKCS8PrivateKey(cert.PrivateKey)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur sérialisation clé privée"})
|
||||
return
|
||||
}
|
||||
|
||||
// Convertir en PEM
|
||||
pemKey := &pem.Block{
|
||||
Type: "PRIVATE KEY",
|
||||
Bytes: privateKeyDER,
|
||||
}
|
||||
keyPEM := pem.EncodeToMemory(pemKey)
|
||||
|
||||
// Combiner certificat + clé privée
|
||||
combined := string(certPEM) + string(keyPEM)
|
||||
|
||||
// Retourner en tant que fichier téléchargeable
|
||||
filename := "certificate_" + id + "_with_key.pem"
|
||||
c.Header("Content-Type", "application/x-pem-file")
|
||||
c.Header("Content-Disposition", "attachment; filename="+filename)
|
||||
c.String(http.StatusOK, combined)
|
||||
}
|
||||
|
||||
// ExportCertificateChain retourne une chaîne de certificats (certificat + CA parent)
|
||||
func ExportCertificateChain(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
|
||||
cert, err := certificateStore.GetCertificate(id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "certificat non trouvé"})
|
||||
return
|
||||
}
|
||||
|
||||
if cert.Cert == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "certificat non disponible"})
|
||||
return
|
||||
}
|
||||
|
||||
// Commencer par le certificat lui-même
|
||||
var chain []*pem.Block
|
||||
|
||||
pemCert := &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: cert.Cert.Raw,
|
||||
}
|
||||
chain = append(chain, pemCert)
|
||||
|
||||
// Essayer de trouver le certificat parent (issuer)
|
||||
// En cherchant par l'issuer DN
|
||||
if cert.Issuer != cert.Subject {
|
||||
allCerts := certificateStore.ListCertificates()
|
||||
for _, parentCert := range allCerts {
|
||||
if parentCert.Cert != nil && parentCert.Subject == cert.Issuer {
|
||||
parentPEM := &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: parentCert.Cert.Raw,
|
||||
}
|
||||
chain = append(chain, parentPEM)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Encoder tous les certificats en PEM
|
||||
var chainPEM string
|
||||
for _, block := range chain {
|
||||
chainPEM += string(pem.EncodeToMemory(block))
|
||||
}
|
||||
|
||||
// Retourner en tant que fichier téléchargeable
|
||||
filename := "certificate_chain_" + id + ".pem"
|
||||
c.Header("Content-Type", "application/x-pem-file")
|
||||
c.Header("Content-Disposition", "attachment; filename="+filename)
|
||||
c.String(http.StatusOK, chainPEM)
|
||||
}
|
||||
|
||||
@@ -41,6 +41,12 @@ func RegisterRoutesWithStore(router *gin.Engine, caStore storage.CertificateStor
|
||||
v1.GET("/certificates/:id", GetCertificate)
|
||||
v1.POST("/revoke", RevokeCertificate)
|
||||
|
||||
// Endpoints Export Certificats
|
||||
v1.GET("/certificates/:id/export/pem", ExportCertificatePEM)
|
||||
v1.GET("/certificates/:id/export/der", ExportCertificateDER)
|
||||
v1.GET("/certificates/:id/export/pem-with-key", ExportCertificateWithPrivateKeyPEM)
|
||||
v1.GET("/certificates/:id/export/chain", ExportCertificateChain)
|
||||
|
||||
// Endpoints CRL
|
||||
v1.GET("/crl", GetCRL)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user