Enforce CSR proof of possession during certificate issuance

This commit is contained in:
2026-10-25 21:21:16 +00:00
parent cc7059c100
commit a8d322776d
2 changed files with 63 additions and 0 deletions
@@ -672,6 +672,10 @@ async def issue_certificate(
request_obj = await find_request(vault, payload.get("request_id"))
if payload.get("request_id") and request_obj is None:
raise ValidationError("signing request not found")
if request_obj is not None:
csr = req_core.parse_csr(bytes(request_obj.req_der))
if not req_core.verify_csr(csr):
raise ValidationError("signing request has an invalid signature")
signer = await find_certificate(vault, payload.get("signer_id"))
if payload.get("signer_id") and signer is None:
+59
View File
@@ -91,6 +91,65 @@ class RouteExtraTests(unittest.TestCase):
response = await client.get(f"/api/vaults/{vault_id}/requests/{request['id']}")
assert response.json()["signed"] is True
@async_test
async def test_invalid_csr_signature_cannot_be_issued(self) -> None:
from cryptography import x509
from kca.core import keys as key_core
from kca.core import requests as req_core
from kca.models import Certificate
async with api_client() as client:
with oidc_user("alice"):
vault_id = await _vault(client)
ca_key = await _key(client, vault_id, "ca key")
ca = await _cert(client, vault_id, ca_key["id"], "Root CA", ca=True)
key = key_core.generate_key("rsa", 2048)
csr = req_core.build_csr(
subject=x509.Name(
[x509.NameAttribute(x509.oid.NameOID.COMMON_NAME, "invalid.example.com")]
),
private_key=key,
)
der = bytearray(req_core.csr_der(csr))
der[-1] ^= 1
invalid_der = bytes(der)
assert not req_core.verify_csr(req_core.parse_csr(invalid_der))
response = await client.post(
f"/api/vaults/{vault_id}/import",
json={
"filename": "invalid.csr",
"content_base64": base64.b64encode(invalid_der).decode(),
},
)
assert response.status_code == 200, response.text
imported = response.json()["imported"]
assert len(imported) == 1
assert imported[0]["kind"] == "request"
request_id = imported[0]["id"]
response = await client.get(f"/api/vaults/{vault_id}/requests/{request_id}")
assert response.status_code == 200, response.text
assert response.json()["signature_valid"] is False
now = datetime.now(timezone.utc)
response = await client.post(
f"/api/vaults/{vault_id}/certificates",
json={
"name": "invalid CSR cert",
"request_id": request_id,
"signer_id": ca["id"],
"not_before": now.isoformat(),
"not_after": (now + timedelta(days=30)).isoformat(),
},
)
assert response.status_code == 400, response.text
assert response.json()["error"] == "signing request has an invalid signature"
assert await Certificate.filter(request_id=request_id).count() == 0
response = await client.get(f"/api/vaults/{vault_id}/requests/{request_id}")
assert response.json()["signed"] is False
@async_test
async def test_token_authentication(self) -> None:
async with api_client() as client: