1. Ziel & Architektur
Die beiden Schnittstellen SOAP und CMP laufen in derselben Validierungsstrecke zusammen, nämlich in ZasCaIntegrationCertificationServiceImpl. Es gibt dort aber zwei getrennte Unterpfade:
- SOAP liefert immer ein PKCS#10 → Pfad
validateAndTransformX509CertificationRequest(...)bzw.validateAndTransformCvcCertificationRequest(...). - CMP liefert CRMF (
CertReqMessages, Body-TypTYPE_CERT_REQ) → PfadvalidateAndTransformToTmsX509CertificationRequests(...)bzw.validateAndTransformToTmsCvCertificationRequests(...).
Deshalb muss der Schalter an zwei X509-Stellen greifen: einmal im PKCS#10-Pfad (SOAP) und einmal im CRMF-Pfad (CMP). Der CVC-Pfad ist bereits ECC-only und bleibt unverändert.
2. Ist-Zustand (Code-Befund)
| Stelle | Datei:Zeile | Heutiges Verhalten |
|---|---|---|
VALID_CVC_ALGORITHM_IDS | TmsCaIntConstants.java:53 | 6 ECC-OIDs → bereits ECC-only |
VALID_X509_ALGORITHM_IDS | TmsCaIntConstants.java:62 | [id_rsa] → SOAP-X509 aktuell RSA-only (ECC wird abgelehnt) |
VALID_X509_ROOT_CA_ALGORITHM_IDS | TmsCaIntConstants.java:64 | [id_rsa, id_ecPublickey] → nur SubCA-Pfad, bleibt unverändert |
| SOAP-X509-Gate | ZasCaIntegrationCertificationServiceImpl.java:506 | checkPublicKeyAlgorithm(..., VALID_X509_ALGORITHM_IDS) |
| CMP-X509-Gate (implizit) | CertRequestUtils.java:693-701 | verifyX509Popo akzeptiert RSA oder EC → CMP-X509 aktuell beides |
| Signatur-Alg-Check PKCS#10 | CryptUtils.java:285-304 | Unterstützt ECDSA für EC-Key bereits; keine Änderung nötig |
| SubCA-Schalter (Vorbild) | SubcaAntragsdatenServiceImpl.java:498 | allowRsa=false ⇒ RSA abgelehnt |
allowRsa/validation.allowed.rsa greift ausschließlich im SubCA-Plugin (SubcaAntragsdatenServiceImpl). Die SOAP/CMP-Validierung ist hart kodiert (statische Konstante bzw. RSA-oder-EC-Check).
3. Schalter-Design
Neuer Blueprint-Property im bestehenden Persistent-ID de.authentidate.ors1.pki.tms.ca.integration.boundary:
certrequest.allowed.rsa=false
false(Default) = ECC-only: RSA-Requests werden an beiden Schnittstellen abgelehnt.true= Rückwärtskompatibel: ECC und RSA werden akzeptiert.
Der Wert wird als allowRsa-Feld in ZasCaIntegrationCertificationServiceImpl injiziert – exakt nach dem Muster des SubCA-Plugins. ECC wird im X509-Pfad damit immer erlaubt; RSA nur, wenn allowRsa=true.
4. Umsetzungsreihenfolge
Task 1 – Config & Schalter-Verdrahtung (Dev)
core/ca-intermediate/pms-ca-int-layer-impl/src/main/resources/OSGI-INF/blueprint/config.xmlcore/dist/assembly/src/main/unfiltered/etc/de.authentidate.ors1.pki.tms.ca.integration.boundary.cfg.github/docker/etc/de.authentidate.ors1.pki.tms.ca.integration.boundary.cfgcore/ca-intermediate/pms-ca-int-layer-impl/.../ZasCaIntegrationCertificationServiceImpl.java
- In
cm:default-propertiesdes Blueprints ergänzen:<cm:property name="certrequest.allowed.rsa" value="false" /> - Am Bean
caIntLayerCertificationServiceergänzen:<property name="allowRsa" value="${certrequest.allowed.rsa}" /> - In beiden
.cfg-Dateien die Zeilecertrequest.allowed.rsa=falseergänzen. - Feld + Setter im Service:
private boolean allowRsa = false; public void setAllowRsa(boolean allowRsa) { this.allowRsa = allowRsa; } mvn -pl core/ca-intermediate/pms-ca-int-layer-impl -am testläuft grün (noch kein Verhaltensänderung).
Task 2 – X509-Algorithmuslisten dynamisch machen (Dev)
core/ca-intermediate/pms-ca-int-layer-utils/.../TmsCaIntConstants.java:59-65core/ca-intermediate/pms-ca-int-layer-impl/.../ZasCaIntegrationCertificationServiceImpl.java
- Konstanten aufteilen (die alte
VALID_X509_ALGORITHM_IDSentfällt; einziger Nutzer war Zeile 506):public final static List<ASN1ObjectIdentifier> VALID_X509_EC_ALGORITHM_IDS = Arrays.asList(EgkIdentifiers.id_ecPublickey); public final static List<ASN1ObjectIdentifier> VALID_X509_RSA_ALGORITHM_IDS = Arrays.asList(EgkIdentifiers.id_rsa); - Im Service eine Helfermethode ergänzen:
private List<ASN1ObjectIdentifier> buildAllowedX509Algorithms() { List<ASN1ObjectIdentifier> allowed = new ArrayList<>(TmsCaIntConstants.VALID_X509_EC_ALGORITHM_IDS); if (allowRsa) { allowed.addAll(TmsCaIntConstants.VALID_X509_RSA_ALGORITHM_IDS); } return allowed; } VALID_X509_ROOT_CA_ALGORITHM_IDS(SubCA) nicht anfassen.
Task 3 – SOAP-PKCS#10-X509-Gate umstellen + Fehlermeldung verallgemeinern (Dev)
core/ca-intermediate/pms-ca-int-layer-impl/.../ZasCaIntegrationCertificationServiceImpl.java:230-282, 497-510
- Aufruf in
validateAndTransformX509CertificationRequestumstellen:result = checkPublicKeyAlgorithm(p10, pkcs10CertReq, buildAllowedX509Algorithms()); - In
checkPublicKeyAlgorithmdie hart kodierteexpected-Logik (Zeilen 259-278) durch eine generische ersetzen:if (!validAlgorithms.contains(publicKeyAlgorithm)) { String expected = validAlgorithms.stream() .map(ASN1ObjectIdentifier::getId) .collect(java.util.stream.Collectors.joining(" / ")); String detailedMessage = "Expected one of: " + expected + ", got: " + publicKeyAlgorithm.getId(); LOG.warn(detailedMessage); result.put(Boolean.FALSE, TmsCertificationRequestImpl.buildErrorRequestMsg(p10, PKIFailureTextMsg.REQUEST_KEY_ALG_ERROR, detailedMessage, PKIFailureInfo.badAlg)); return result; } - Hinweis: Dadurch ändert sich auch die CVC-Fehlermeldung textuell (bleibt korrekt, nur generischer formuliert).
Task 4 – CMP-CRMF-X509-Gate (Dev)
core/ca-intermediate/pms-ca-int-layer-impl/.../CertRequestUtils.java:681-720core/ca-intermediate/pms-ca-int-layer-impl/.../ZasCaIntegrationCertificationServiceImpl.java:883
- Signatur von
verifyX509Popoumboolean allowRsaerweitern (einziger Aufrufer ist Zeile 883). - RSA-Ablehnung in den bestehenden Keytype-Check einbauen:
ASN1ObjectIdentifier algo = pkInfo.getAlgorithm().getAlgorithm(); if (PKCSObjectIdentifiers.rsaEncryption.equals(algo) && !allowRsa) { throw new InvalidKeySpecException("RSA is disabled (ECC-only mode)"); } if (PKCSObjectIdentifiers.rsaEncryption.equals(algo) == false && X9ObjectIdentifiers.id_ecPublicKey.equals(algo) == false) { throw new InvalidKeySpecException("Wrong keytype for request of type x509, expected RSA or EC, was " + pkInfo.getAlgorithm().getAlgorithm().toString()); } - Aufruf anpassen:
boolean verifiedPop = CertRequestUtils.verifyX509Popo(certRequest, popo, buffer, allowRsa);
Task 5 – POP-Verifikation im SOAP-PKCS#10-Pfad EC-sicher machen (Dev)
core/ca-intermediate/pms-ca-int-layer-impl/.../ZasCaIntegrationCertificationServiceImpl.java:512-520
- Verifizieren, ob
org.xipki.security.SignerUtil.verifyPOP(pkcs10CertReq)EC-Public-Keys unterstützt. Falls ja: keine Codeänderung, nur Test. - Falls nein (oder um keytype-agnostisch zu sein), auf BouncyCastle umstellen:
ContentVerifierProvider cvp = new JcaContentVerifierProviderBuilder() .setProvider("BC") .build(pkcs10CertReq.getSubjectPublicKeyInfo()); if (!pkcs10CertReq.isSignatureValid(cvp)) { /* badPOP wie bisher */ } - Neuer Import:
org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder.
Task 6 – Unit- & Integrationstests (Test)
core/ca-intermediate/pms-ca-int-layer-impl/src/test/java/.../ZasCaIntegrationCertificationServiceImpl*Test.java (neu/erweitern)plugins/zertifikatsausstellung/zas-integrationtest/.../SoapTest.javaplugins/zertifikatsausstellung/zas-integrationtest/.../CMPTest.java
- Unit:
buildAllowedX509Algorithms()liefert beiallowRsa=falsenur EC, beitrueEC+RSA. - Unit:
checkPublicKeyAlgorithmlehnt RSA ab, wennallowRsa=false, und akzeptiert EC. - SOAP-Integrationstest: X509-EC-Request → akzeptiert; X509-RSA-Request →
PKIStatus.REJECTION+badAlg(DefaultallowRsa=false). - CMP-Integrationstest: X509-EC-Request → akzeptiert; X509-RSA-Request →
POPO_VERIFICATION_FAILEDbzw. Ablehnung. - Bestehende RSA-basierte Fixtures inventarisieren und auf EC umstellen oder mit
certrequest.allowed.rsa=trueals eigenem Testprofil laufen lassen (z. B.ZASServiceTest.java:422prüft heute eine RSA-Fehlermeldung).
Task 7 – Rollout & End-to-End (Integration)
core/dist/assembly/.../de.authentidate.ors1.pki.tms.ca.integration.boundary.cfg · .github/docker/... · Umgebungs-Configs
- In allen Zielumgebungen
certrequest.allowed.rsa=falsesetzen (ECC-only). - End-to-End gegen echte CA: je ein ECC-X509-Request über SOAP und CMP, plus ein RSA-Negativtest.
- Regression SubCA-Pfad (
VALID_X509_ROOT_CA_ALGORITHM_IDSunverändert) und CVC-Pfad bestätigen. - Doku (
README/Config-Doku) um den neuen Property ergänzen.
5. Aufwandsschätzung (Personentage)
| Phase | Umfang | Aufwand |
|---|---|---|
| Dev | Task 1–5 (Verdrahtung, Konstanten, zwei Gates, POP) | 2,5 – 3,5 PT |
| Test | Task 6 (Unit + SOAP/CMP-Integration + Fixture-Anpassung) | 2,5 – 3,5 PT |
| Integration | Task 7 (Config-Rollout, E2E, Regression, Doku) | 1,5 – 2,0 PT |
| Summe | ohne Puffer | 6,5 – 9,0 PT |
Mit ~20 % Puffer für den offenen Punkt „SignerUtil.verifyPOP EC-Support“ und Umgebungsüberraschungen: ca. 8 – 11 PT.
6. Risiken & offene Punkte
SignerUtil.verifyPOP(xipki, extern) muss EC können – sonst greift der Fallback aus Task 5.- Default
falseändert das Verhalten in allen Umgebungen; Bestands-RSA-Integrationen brechen beim Rollout – daher explizittrueals Übergangsschalter dokumentieren. - Die CVC-Fehlermeldung in
checkPublicKeyAlgorithmwird durch den generischen Text leicht verändert (nur Text, kein Verhalten). - Kurven-Whitelisting (welche EC-Kurven erlaubt sind) ist nicht Teil dieses Plans – aktuell wird nur der Algorithmus (EC vs. RSA) geprüft. Falls Kurven eingeschränkt werden sollen, wäre das ein Folge-Ticket.