202 lines
6.1 KiB
Go
202 lines
6.1 KiB
Go
package cert
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"math/big"
|
|
"testing"
|
|
"time"
|
|
|
|
"software.sslmate.com/src/go-pkcs12"
|
|
)
|
|
|
|
const testPassword = "test-password-123"
|
|
|
|
// generarP12 crea un certificado autofirmado y lo empaqueta en P12.
|
|
// notBefore y notAfter controlan la validez del certificado.
|
|
func generarP12(t *testing.T, cn string, notBefore, notAfter time.Time) []byte {
|
|
t.Helper()
|
|
|
|
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
if err != nil {
|
|
t.Fatalf("generando clave RSA: %v", err)
|
|
}
|
|
|
|
template := &x509.Certificate{
|
|
SerialNumber: big.NewInt(1),
|
|
Subject: pkix.Name{CommonName: cn},
|
|
NotBefore: notBefore,
|
|
NotAfter: notAfter,
|
|
}
|
|
|
|
certDER, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key)
|
|
if err != nil {
|
|
t.Fatalf("creando certificado: %v", err)
|
|
}
|
|
|
|
x509Cert, err := x509.ParseCertificate(certDER)
|
|
if err != nil {
|
|
t.Fatalf("parseando certificado: %v", err)
|
|
}
|
|
|
|
p12Data, err := pkcs12.Modern.Encode(key, x509Cert, nil, testPassword)
|
|
if err != nil {
|
|
t.Fatalf("codificando P12: %v", err)
|
|
}
|
|
|
|
return p12Data
|
|
}
|
|
|
|
func TestValidateP12Bytes_CertificadoValido365Dias(t *testing.T) {
|
|
now := time.Now()
|
|
p12 := generarP12(t, "Valid 365 days", now.Add(-time.Hour), now.Add(365*24*time.Hour))
|
|
|
|
result := ValidateP12Bytes(p12, testPassword)
|
|
|
|
if !result.Valid {
|
|
t.Errorf("esperado válido, obtuvo error: %s", result.Error)
|
|
}
|
|
if result.Error != "" {
|
|
t.Errorf("no debería haber error, obtuvo: %s", result.Error)
|
|
}
|
|
if len(result.Warnings) > 0 {
|
|
t.Errorf("no debería haber warnings para cert de 365 días, obtuvo: %v", result.Warnings)
|
|
}
|
|
if result.CertInfo == nil {
|
|
t.Fatal("CertInfo no debería ser nil para cert válido")
|
|
}
|
|
if result.CertInfo.DaysUntilExpiry < 360 {
|
|
t.Errorf("días hasta expiración: esperado ~365, obtuvo %d", result.CertInfo.DaysUntilExpiry)
|
|
}
|
|
}
|
|
|
|
func TestValidateP12Bytes_CertificadoValido60Dias(t *testing.T) {
|
|
now := time.Now()
|
|
p12 := generarP12(t, "Valid 60 days", now.Add(-time.Hour), now.Add(60*24*time.Hour))
|
|
|
|
result := ValidateP12Bytes(p12, testPassword)
|
|
|
|
if !result.Valid {
|
|
t.Errorf("esperado válido, obtuvo error: %s", result.Error)
|
|
}
|
|
if len(result.Warnings) > 0 {
|
|
t.Errorf("no debería haber warnings para cert de 60 días, obtuvo: %v", result.Warnings)
|
|
}
|
|
if result.CertInfo.DaysUntilExpiry < 55 || result.CertInfo.DaysUntilExpiry > 65 {
|
|
t.Errorf("días hasta expiración: esperado ~60, obtuvo %d", result.CertInfo.DaysUntilExpiry)
|
|
}
|
|
}
|
|
|
|
func TestValidateP12Bytes_CertificadoExpirado(t *testing.T) {
|
|
now := time.Now()
|
|
p12 := generarP12(t, "Expired", now.Add(-20*24*time.Hour), now.Add(-5*24*time.Hour))
|
|
|
|
result := ValidateP12Bytes(p12, testPassword)
|
|
|
|
if result.Valid {
|
|
t.Error("esperado inválido para certificado expirado")
|
|
}
|
|
if result.Error != "certificate_expired" {
|
|
t.Errorf("error esperado: certificate_expired, obtuvo: %s", result.Error)
|
|
}
|
|
if result.CertInfo == nil || !result.CertInfo.Expired {
|
|
t.Error("CertInfo.Expired debería ser true")
|
|
}
|
|
}
|
|
|
|
func TestValidateP12Bytes_CertificadoCaducaProximamente(t *testing.T) {
|
|
now := time.Now()
|
|
p12 := generarP12(t, "Expiring Soon", now.Add(-time.Hour), now.Add(15*24*time.Hour))
|
|
|
|
result := ValidateP12Bytes(p12, testPassword)
|
|
|
|
if !result.Valid {
|
|
t.Errorf("esperado válido con warning, obtuvo error: %s", result.Error)
|
|
}
|
|
if len(result.Warnings) == 0 {
|
|
t.Error("esperado warning certificate_expiring_soon para cert que caduca en 15 días")
|
|
}
|
|
if len(result.Warnings) > 0 && result.Warnings[0] != "certificate_expiring_soon" {
|
|
t.Errorf("warning esperado: certificate_expiring_soon, obtuvo: %s", result.Warnings[0])
|
|
}
|
|
if result.CertInfo == nil || !result.CertInfo.ExpiringSoon {
|
|
t.Error("CertInfo.ExpiringSoon debería ser true")
|
|
}
|
|
}
|
|
|
|
func TestValidateP12Bytes_CertificadoAunNoValido(t *testing.T) {
|
|
now := time.Now()
|
|
p12 := generarP12(t, "Not Yet Valid", now.Add(30*24*time.Hour), now.Add(395*24*time.Hour))
|
|
|
|
result := ValidateP12Bytes(p12, testPassword)
|
|
|
|
if result.Valid {
|
|
t.Error("esperado inválido para certificado aún no vigente")
|
|
}
|
|
if result.Error != "certificate_not_yet_valid" {
|
|
t.Errorf("error esperado: certificate_not_yet_valid, obtuvo: %s", result.Error)
|
|
}
|
|
}
|
|
|
|
func TestValidateP12Bytes_ContraseñaIncorrecta(t *testing.T) {
|
|
now := time.Now()
|
|
p12 := generarP12(t, "Valid Cert", now.Add(-time.Hour), now.Add(365*24*time.Hour))
|
|
|
|
result := ValidateP12Bytes(p12, "contraseña-incorrecta")
|
|
|
|
if result.Valid {
|
|
t.Error("esperado inválido con contraseña incorrecta")
|
|
}
|
|
if result.Error != "invalid_password_or_format" {
|
|
t.Errorf("error esperado: invalid_password_or_format, obtuvo: %s", result.Error)
|
|
}
|
|
}
|
|
|
|
func TestValidateP12Bytes_BytesCorruptos(t *testing.T) {
|
|
result := ValidateP12Bytes([]byte("esto no es un P12 valido"), testPassword)
|
|
|
|
if result.Valid {
|
|
t.Error("esperado inválido para bytes corruptos")
|
|
}
|
|
if result.Error != "invalid_password_or_format" {
|
|
t.Errorf("error esperado: invalid_password_or_format, obtuvo: %s", result.Error)
|
|
}
|
|
}
|
|
|
|
func TestValidateP12Bytes_UmbralWarning30Dias(t *testing.T) {
|
|
now := time.Now()
|
|
|
|
// Exactamente en el umbral: 30 días → debe dar warning
|
|
p12Umbral := generarP12(t, "At threshold", now.Add(-time.Hour), now.Add(30*24*time.Hour))
|
|
resultUmbral := ValidateP12Bytes(p12Umbral, testPassword)
|
|
if !resultUmbral.Valid {
|
|
t.Errorf("cert en umbral debe ser válido, obtuvo error: %s", resultUmbral.Error)
|
|
}
|
|
if len(resultUmbral.Warnings) == 0 {
|
|
t.Error("cert con exactamente 30 días debe tener warning")
|
|
}
|
|
|
|
// 31 días → NO debe dar warning
|
|
p12Ok := generarP12(t, "Just above threshold", now.Add(-time.Hour), now.Add(31*24*time.Hour))
|
|
resultOk := ValidateP12Bytes(p12Ok, testPassword)
|
|
if !resultOk.Valid {
|
|
t.Errorf("cert con 31 días debe ser válido, obtuvo error: %s", resultOk.Error)
|
|
}
|
|
if len(resultOk.Warnings) > 0 {
|
|
t.Error("cert con 31 días NO debe tener warning")
|
|
}
|
|
}
|
|
|
|
func TestValidateP12_ArchivoNoExiste(t *testing.T) {
|
|
result := ValidateP12("/ruta/que/no/existe.p12", testPassword)
|
|
|
|
if result.Valid {
|
|
t.Error("esperado inválido para archivo inexistente")
|
|
}
|
|
if result.Error != "file_not_found" {
|
|
t.Errorf("error esperado: file_not_found, obtuvo: %s", result.Error)
|
|
}
|
|
}
|