212 lines
6.5 KiB
Python
212 lines
6.5 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""
|
||
|
|
Simulation script for VeriFactu API certificate validation.
|
||
|
|
Simulates a real user making API calls to register certificates.
|
||
|
|
"""
|
||
|
|
|
||
|
|
# ==================== CONFIGURABLE PASSWORD ====================
|
||
|
|
# THIS MUST MATCH THE PASSWORD IN generate_certs.py
|
||
|
|
CERT_PASSWORD = "Mecedora12@"
|
||
|
|
|
||
|
|
# RUTA DEL CERTIFICADO REAL
|
||
|
|
REAL_CERT_PATH = r"D:\Importante\53950250R_JOSEP VICENT_MESTRE__1752317947215 - copia.p12"
|
||
|
|
# ============================================================
|
||
|
|
|
||
|
|
import os
|
||
|
|
import sys
|
||
|
|
import base64
|
||
|
|
import json
|
||
|
|
import datetime
|
||
|
|
import subprocess
|
||
|
|
from pathlib import Path
|
||
|
|
from urllib.request import urlopen, Request
|
||
|
|
from urllib.error import URLError
|
||
|
|
|
||
|
|
API_URL = "http://localhost:6789"
|
||
|
|
CERTS_DIR = Path(__file__).parent / "certs"
|
||
|
|
|
||
|
|
# Try to import cryptography for RSA encryption
|
||
|
|
try:
|
||
|
|
from cryptography import x509
|
||
|
|
from cryptography.hazmat.primitives import serialization
|
||
|
|
from cryptography.hazmat.primitives.asymmetric import padding
|
||
|
|
from cryptography.hazmat.backends import default_backend
|
||
|
|
HAS_CRYPTO = True
|
||
|
|
except ImportError:
|
||
|
|
HAS_CRYPTO = False
|
||
|
|
|
||
|
|
|
||
|
|
def call_api(endpoint, data=None, method="GET"):
|
||
|
|
"""Make HTTP call to API."""
|
||
|
|
url = f"{API_URL}{endpoint}"
|
||
|
|
|
||
|
|
try:
|
||
|
|
if method == "GET":
|
||
|
|
req = Request(url, method="GET")
|
||
|
|
else:
|
||
|
|
req = Request(url, data=json.dumps(data).encode(), method="POST")
|
||
|
|
req.add_header("Content-Type", "application/json")
|
||
|
|
|
||
|
|
with urlopen(req, timeout=10) as response:
|
||
|
|
return json.loads(response.read().decode())
|
||
|
|
except URLError as e:
|
||
|
|
return {"error": str(e)}
|
||
|
|
except Exception as e:
|
||
|
|
return {"error": f"API call failed: {e}"}
|
||
|
|
|
||
|
|
|
||
|
|
def get_public_key():
|
||
|
|
"""Step 1: Get public key from API."""
|
||
|
|
print("\n" + "=" * 60)
|
||
|
|
print("STEP 1: Get Public Key from API")
|
||
|
|
print("=" * 60)
|
||
|
|
|
||
|
|
result = call_api("/api/v1/health")
|
||
|
|
print(f"Health check: {result}")
|
||
|
|
|
||
|
|
if "error" in result:
|
||
|
|
print(f"ERROR: API not running - {result}")
|
||
|
|
return None
|
||
|
|
|
||
|
|
result = call_api("/api/v1/auth/public-key")
|
||
|
|
|
||
|
|
if "public_key" not in result:
|
||
|
|
print(f"ERROR: No public key in response")
|
||
|
|
return None
|
||
|
|
|
||
|
|
pub_key_b64 = result["public_key"]
|
||
|
|
pub_key = base64.b64decode(pub_key_b64)
|
||
|
|
|
||
|
|
print(f"Public Key received (length: {len(pub_key)} bytes)")
|
||
|
|
return pub_key
|
||
|
|
|
||
|
|
|
||
|
|
def encrypt_password(public_key_pem, password):
|
||
|
|
"""Step 2: Encrypt password with public key (RSA)."""
|
||
|
|
print("\n" + "=" * 60)
|
||
|
|
print("STEP 2: Encrypt Password")
|
||
|
|
print("=" * 60)
|
||
|
|
|
||
|
|
if not HAS_CRYPTO:
|
||
|
|
print("WARNING: cryptography not available, using base64 (NOT SECURE!)")
|
||
|
|
return base64.b64encode(password.encode()).decode()
|
||
|
|
|
||
|
|
try:
|
||
|
|
public_key = serialization.load_pem_public_key(public_key_pem, default_backend())
|
||
|
|
|
||
|
|
encrypted = public_key.encrypt(
|
||
|
|
password.encode(),
|
||
|
|
padding.PKCS1v15()
|
||
|
|
)
|
||
|
|
|
||
|
|
encrypted_b64 = base64.b64encode(encrypted).decode()
|
||
|
|
print(f"Password encrypted (RSA)")
|
||
|
|
return encrypted_b64
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
print(f"ERROR encrypting: {e}")
|
||
|
|
return None
|
||
|
|
|
||
|
|
|
||
|
|
def register_certificate(cert_path, encrypted_password, test_name="default"):
|
||
|
|
"""Step 3: Register certificate via API."""
|
||
|
|
print("\n" + "=" * 60)
|
||
|
|
print("STEP 3: Register Certificate")
|
||
|
|
print("=" * 60)
|
||
|
|
print(f"Certificate path: {cert_path}")
|
||
|
|
print(f"Password (encrypted): {encrypted_password[:40]}...")
|
||
|
|
|
||
|
|
data = {
|
||
|
|
"cert_name": test_name.replace(" ", "_").replace("(", "").replace(")", ""),
|
||
|
|
"cert_path": cert_path,
|
||
|
|
"password_encrypted": encrypted_password
|
||
|
|
}
|
||
|
|
|
||
|
|
result = call_api("/api/v1/auth/register", data, method="POST")
|
||
|
|
|
||
|
|
return result
|
||
|
|
|
||
|
|
|
||
|
|
def test_certificate(cert_file, password, expected_result, test_name):
|
||
|
|
"""Test a single certificate via real API calls."""
|
||
|
|
print(f"\n{'#' * 60}")
|
||
|
|
print(f"# TEST: {test_name}")
|
||
|
|
print(f"# Expected: {expected_result}")
|
||
|
|
print(f"{'#' * 60}")
|
||
|
|
|
||
|
|
# Step 1: Get public key
|
||
|
|
pub_key = get_public_key()
|
||
|
|
if not pub_key:
|
||
|
|
print("[X] Cannot get public key - is API running?")
|
||
|
|
return
|
||
|
|
|
||
|
|
# Step 2: Encrypt password
|
||
|
|
enc_password = encrypt_password(pub_key, password)
|
||
|
|
if not enc_password:
|
||
|
|
print("[X] Cannot encrypt password")
|
||
|
|
return
|
||
|
|
|
||
|
|
# Step 3: Register certificate
|
||
|
|
cert_path = str(CERTS_DIR / cert_file)
|
||
|
|
result = register_certificate(cert_path, enc_password, test_name)
|
||
|
|
|
||
|
|
print(f"\nAPI Response:")
|
||
|
|
print(json.dumps(result, indent=2))
|
||
|
|
|
||
|
|
# Validate result
|
||
|
|
success = result.get("success", False)
|
||
|
|
error = result.get("error", "")
|
||
|
|
warnings = result.get("warnings", [])
|
||
|
|
|
||
|
|
if expected_result == "PASS" and success:
|
||
|
|
print(f"\n[OK] TEST PASSED")
|
||
|
|
elif expected_result == "FAIL" and not success:
|
||
|
|
print(f"\n[OK] TEST PASSED (expected failure: {error})")
|
||
|
|
elif expected_result == "PASS with WARNING" and success and warnings:
|
||
|
|
print(f"\n[OK] TEST PASSED (with warnings: {warnings})")
|
||
|
|
else:
|
||
|
|
print(f"\n[FAIL] TEST FAILED (success={success}, error={error})")
|
||
|
|
|
||
|
|
|
||
|
|
def main():
|
||
|
|
"""Main test runner."""
|
||
|
|
print("=" * 60)
|
||
|
|
print("VeriFactu API - Real User Simulation")
|
||
|
|
print("=" * 60)
|
||
|
|
print(f"\nAPI URL: {API_URL}")
|
||
|
|
print(f"Certs Directory: {CERTS_DIR}")
|
||
|
|
|
||
|
|
# Check if API is running
|
||
|
|
print("\nChecking if API is running...")
|
||
|
|
result = call_api("/api/v1/health")
|
||
|
|
if "error" in result:
|
||
|
|
print("ERROR: API not running!")
|
||
|
|
print("Start with: ./verifactu.exe")
|
||
|
|
return
|
||
|
|
|
||
|
|
print(f"API is running!")
|
||
|
|
|
||
|
|
print("\n" + "=" * 60)
|
||
|
|
print("Running Tests via Real API Calls")
|
||
|
|
print("=" * 60)
|
||
|
|
|
||
|
|
test_cases = [
|
||
|
|
("valid_365days.p12", CERT_PASSWORD, "PASS", "Valid certificate (365 days)"),
|
||
|
|
("valid_60days.p12", CERT_PASSWORD, "PASS", "Valid certificate (60 days)"),
|
||
|
|
("expired.p12", CERT_PASSWORD, "FAIL", "Expired certificate"),
|
||
|
|
("expiring_soon.p12", CERT_PASSWORD, "PASS with WARNING", "Expiring soon (15 days)"),
|
||
|
|
("not_yet_valid.p12", CERT_PASSWORD, "FAIL", "Not yet valid certificate"),
|
||
|
|
("wrong_password.p12", CERT_PASSWORD, "FAIL", "Wrong password"),
|
||
|
|
(REAL_CERT_PATH, CERT_PASSWORD, "PASS", "REAL certificate with REAL password"),
|
||
|
|
]
|
||
|
|
|
||
|
|
for cert_file, password, expected, test_name in test_cases:
|
||
|
|
test_certificate(cert_file, password, expected, test_name)
|
||
|
|
|
||
|
|
print("\n" + "=" * 60)
|
||
|
|
print("ALL TESTS COMPLETED")
|
||
|
|
print("=" * 60)
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|