Fix issuer links for PEM certificate chain imports

This commit is contained in:
2026-10-26 13:35:28 +00:00
parent 7523c0b8c4
commit c8b4e211a0
2 changed files with 116 additions and 8 deletions
+23 -8
View File
@@ -1284,6 +1284,12 @@ async def relink_certificates(vault: Vault) -> None:
for cert in certs:
by_hash.setdefault(cert.subject_hash, []).append(cert)
for cert in certs:
if cert.issuer_id == cert.id:
# Older imports could accidentally self-link a certificate. That
# is never a valid issuer reference in this model; clear it so the
# real issuer can be discovered below.
cert.issuer_id = None
await cert.save(update_fields=["issuer_id"])
if cert.issuer_id is not None:
continue
candidates = by_hash.get(cert.issuer_hash, [])
@@ -1337,7 +1343,15 @@ async def import_parsed_items(
imported: List[Dict[str, Any]] = []
skipped: List[Dict[str, Any]] = []
# Map XCA item ids to created rows so issuer/key links can be restored.
# Ordinary PEM/DER imports have no XCA ids; do not collapse them all onto
# a single ``None`` entry, which would create bogus key/issuer references.
id_map: Dict[int, Any] = {}
def remember_xca_row(item: ParsedItem, row: Any) -> None:
item_id = item.extra.get("xca_item_id")
if item_id is not None:
id_map[item_id] = row
# Keys first, then certificates, requests, CRLs, templates.
ordered = sorted(chosen, key=lambda item: {"key": 0, "certificate": 1, "request": 2, "crl": 3, "template": 4}.get(item.kind, 9))
for item in ordered:
@@ -1355,7 +1369,7 @@ async def import_parsed_items(
comment=str(item.extra.get("comment") or ""),
source=str(item.extra.get("source") or "imported"),
)
id_map[item.extra.get("xca_item_id")] = key
remember_xca_row(item, key)
imported.append({"kind": "key", "id": str(key.id), "name": key.name})
elif item.kind == "certificate":
info = cert_core.certificate_info(item.obj)
@@ -1364,7 +1378,7 @@ async def import_parsed_items(
continue
key_model = None
xca_key_id = item.extra.get("xca_key_id")
if xca_key_id in id_map:
if xca_key_id is not None and xca_key_id in id_map:
key_model = id_map[xca_key_id]
if key_model is None:
key_model = await Key.get_or_none(vault_id=vault.id, public_key_sha256=hashlib.sha256(info.public_key_der).hexdigest())
@@ -1398,7 +1412,7 @@ async def import_parsed_items(
extension_search_text(cert_core.certificate_extensions(item.obj)),
),
)
id_map[item.extra.get("xca_item_id")] = row
remember_xca_row(item, row)
if info.is_ca:
authority_extra = item.extra.get("authority") or {}
authority = await Authority.create(
@@ -1419,7 +1433,8 @@ async def import_parsed_items(
if await Request.filter(vault_id=vault.id, request_sha256=info.fingerprint_sha256).exists():
skipped.append({"kind": item.kind, "name": item.name, "reason": "duplicate"})
continue
key_model = id_map.get(item.extra.get("xca_key_id"))
xca_key_id = item.extra.get("xca_key_id")
key_model = id_map.get(xca_key_id) if xca_key_id is not None else None
if key_model is None:
key_model = await Key.get_or_none(vault_id=vault.id, public_key_sha256=hashlib.sha256(info.public_key_der).hexdigest())
row = await Request.create(
@@ -1442,7 +1457,7 @@ async def import_parsed_items(
extension_search_text(req_core.csr_extensions(item.obj)),
),
)
id_map[item.extra.get("xca_item_id")] = row
remember_xca_row(item, row)
imported.append({"kind": "request", "id": str(row.id), "name": row.name})
elif item.kind == "crl":
info = crl_core.crl_info(item.obj)
@@ -1463,7 +1478,7 @@ async def import_parsed_items(
signature_algorithm=info.signature_algorithm,
search_text=search_text(item.name, info.issuer.rfc4514_string(), info.crl_number),
)
id_map[item.extra.get("xca_item_id")] = row
remember_xca_row(item, row)
imported.append({"kind": "crl", "id": str(row.id), "name": row.name})
elif item.kind == "template":
template = item.obj
@@ -1476,7 +1491,7 @@ async def import_parsed_items(
settings=template.get("settings") or {},
search_text=search_text(template.get("name"), template.get("comment")),
)
id_map[item.extra.get("xca_item_id")] = row
remember_xca_row(item, row)
imported.append({"kind": "template", "id": str(row.id), "name": row.name})
except Exception as exc: # noqa: BLE001
skipped.append({"kind": item.kind, "name": item.name, "reason": str(exc)})
@@ -1486,7 +1501,7 @@ async def import_parsed_items(
continue
row = id_map.get(item.extra.get("xca_item_id"))
issuer_id = item.extra.get("xca_issuer_id")
if row is not None and issuer_id in id_map:
if row is not None and issuer_id is not None and issuer_id in id_map:
issuer = id_map[issuer_id]
if isinstance(issuer, Certificate):
row.issuer_id = issuer.id