diff --git a/.github/workflows/lint-flak8.yml b/.github/workflows/lint-flak8.yml index 99028a586d4..2136a4953b3 100644 --- a/.github/workflows/lint-flak8.yml +++ b/.github/workflows/lint-flak8.yml @@ -50,7 +50,7 @@ jobs: - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude=jans-linux-setup/jans_setup/setup_app/pylib,jans-linux-setup/jans_setup/static/extension + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude=jans_setup/setup_app/pylib,jans_setup/static/extension # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --exclude=jans-linux-setup/jans_setup/setup_app/pylib,jans-linux-setup/jans_setup/static/extension + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --exclude=jans_setup/setup_app/pylib,jans_setup/static/extension working-directory: ${{ matrix.python-projects }} diff --git a/jans-cli-tui/cli_tui/plugins/120_lock/main.py b/jans-cli-tui/cli_tui/plugins/120_lock/main.py index 83a6f8c97bd..dd4f6c7721f 100644 --- a/jans-cli-tui/cli_tui/plugins/120_lock/main.py +++ b/jans-cli-tui/cli_tui/plugins/120_lock/main.py @@ -13,7 +13,7 @@ from utils.multi_lang import _ from utils.utils import DialogUtils, common_data -from utils.static import cli_style +from utils.static import cli_style, common_strings class Plugin(DialogUtils): diff --git a/jans-fido2/model/src/main/java/io/jans/fido2/model/attestation/AttestationResult.java b/jans-fido2/model/src/main/java/io/jans/fido2/model/attestation/AttestationResult.java index db4fd022a6b..6f00edd8158 100644 --- a/jans-fido2/model/src/main/java/io/jans/fido2/model/attestation/AttestationResult.java +++ b/jans-fido2/model/src/main/java/io/jans/fido2/model/attestation/AttestationResult.java @@ -1,8 +1,9 @@ package io.jans.fido2.model.attestation; -import java.util.HashMap; +import java.util.Map; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; @JsonIgnoreProperties(ignoreUnknown = true) public class AttestationResult { @@ -10,7 +11,7 @@ public class AttestationResult { private String type; private String rawId; private Response response; - private HashMap clientExtensionResults; + private Map clientExtensionResults; private String authentictatorAttachment; public String getId() { @@ -37,11 +38,11 @@ public void setResponse(Response response) { this.response = response; } - public HashMap getClientExtensionResults() { + public Map getClientExtensionResults() { return clientExtensionResults; } - public void setClientExtensionResults(HashMap clientExtensionResults) { + public void setClientExtensionResults(Map clientExtensionResults) { this.clientExtensionResults = clientExtensionResults; } @@ -61,8 +62,6 @@ public void setRawId(String rawId) { this.rawId = rawId; } - - @Override public String toString() { return "AttestationResult [id=" + id + ", type=" + type + ", rawId=" + rawId + ", response=" + response @@ -70,5 +69,32 @@ public String toString() { + authentictatorAttachment + "]"; } +} + +class ClientExtensionResults { + @JsonProperty("credProps") + private CredProps credProps; + + public CredProps getCredProps() { + return credProps; + } + + public void setCredProps(CredProps credProps) { + this.credProps = credProps; + } + +} + +class CredProps { + @JsonProperty("rk") + private boolean rk; + + public boolean isRk() { + return rk; + } + + public void setRk(boolean rk) { + this.rk = rk; + } } diff --git a/jans-fido2/server/src/main/java/io/jans/fido2/model/auth/AuthData.java b/jans-fido2/server/src/main/java/io/jans/fido2/model/auth/AuthData.java index e2d97bcfa77..efaa46d0c5e 100644 --- a/jans-fido2/server/src/main/java/io/jans/fido2/model/auth/AuthData.java +++ b/jans-fido2/server/src/main/java/io/jans/fido2/model/auth/AuthData.java @@ -18,6 +18,8 @@ package io.jans.fido2.model.auth; +import java.util.Arrays; + /** * authData structure from https://www.w3.org/TR/webauthn/#authenticator-data * @author Yuriy Movchan @@ -124,4 +126,13 @@ public AuthData setExtensions(byte[] extensions) { return this; } + @Override + public String toString() { + return "AuthData [rpIdHash=" + Arrays.toString(rpIdHash) + ", flags=" + Arrays.toString(flags) + ", counters=" + + Arrays.toString(counters) + ", aaguid=" + Arrays.toString(aaguid) + ", credId=" + + Arrays.toString(credId) + ", attestationBuffer=" + Arrays.toString(attestationBuffer) + ", keyType=" + + keyType + ", cosePublicKey=" + Arrays.toString(cosePublicKey) + ", extensions=" + + Arrays.toString(extensions) + ", authDataDecoded=" + Arrays.toString(authDataDecoded) + "]"; + } + } diff --git a/jans-fido2/server/src/main/java/io/jans/fido2/service/CertificateService.java b/jans-fido2/server/src/main/java/io/jans/fido2/service/CertificateService.java index c1955d558d0..1f2ff057282 100644 --- a/jans-fido2/server/src/main/java/io/jans/fido2/service/CertificateService.java +++ b/jans-fido2/server/src/main/java/io/jans/fido2/service/CertificateService.java @@ -72,6 +72,7 @@ public X509Certificate getCertificate(InputStream is) { return certificate; } catch (CertificateException e) { + throw errorResponseFactory.badRequestException(AttestationErrorResponseType.INVALID_CERTIFICATE, e.getMessage(), e); } } diff --git a/jans-fido2/server/src/main/java/io/jans/fido2/service/mds/AttestationCertificateService.java b/jans-fido2/server/src/main/java/io/jans/fido2/service/mds/AttestationCertificateService.java index ab870d59fad..9575304490e 100644 --- a/jans-fido2/server/src/main/java/io/jans/fido2/service/mds/AttestationCertificateService.java +++ b/jans-fido2/server/src/main/java/io/jans/fido2/service/mds/AttestationCertificateService.java @@ -164,13 +164,46 @@ private JsonNode getMetadataForAuthenticator(AuthData authData) { return metadataForAuthenticator; } + + + public JsonNode getMetadataForU2fAuthenticator(String attestationCertificateKeyIdentifiers) { + + Fido2Configuration fido2Configuration = appConfiguration.getFido2Configuration(); + JsonNode metadataForAuthenticator; + if (fido2Configuration.isEnterpriseAttestation()) { + metadataForAuthenticator = localMdsService.getAuthenticatorsMetadata(attestationCertificateKeyIdentifiers); + if (metadataForAuthenticator == null) { + metadataForAuthenticator = dataMapperService.createObjectNode(); + } + } else { + try { + log.info("No Local metadata for authenticator {}. Checking for metadata MDS3 blob", + attestationCertificateKeyIdentifiers); + if (!fido2Configuration.isDisableMetadataService() ) { + JsonNode metadata = mdsService.fetchMetadata(attestationCertificateKeyIdentifiers.getBytes()); + commonVerifiers.verifyThatMetadataIsValid(metadata); + metadataForAuthenticator = metadata; + } else { + metadataForAuthenticator = dataMapperService.createObjectNode(); + log.debug("disableMetadataService has been configured as true"); + } + } catch (Fido2RuntimeException ex) { + log.warn("Failed to get metadata from Fido2 meta-data server: {}", ex.getMessage(), ex); + + metadataForAuthenticator = dataMapperService.createObjectNode(); + } + } + return metadataForAuthenticator; + } + + public List getAttestationRootCertificates(AuthData authData, List attestationCertificates) { JsonNode metadataForAuthenticator = getMetadataForAuthenticator(authData); return getAttestationRootCertificates(metadataForAuthenticator, attestationCertificates); } public X509TrustManager populateTrustManager(AuthData authData, List attestationCertificates) { - String aaguid = Hex.encodeHexString(authData.getAaguid()); + String aaguid = Hex.encodeHexString(authData. getAaguid()); List trustedCertificates = getAttestationRootCertificates(authData, attestationCertificates); if ((trustedCertificates == null) || (trustedCertificates.size() == 0)) { log.error("Failed to get trusted certificates"); diff --git a/jans-fido2/server/src/main/java/io/jans/fido2/service/mds/MdsService.java b/jans-fido2/server/src/main/java/io/jans/fido2/service/mds/MdsService.java index 233233847ae..6d0faa12676 100644 --- a/jans-fido2/server/src/main/java/io/jans/fido2/service/mds/MdsService.java +++ b/jans-fido2/server/src/main/java/io/jans/fido2/service/mds/MdsService.java @@ -36,10 +36,8 @@ import io.jans.fido2.model.mds.AuthenticatorCertificationStatus; import io.jans.fido2.service.Base64Service; import io.jans.fido2.service.DataMapperService; -import io.jans.fido2.service.client.ResteasyClientFactory; import io.jans.fido2.service.verifier.CommonVerifiers; import io.jans.service.cdi.event.ApplicationInitialized; - import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Observes; import jakarta.inject.Inject; diff --git a/jans-fido2/server/src/main/java/io/jans/fido2/service/mds/TocService.java b/jans-fido2/server/src/main/java/io/jans/fido2/service/mds/TocService.java index 2147190a5a9..ddbd542baf0 100644 --- a/jans-fido2/server/src/main/java/io/jans/fido2/service/mds/TocService.java +++ b/jans-fido2/server/src/main/java/io/jans/fido2/service/mds/TocService.java @@ -60,35 +60,36 @@ import static java.time.format.DateTimeFormatter.ISO_DATE; /** - * TOC is parsed and Hashmap containing JSON object of individual Authenticators is created. + * TOC is parsed and Hashmap containing JSON object of individual Authenticators + * is created. * */ @ApplicationScoped public class TocService { - @Inject - private Logger log; + @Inject + private Logger log; - @Inject - private DataMapperService dataMapperService; + @Inject + private DataMapperService dataMapperService; - @Inject - private CertificateVerifier certificateVerifier; + @Inject + private CertificateVerifier certificateVerifier; - @Inject - private CertificateService certificateService; + @Inject + private CertificateService certificateService; - @Inject - private Base64Service base64Service; + @Inject + private Base64Service base64Service; - @Inject - private AppConfiguration appConfiguration; + @Inject + private AppConfiguration appConfiguration; @Inject private ConfigurationFactory configurationFactory; - + @Inject - private FetchMdsProviderService fetchMdsProviderService; + private FetchMdsProviderService fetchMdsProviderService; @Inject private DBDocumentService dbDocumentService; @@ -96,75 +97,53 @@ public class TocService { @Inject private Fido2Service fido2Service; - private Map tocEntries; - - private LocalDate nextUpdate; - private MessageDigest digester; + private Map tocEntries; + + private LocalDate nextUpdate; + private MessageDigest digester; - public LocalDate getNextUpdateDate() - { - return nextUpdate; - } + public LocalDate getNextUpdateDate() { + return nextUpdate; + } - public void init(@Observes @ApplicationInitialized(ApplicationScoped.class) Object init) { - refresh(); + public void init(@Observes @ApplicationInitialized(ApplicationScoped.class) Object init) { + refresh(); loadMetadataServiceExternalProvider(); - } + } - public void refresh() { + public void refresh() { this.tocEntries = Collections.synchronizedMap(new HashMap()); if (appConfiguration.getFido2Configuration().isDisableMetadataService()) { log.debug("SkipDownloadMds is enabled"); - } else { + } else { tocEntries.putAll(parseTOCs()); } } - + private Map parseTOCs() { Fido2Configuration fido2Configuration = appConfiguration.getFido2Configuration(); + List> maps = new ArrayList<>(); if (fido2Configuration == null) { log.warn("Fido2 configuration not exists"); return new HashMap(); } String mdsTocRootCertsFolder = fido2Configuration.getMdsCertsFolder(); - String mdsTocFilesFolder = fido2Configuration.getMdsTocsFolder(); - if (StringHelper.isEmpty(mdsTocRootCertsFolder) || StringHelper.isEmpty(mdsTocFilesFolder)) { + if (StringHelper.isEmpty(mdsTocRootCertsFolder)) { log.warn("Fido2 MDS cert and TOC properties should be set"); return new HashMap(); } log.info("Populating TOC certs entries from {}", mdsTocRootCertsFolder); - log.info("Populating TOC entries from {}", mdsTocFilesFolder); try { - List tocRootCertsDocuments = dbDocumentService.getDocumentsByFilePath(mdsTocRootCertsFolder); - } catch (Exception e) { - log.error("Failed to fetch toc Root Certs Documents ", e); - throw new DocumentException(e); - } + Document tocRootCertsDocument = dbDocumentService.getDocumentByDisplayName("mdsTocsFolder"); + Pair> result = parseTOC(mdsTocRootCertsFolder, tocRootCertsDocument.getDocument()); + log.info("Get TOC {} entries with nextUpdate date {}", result.getSecond().size(), result.getFirst()); - List tocFilesdocuments = new ArrayList<>(); - try { - tocFilesdocuments = dbDocumentService.getDocumentsByFilePath(mdsTocFilesFolder); + maps.add(result.getSecond()); } catch (Exception e) { - log.error("Failed to fetch toc Files Documents ", e); - throw new DocumentException(e); - } - - List> maps = new ArrayList<>(); - for(Document document :tocFilesdocuments){ - try { - Pair> result = parseTOC(mdsTocRootCertsFolder, document.getDocument()); - log.info("Get TOC {} entries with nextUpdate date {}", result.getSecond().size(), - result.getFirst()); - - maps.add(result.getSecond()); - } catch (IOException e) { - log.warn("Can't access or open path: {}", document.getFileName(), e); - } catch (ParseException e) { - log.warn("Can't parse path: {}", document.getFileName(), e); - } - } + log.warn("Can't access document : {}", e.getMessage(), e); + } return mergeAndResolveDuplicateEntries(maps); } @@ -183,104 +162,106 @@ private Pair> parseTOC(String mdsTocRootCertsFo } } - private JWSVerifier resolveVerifier(JWSAlgorithm algorithm, String mdsTocRootCertsFolder, List certificateChain) { - List x509CertificateChain = certificateService.getCertificates(certificateChain); - List x509TrustedCertificates = certificateService.getCertificates(mdsTocRootCertsFolder); + private JWSVerifier resolveVerifier(JWSAlgorithm algorithm, String mdsTocRootCertsFolder, + List certificateChain) { + List x509CertificateChain = certificateService.getCertificates(certificateChain); + List x509TrustedCertificates = certificateService.getCertificates(mdsTocRootCertsFolder); List enabledFidoAlgorithms = appConfiguration.getFido2Configuration().getEnabledFidoAlgorithms(); - X509Certificate verifiedCert = certificateVerifier.verifyAttestationCertificates(x509CertificateChain, x509TrustedCertificates); - //possible set of algos are : ES256, RS256, PS256, ED256, ED25519 - // no support for ED256 in JOSE library + X509Certificate verifiedCert = certificateVerifier.verifyAttestationCertificates(x509CertificateChain, + x509TrustedCertificates); + // possible set of algos are : ES256, RS256, PS256, ED256, ED25519 + // no support for ED256 in JOSE library - if(!(enabledFidoAlgorithms.contains(algorithm.getName()) || enabledFidoAlgorithms.contains(Curve.Ed25519.getName()))) { - throw new Fido2RuntimeException("Unable to create a verifier for algorithm " + algorithm + " as it is not supported. Add this algorithm in the FIDO2 configuration to support it."); + if (!(enabledFidoAlgorithms.contains(algorithm.getName()) + || enabledFidoAlgorithms.contains(Curve.Ed25519.getName()))) { + throw new Fido2RuntimeException("Unable to create a verifier for algorithm " + algorithm + + " as it is not supported. Add this algorithm in the FIDO2 configuration to support it."); } - - if (JWSAlgorithm.ES256.equals(algorithm)) { - log.debug("resolveVerifier : ES256"); - try { - return new ECDSAVerifier((ECPublicKey) verifiedCert.getPublicKey()); - } catch (JOSEException e) { - throw new Fido2RuntimeException("Unable to create verifier for algorithm " + algorithm, e); - } - } - else if (JWSAlgorithm.RS256.equals(algorithm) || JWSAlgorithm.PS256.equals(algorithm)) { - log.debug("resolveVerifier : RS256"); - return new RSASSAVerifier((RSAPublicKey) verifiedCert.getPublicKey()); - } - else if (JWSAlgorithm.EdDSA.equals(algorithm) && ((OctetKeyPair) verifiedCert.getPublicKey()).getCurve().equals(Curve.Ed25519)) { + + if (JWSAlgorithm.ES256.equals(algorithm)) { + log.debug("resolveVerifier : ES256"); + try { + return new ECDSAVerifier((ECPublicKey) verifiedCert.getPublicKey()); + } catch (JOSEException e) { + throw new Fido2RuntimeException("Unable to create verifier for algorithm " + algorithm, e); + } + } else if (JWSAlgorithm.RS256.equals(algorithm) || JWSAlgorithm.PS256.equals(algorithm)) { + log.debug("resolveVerifier : RS256"); + return new RSASSAVerifier((RSAPublicKey) verifiedCert.getPublicKey()); + } else if (JWSAlgorithm.EdDSA.equals(algorithm) + && ((OctetKeyPair) verifiedCert.getPublicKey()).getCurve().equals(Curve.Ed25519)) { log.debug("resolveVerifier : Ed25519"); try { - return new Ed25519Verifier((OctetKeyPair) verifiedCert.getPublicKey()); + return new Ed25519Verifier((OctetKeyPair) verifiedCert.getPublicKey()); } catch (JOSEException e) { throw new Fido2RuntimeException("Error during resolving Ed25519 verifier " + e.getMessage()); } + } else { + throw new Fido2RuntimeException("Don't know what to do with " + algorithm); } - else { - throw new Fido2RuntimeException("Don't know what to do with " + algorithm); - } - } - - private MessageDigest resolveDigester(JWSAlgorithm algorithm) { - // fix: algorithm RS256 added for https://github.com/GluuFederation/fido2/issues/16 - if (JWSAlgorithm.ES256.equals(algorithm) || JWSAlgorithm.RS256.equals(algorithm) ) { - return DigestUtils.getSha256Digest(); - } else if(JWSAlgorithm.EdDSA.equals(algorithm)) { + } + + private MessageDigest resolveDigester(JWSAlgorithm algorithm) { + // fix: algorithm RS256 added for + // https://github.com/GluuFederation/fido2/issues/16 + if (JWSAlgorithm.ES256.equals(algorithm) || JWSAlgorithm.RS256.equals(algorithm)) { + return DigestUtils.getSha256Digest(); + } else if (JWSAlgorithm.EdDSA.equals(algorithm)) { return DigestUtils.getSha512Digest(); } else { - throw new Fido2RuntimeException("Don't know what to do with " + algorithm); - } - } - - private Map mergeAndResolveDuplicateEntries(List> maps) { - Map allEntries = new HashMap<>(); - Map a[] = new Map[maps.size()]; - maps.toArray(a); - - allEntries.putAll( - Stream.of(a).flatMap(m -> m.entrySet().stream()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> { - log.warn("Duplicate values {} {}", v1, v2); - - LocalDate dateV1 = getDate(v1); - LocalDate dateV2 = getDate(v2); - - JsonNode result; - if (dateV1.isAfter(dateV2)) { - result = v1; - } else { - result = v2; - } - - log.debug("Selected value {} ", result); - - return result; - }))); - - return allEntries; - } - - - private LocalDate getDate(JsonNode node) { - JsonNode dateNode = node.get("timeOfLastStatusChange"); - LocalDate date; - if (dateNode != null) { - date = LocalDate.parse(dateNode.asText(), ISO_DATE); - } else { - date = LocalDate.now(); - } - - return date; - } - - public JsonNode getAuthenticatorsMetadata(String aaguid) { - - return tocEntries.get(aaguid); - } - - public MessageDigest getDigester() { - return digester; - } - + throw new Fido2RuntimeException("Don't know what to do with " + algorithm); + } + } + + private Map mergeAndResolveDuplicateEntries(List> maps) { + Map allEntries = new HashMap<>(); + Map a[] = new Map[maps.size()]; + maps.toArray(a); + + allEntries.putAll(Stream.of(a).flatMap(m -> m.entrySet().stream()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> { + log.warn("Duplicate values {} {}", v1, v2); + + LocalDate dateV1 = getDate(v1); + LocalDate dateV2 = getDate(v2); + + JsonNode result; + if (dateV1.isAfter(dateV2)) { + result = v1; + } else { + result = v2; + } + + log.debug("Selected value {} ", result); + + return result; + }))); + + return allEntries; + } + + private LocalDate getDate(JsonNode node) { + JsonNode dateNode = node.get("timeOfLastStatusChange"); + LocalDate date; + if (dateNode != null) { + date = LocalDate.parse(dateNode.asText(), ISO_DATE); + } else { + date = LocalDate.now(); + } + + return date; + } + + public JsonNode getAuthenticatorsMetadata(String aaguid) { + + return tocEntries.get(aaguid); + } + + public MessageDigest getDigester() { + return digester; + } + public boolean downloadMdsFromServer(URL metadataUrl) { Fido2Configuration fido2Configuration = appConfiguration.getFido2Configuration(); @@ -288,11 +269,11 @@ public boolean downloadMdsFromServer(URL metadataUrl) { String mdsTocFilesFolder = fido2Configuration.getMdsTocsFolder(); try { List documents = dbDocumentService.getDocumentsByFilePath(mdsTocFilesFolder); - for (Document document : documents){ + for (Document document : documents) { dbDocumentService.removeDocument(document); } } catch (Exception e) { - log.error("Failed to remove old document of mdsTocFilesFolder" , e); + log.error("Failed to remove old document of mdsTocFilesFolder", e); throw new DocumentException(e); } @@ -313,14 +294,14 @@ public boolean downloadMdsFromServer(URL metadataUrl) { document.setEnabled(true); dbDocumentService.addDocument(document); } catch (Exception e) { - log.error("Failed to add new document of mdsTocFilesFolder" , e); + log.error("Failed to add new document of mdsTocFilesFolder", e); throw new DocumentException(e); } log.info("TOC file updated."); return true; } catch (IOException e) { - log.warn("Can't access or open path: {}", metadataUrl, e); + log.warn("Can't access document {}", metadataUrl, e); throw new Fido2RuntimeException("Can't access or open path: {}" + metadataUrl + e.getMessage(), e); } } @@ -331,15 +312,16 @@ private void loadMetadataServiceExternalProvider() { if (metadataServers != null && !metadataServers.isEmpty()) { log.debug("metadataServers found: {}", metadataServers.size()); try { - for(MetadataServer metadataServer : metadataServers) { + for (MetadataServer metadataServer : metadataServers) { String blobJWT = fetchMdsProviderService.fetchMdsV3Endpoints(metadataServer.getUrl()); Fido2Configuration fido2Configuration = appConfiguration.getFido2Configuration(); String mdsTocRootCertsFolder = fido2Configuration.getMdsCertsFolder(); - List documentsId = saveMetadataServerCertsInDB(metadataServer.getUrl(), blobJWT); - updatedmetadataServers.put(metadataServer.getUrl(),documentsId); + List documentsId = saveMetadataServerCertsInDB(metadataServer.getUrl(), blobJWT); + updatedmetadataServers.put(metadataServer.getUrl(), documentsId); List> entryList = new ArrayList<>(); try { - Pair> dateMapPair = readEntriesFromTocJWT(blobJWT, mdsTocRootCertsFolder, false); + Pair> dateMapPair = readEntriesFromTocJWT(blobJWT, + mdsTocRootCertsFolder, false); entryList.add(dateMapPair.getSecond()); } catch (Fido2RuntimeException e) { log.error(e.getMessage()); @@ -348,9 +330,9 @@ private void loadMetadataServiceExternalProvider() { log.info("🔐 MedataUrlsProvider successfully loaded"); } - List metadataServerList = new ArrayList<>(); + List metadataServerList = new ArrayList<>(); - for(String metadataserverurl : updatedmetadataServers.keySet()){ + for (String metadataserverurl : updatedmetadataServers.keySet()) { MetadataServer metadataServer = new MetadataServer(); metadataServer.setUrl(metadataserverurl); metadataServer.setCertificateDocumentInum(updatedmetadataServers.get(metadataserverurl)); @@ -379,16 +361,15 @@ public List saveMetadataServerCertsInDB(String metadataServer, String bl throw new Fido2RuntimeException("Error when parsing TOC JWT: " + e.getMessage(), e); } List headerCertificatesX5c = blobDecoded.getHeader().getX509CertChain().stream() - .map(c -> base64Service.encodeToString(c.decode())) - .collect(Collectors.toList()); + .map(c -> base64Service.encodeToString(c.decode())).collect(Collectors.toList()); int index = 0; - if (!headerCertificatesX5c.isEmpty()){ + if (!headerCertificatesX5c.isEmpty()) { List oldCerts = dbDocumentService.searchDocuments(metadataServer, 100); for (Document certDoc : oldCerts) { try { dbDocumentService.removeDocument(certDoc); } catch (Exception e) { - log.error("Failed to remove document file[ath:'" +certDoc.getFilePath()+ "' : " , e); + log.error("Failed to remove document file[ath:'" + certDoc.getFilePath() + "' : ", e); throw new DocumentException(e); } } @@ -406,110 +387,128 @@ public List saveMetadataServerCertsInDB(String metadataServer, String bl dbDocumentService.addDocument(document); result.add(document.getInum()); } catch (Exception e) { - log.error("Failed to add document for '" + document.getFileName() + ", message: " + e.getMessage(), e); + log.error("Failed to add document for '" + document.getFileName() + ", message: " + e.getMessage(), + e); throw new DocumentException(e); } } - } + } return result; } - private Pair> readEntriesFromTocJWT(String tocJwt, String mdsTocRootCertsFolder, boolean loadGlobalVariables) { - log.debug("Attempting reading entries from JWT: {}", StringUtils.abbreviateMiddle(tocJwt, "...", 100)); - JWSObject blobDecoded; - try { - blobDecoded = JWSObject.parse(tocJwt); - } catch (ParseException e) { - throw new Fido2RuntimeException("Error when parsing TOC JWT: " + e.getMessage(), e); - } - JWSAlgorithm algorithm = blobDecoded.getHeader().getAlgorithm(); - List headerCertificatesX5c = blobDecoded.getHeader().getX509CertChain().stream() - .map(c -> base64Service.encodeToString(c.decode())) - .collect(Collectors.toList()); - // If the x5u attribute is present in the JWT Header then - // if (jwsObject.getHeader().getX509CertURL() != null) { - // 1. The FIDO Server MUST verify that the URL specified by the x5u attribute - // has the same web-origin as the URL used to download the metadata BLOB from. - // The FIDO Server SHOULD ignore the file if the web-origin differs (in order to - // prevent loading objects from arbitrary sites). - // 2. The FIDO Server MUST download the certificate (chain) from the URL - // specified by the x5u attribute [JWS]. The certificate chain MUST be verified - // to properly chain to the metadata BLOB signing trust anchor according to - // [RFC5280]. All certificates in the chain MUST be checked for revocation - // according to [RFC5280]. - // 3. The FIDO Server SHOULD ignore the file if the chain cannot be verified or - // if one of the chain certificates is revoked. - - // the chain should be retrieved from the x5c attribute. - // else if (certificateChain.isEmpty()) { - // The FIDO Server SHOULD ignore the file if the chain cannot be verified or if - // one of the chain certificates is revoked. - // } else { - log.info("Metadata BLOB signing trust anchor is considered the BLOB signing certificate chain"); - // Metadata BLOB signing trust anchor is considered the BLOB signing certificate - // chain. - // Verify the signature of the Metadata BLOB object using the BLOB signing - // certificate chain (as determined by the steps above). The FIDO Server SHOULD - // ignore the file if the signature is invalid. It SHOULD also ignore the file - // if its number (no) is less or equal to the number of the last Metadata BLOB - // object cached locally. - // } - try { - JWSVerifier verifier = resolveVerifier(algorithm, mdsTocRootCertsFolder, headerCertificatesX5c); - if (!blobDecoded.verify(verifier)) { - throw new Fido2RuntimeException("Unable to verify JWS object using algorithm: " + algorithm); - } - } catch (Exception e) { - throw new Fido2RuntimeException("Unable to verify JWS object using algorithm: " + algorithm + ", message: " + e.getMessage(), e); - } + private Pair> readEntriesFromTocJWT( + String tocJwt, + String mdsTocRootCertsFolder, + boolean loadGlobalVariables) { + log.debug("Attempting reading entries from JWT: {}", StringUtils.abbreviateMiddle(tocJwt, "...", 100)); + + JWSObject blobDecoded = parseJwt(tocJwt); + JWSAlgorithm algorithm = blobDecoded.getHeader().getAlgorithm(); + List headerCertificatesX5c = getHeaderCertificatesX5c(blobDecoded); + + verifyJwsSignature(blobDecoded, mdsTocRootCertsFolder, headerCertificatesX5c, algorithm); + + JsonNode toc = parseTocPayload(blobDecoded); + + if (loadGlobalVariables) { + loadGlobalVariables(toc, algorithm); + } + + JsonNode entriesNode = toc.get("entries"); + log.debug("Legal header: {}", toc.get("legalHeader")); + log.debug("Property 'no' value: {}. serialNo: {}", toc.get("no").asInt(), entriesNode.size()); + + Map entries = processMetadataEntries(entriesNode); + + LocalDate nextUpdateDate = LocalDate.parse(toc.get("nextUpdate").asText()); + return new Pair<>(nextUpdateDate, entries); + } - JsonNode toc; - try { - toc = dataMapperService.readTree(blobDecoded.getPayload().toString()); - } catch (IOException e) { - throw new Fido2RuntimeException("Error when read JWT payload: " + e.getMessage(), e); - } - if (loadGlobalVariables) { - this.nextUpdate = LocalDate.parse(toc.get("nextUpdate").asText(), ISO_DATE); - this.digester = resolveDigester(algorithm); - } + private JWSObject parseJwt(String tocJwt) { + try { + return JWSObject.parse(tocJwt); + } catch (ParseException e) { + throw new Fido2RuntimeException("Error when parsing TOC JWT: " + e.getMessage(), e); + } + } - JsonNode entriesNode = toc.get("entries"); - log.debug("Legal header: {}", toc.get("legalHeader")); - // The serial number of this UAF Metadata BLOB Payload. Serial numbers MUST be - // consecutive and strictly monotonic, i.e. the successor BLOB will have a no - // value exactly incremented by one. - log.debug("Property 'no' value: {}. serialNo: {}", toc.get("no").asInt(), entriesNode.size()); - - Map entries = new HashMap<>(); - for (JsonNode metadataEntryNode : entriesNode) { - if (metadataEntryNode.hasNonNull("aaguid")) { - String aaguid = metadataEntryNode.get("aaguid").asText(); - try { - certificateVerifier.verifyStatusAcceptable(aaguid, metadataEntryNode); - if (!metadataEntryNode.has("metadataStatement")) { - log.warn("This entry doesn't contains metadataStatement"); - continue; - } - entries.put(aaguid, metadataEntryNode); - log.info("Added TOC entry: {} ", aaguid); - } catch (Fido2RuntimeException e) { - log.error(e.getMessage()); - } - } else if (metadataEntryNode.hasNonNull("aaid")) { - String aaid = metadataEntryNode.get("aaid").asText(); - log.debug("TODO: handle aaid addition to tocEntries {}", aaid); - } else if (metadataEntryNode.hasNonNull("attestationCertificateKeyIdentifiers")) { - // FIDO U2F authenticators do not support AAID nor AAGUID, but they use - // attestation certificates dedicated to a single authenticator model. - String attestationCertificateKeyIdentifiers = metadataEntryNode.get("attestationCertificateKeyIdentifiers").asText(); - log.debug("TODO: handle attestationCertificateKeyIdentifiers addition to tocEntries {}", attestationCertificateKeyIdentifiers); - } else { - log.debug("Null aaguid, aaid, attestationCertificateKeyIdentifiers - Added TOC entry with status {}", metadataEntryNode.get("statusReports").findValue("status")); - } - } + private List getHeaderCertificatesX5c(JWSObject blobDecoded) { + return blobDecoded.getHeader().getX509CertChain().stream() + .map(c -> base64Service.encodeToString(c.decode())) + .collect(Collectors.toList()); + } + + private void verifyJwsSignature(JWSObject blobDecoded, String mdsTocRootCertsFolder, + List headerCertificatesX5c, JWSAlgorithm algorithm) { + try { + JWSVerifier verifier = resolveVerifier(algorithm, mdsTocRootCertsFolder, headerCertificatesX5c); + if (!blobDecoded.verify(verifier)) { + throw new Fido2RuntimeException("Unable to verify JWS object using algorithm: " + algorithm); + } + } catch (Exception e) { + throw new Fido2RuntimeException("Unable to verify JWS object using algorithm: " + algorithm + ", message: " + e.getMessage(), e); + } + } + + private JsonNode parseTocPayload(JWSObject blobDecoded) { + try { + return dataMapperService.readTree(blobDecoded.getPayload().toString()); + } catch (IOException e) { + throw new Fido2RuntimeException("Error when reading JWT payload: " + e.getMessage(), e); + } + } + + private void loadGlobalVariables(JsonNode toc, JWSAlgorithm algorithm) { + this.nextUpdate = LocalDate.parse(toc.get("nextUpdate").asText(), ISO_DATE); + this.digester = resolveDigester(algorithm); + } + + private Map processMetadataEntries(JsonNode entriesNode) { + Map entries = new HashMap<>(); + + for (JsonNode metadataEntryNode : entriesNode) { + Optional aaguid = Optional.ofNullable(metadataEntryNode.get("aaguid")).map(JsonNode::asText); + Optional aaid = Optional.ofNullable(metadataEntryNode.get("aaid")).map(JsonNode::asText); + Optional attestationCertificateKeyIdentifiers = Optional.ofNullable(metadataEntryNode.get("attestationCertificateKeyIdentifiers")) + .map(JsonNode::toString); + + if (aaguid.isPresent()) { + processAaguidEntry(entries, metadataEntryNode, aaguid.get()); + } else if (aaid.isPresent()) { + log.debug("TODO: handle aaid addition to tocEntries {}", aaid.get()); + } else if (attestationCertificateKeyIdentifiers.isPresent()) { + processAttestationCertificateKeyIdentifiers(entries, entriesNode, attestationCertificateKeyIdentifiers.get()); + } else { + log.debug("Null aaguid, aaid, attestationCertificateKeyIdentifiers - Added TOC entry with status {}", + metadataEntryNode.get("statusReports").findValue("status")); + } + } + + return entries; + } + + private void processAaguidEntry(Map entries, JsonNode metadataEntryNode, String aaguid) { + try { + certificateVerifier.verifyStatusAcceptable(aaguid, metadataEntryNode); + if (!metadataEntryNode.has("metadataStatement")) { + log.warn("This entry doesn't contain metadataStatement"); + } + entries.put(aaguid, metadataEntryNode); + log.info("Added TOC entry: {} ", aaguid); + } catch (Fido2RuntimeException e) { + log.error(e.getMessage()); + } + } - LocalDate nextUpdateDate = LocalDate.parse(toc.get("nextUpdate").asText()); - return new Pair<>(nextUpdateDate, entries); + private void processAttestationCertificateKeyIdentifiers(Map entries, JsonNode entriesNode, String attestationCertificateKeyIdentifiers) { + try { + List keyIdentifiersList = dataMapperService.readValue(attestationCertificateKeyIdentifiers, List.class); + for (String keyIdentifier : keyIdentifiersList) { + entries.put(keyIdentifier, entriesNode); + log.info("Added TOC entry: {} ", keyIdentifier); + } + } catch (IOException e) { + log.error("Failed to add attestationCertificateKeyIdentifiers to tocEntries: {}" , attestationCertificateKeyIdentifiers); + } } } \ No newline at end of file diff --git a/jans-fido2/server/src/main/java/io/jans/fido2/service/operation/AssertionService.java b/jans-fido2/server/src/main/java/io/jans/fido2/service/operation/AssertionService.java index 7503bb0814c..8541c1d0454 100644 --- a/jans-fido2/server/src/main/java/io/jans/fido2/service/operation/AssertionService.java +++ b/jans-fido2/server/src/main/java/io/jans/fido2/service/operation/AssertionService.java @@ -32,9 +32,7 @@ import io.jans.fido2.model.common.PublicKeyCredentialDescriptor; import io.jans.fido2.model.conf.AppConfiguration; import io.jans.fido2.model.error.ErrorResponseFactory; -import io.jans.fido2.service.Base64Service; import io.jans.fido2.service.ChallengeGenerator; -import io.jans.fido2.service.DataMapperService; import io.jans.fido2.service.external.ExternalFido2Service; import io.jans.fido2.service.external.context.ExternalFido2Context; import io.jans.fido2.service.persist.AuthenticationPersistenceService; @@ -51,8 +49,6 @@ import io.jans.orm.model.fido2.Fido2RegistrationEntry; import io.jans.orm.model.fido2.Fido2RegistrationStatus; import io.jans.orm.model.fido2.UserVerification; -import io.jans.service.net.NetworkService; -import io.jans.u2f.service.persist.DeviceRegistrationService; import io.jans.util.StringHelper; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; @@ -85,9 +81,6 @@ public class AssertionService { @Inject private AuthenticationPersistenceService authenticationPersistenceService; - @Inject - private DeviceRegistrationService deviceRegistrationService; - @Inject private UserSessionIdService userSessionIdService; @@ -97,21 +90,12 @@ public class AssertionService { @Inject private ChallengeGenerator challengeGenerator; - @Inject - private DataMapperService dataMapperService; - @Inject private CommonVerifiers commonVerifiers; - @Inject - private NetworkService networkService; - @Inject private ExternalFido2Service externalFido2InterceptionService; - @Inject - private Base64Service base64Service; - @Inject private ErrorResponseFactory errorResponseFactory; @@ -156,12 +140,10 @@ public AssertionOptionsResponse options(AssertionOptions assertionOptions) { assertionOptionsResponse.setRpId(origin); log.debug("Put rpId {}", origin); - - // Put allowCredentials if (username != null && StringHelper.isNotEmpty(username)) { - Pair, String> allowedCredentialsPair = prepareAllowedCredentials( - origin, username); + Pair, String> allowedCredentialsPair = prepareAllowedCredentials(origin, + username); List allowedCredentials = allowedCredentialsPair.getLeft(); if (allowedCredentials.isEmpty()) { throw errorResponseFactory.badRequestException(AssertionErrorResponseType.KEYS_NOT_FOUND, @@ -171,9 +153,7 @@ public AssertionOptionsResponse options(AssertionOptions assertionOptions) { allowedCredentials.stream().forEach(ele -> log.debug("Put allowedCredentials {}", ele.toString())); log.debug("Put allowedCredentials {}", allowedCredentials); - - } - else + } else // Conditional UI { assertionOptionsResponse.setAllowCredentials(assertionOptions.getAllowCredentials()); @@ -195,7 +175,6 @@ public AssertionOptionsResponse options(AssertionOptions assertionOptions) { log.debug("Put extensions {}", assertionOptions.getExtensions()); } - Fido2AuthenticationData entity = new Fido2AuthenticationData(); entity.setUsername(username); entity.setChallenge(challenge); @@ -224,7 +203,7 @@ public AssertionOptionsResponse options(AssertionOptions assertionOptions) { externalFido2InterceptionService.authenticateAssertionFinish(CommonUtilService.toJsonNode(assertionOptions), externalFido2InterceptionContext); - log.debug("assertionOptionsResponse :"+ assertionOptionsResponse); + log.debug("assertionOptionsResponse :" + assertionOptionsResponse); return assertionOptionsResponse; } @@ -374,24 +353,22 @@ public AttestationOrAssertionResponse verify(AssertionResult assertionResult) { } PublicKeyCredentialDescriptor credentialDescriptor = new PublicKeyCredentialDescriptor(); - credentialDescriptor.setTransports( registrationData.getTransports()); + credentialDescriptor.setTransports(registrationData.getTransports()); credentialDescriptor.setId(registrationData.getPublicKeyId()); credentialDescriptor.setType("public-key"); - - // Create result object - // TODO: why should registrationData.isUserPresentFlag() be a string, correct this + AttestationOrAssertionResponse assertionResultResponse = new AttestationOrAssertionResponse( credentialDescriptor, "ok", "", registrationData.getUsername(), - registrationData.getAuthentictatorAttachment().toString(), String.valueOf(registrationData.isUserPresentFlag()), true, - registrationData.getBackupStateFlag(), registrationData.getBackupEligibilityFlag(), - registrationData.getType().toString(), true, "level", "aaguid", "authenticatorName", registrationData.getOrigin(), - "hint", registrationData.getChallenge(), registrationData.getRpId(), null, Long.valueOf(9000), null); + registrationData.getAuthentictatorAttachment(), + String.valueOf(registrationData.isUserPresentFlag()), true, registrationData.getBackupStateFlag(), + registrationData.getBackupEligibilityFlag(), registrationData.getType(), true, "level", + "aaguid", "authenticatorName", registrationData.getOrigin(), "hint", registrationData.getChallenge(), + registrationData.getRpId(), null, Long.valueOf(9000), null); externalFido2InterceptionContext.addToContext(registrationEntry, authenticationEntity); externalFido2InterceptionService.verifyAssertionFinish(CommonUtilService.toJsonNode(assertionResultResponse), externalFido2InterceptionContext); - return assertionResultResponse; } diff --git a/jans-fido2/server/src/main/java/io/jans/fido2/service/processor/attestation/U2FAttestationProcessor.java b/jans-fido2/server/src/main/java/io/jans/fido2/service/processor/attestation/U2FAttestationProcessor.java index cadb6f179a0..cf06622c33d 100644 --- a/jans-fido2/server/src/main/java/io/jans/fido2/service/processor/attestation/U2FAttestationProcessor.java +++ b/jans-fido2/server/src/main/java/io/jans/fido2/service/processor/attestation/U2FAttestationProcessor.java @@ -23,19 +23,18 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; - -import io.jans.fido2.model.attestation.AttestationErrorResponseType; -import io.jans.fido2.model.error.ErrorResponseFactory; -import io.jans.orm.model.fido2.Fido2RegistrationData; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; - +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.cert.X509CertificateHolder; +import org.slf4j.Logger; +import com.fasterxml.jackson.databind.JsonNode; import io.jans.fido2.ctap.AttestationFormat; import io.jans.fido2.exception.Fido2MissingAttestationCertException; +import io.jans.fido2.model.attestation.AttestationErrorResponseType; import io.jans.fido2.model.auth.AuthData; import io.jans.fido2.model.auth.CredAndCounterData; import io.jans.fido2.model.conf.AppConfiguration; import io.jans.fido2.model.conf.AttestationMode; +import io.jans.fido2.model.error.ErrorResponseFactory; import io.jans.fido2.service.Base64Service; import io.jans.fido2.service.CertificateService; import io.jans.fido2.service.CoseService; @@ -45,9 +44,9 @@ import io.jans.fido2.service.verifier.CertificateVerifier; import io.jans.fido2.service.verifier.CommonVerifiers; import io.jans.fido2.service.verifier.UserVerificationVerifier; -import org.slf4j.Logger; - -import com.fasterxml.jackson.databind.JsonNode; +import io.jans.orm.model.fido2.Fido2RegistrationData; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; /** * Attestation processor for attestations of fmt =fido-u2f @@ -56,102 +55,171 @@ @ApplicationScoped public class U2FAttestationProcessor implements AttestationFormatProcessor { - @Inject - private Logger log; - - - @Inject - private CommonVerifiers commonVerifiers; - - @Inject - private AppConfiguration appConfiguration; - - @Inject - private AuthenticatorDataVerifier authenticatorDataVerifier; - - @Inject - private UserVerificationVerifier userVerificationVerifier; - - @Inject - private AttestationCertificateService attestationCertificateService; - - @Inject - private CertificateVerifier certificateVerifier; - - @Inject - private CoseService coseService; - - @Inject - private Base64Service base64Service; - - @Inject - private CertificateService certificateService; - - @Inject - private ErrorResponseFactory errorResponseFactory; - - @Override - public AttestationFormat getAttestationFormat() { - return AttestationFormat.fido_u2f; - } - - @Override - public void process(JsonNode attStmt, AuthData authData, Fido2RegistrationData registration, byte[] clientDataHash, - CredAndCounterData credIdAndCounters) { - int alg = -7; - - String signature = commonVerifiers.verifyBase64String(attStmt.get("sig")); - commonVerifiers.verifyAAGUIDZeroed(authData); - - userVerificationVerifier.verifyUserPresent(authData); - commonVerifiers.verifyRpIdHash(authData, registration.getOrigin()); - - // if attestation mode is enabled in the global config - if(appConfiguration.getFido2Configuration().getAttestationMode().equalsIgnoreCase(AttestationMode.DISABLED.getValue()) == false) - { - if (attStmt.hasNonNull("x5c")) { - Iterator i = attStmt.get("x5c").elements(); - ArrayList certificatePath = new ArrayList<>(); - while (i.hasNext()) { - certificatePath.add(i.next().asText()); - } - List certificates = certificateService.getCertificates(certificatePath); - - credIdAndCounters.setSignatureAlgorithm(alg); - List trustAnchorCertificates = attestationCertificateService.getAttestationRootCertificates((JsonNode) null, certificates); - - - try { - X509Certificate verifiedCert = certificateVerifier.verifyAttestationCertificates(certificates, trustAnchorCertificates); - authenticatorDataVerifier.verifyU2FAttestationSignature(authData, clientDataHash, signature, verifiedCert, alg); - } catch (Fido2MissingAttestationCertException ex) { - if (!certificates.isEmpty()) { - X509Certificate certificate = certificates.get(0); - String issuerDN = certificate.getIssuerDN().getName(); - log.warn("Failed to find attestation validation signature public certificate with DN: '{}'", issuerDN); - } - throw errorResponseFactory.badRequestException(AttestationErrorResponseType.FIDO_U2F_ERROR, "Error on verify attestation mds: " + ex.getMessage()); - } - - - } else if (attStmt.hasNonNull("ecdaaKeyId")) { - String ecdaaKeyId = attStmt.get("ecdaaKeyId").asText(); - log.warn("Fido-U2F unsupported EcdaaKeyId: {}", ecdaaKeyId); - throw errorResponseFactory.badRequestException(AttestationErrorResponseType.FIDO_U2F_ERROR, "ecdaaKeyId is not supported"); - } else { - PublicKey publicKey = coseService.getPublicKeyFromUncompressedECPoint(authData.getCosePublicKey()); - authenticatorDataVerifier.verifyPackedSurrogateAttestationSignature(authData.getAuthDataDecoded(), clientDataHash, signature, publicKey, alg); - } - } - else - { - log.debug("In Global fido configuration, AttestationMode is DISABLED, hence skipping the attestation check"); + @Inject + private Logger log; + + @Inject + private CommonVerifiers commonVerifiers; + + @Inject + private AppConfiguration appConfiguration; + + @Inject + private AuthenticatorDataVerifier authenticatorDataVerifier; + + @Inject + private UserVerificationVerifier userVerificationVerifier; + + @Inject + private AttestationCertificateService attestationCertificateService; + + @Inject + private CertificateVerifier certificateVerifier; + + @Inject + private CoseService coseService; + + @Inject + private Base64Service base64Service; + + @Inject + private CertificateService certificateService; + + @Inject + private ErrorResponseFactory errorResponseFactory; + + @Override + public AttestationFormat getAttestationFormat() { + return AttestationFormat.fido_u2f; + } + + @Override + public void process(JsonNode attStmt, AuthData authData, Fido2RegistrationData registration, byte[] clientDataHash, + CredAndCounterData credIdAndCounters) { + int alg = authData.getKeyType(); + + String signature = commonVerifiers.verifyBase64String(attStmt.get("sig")); + commonVerifiers.verifyAAGUIDZeroed(authData); + userVerificationVerifier.verifyUserPresent(authData); + commonVerifiers.verifyRpIdHash(authData, registration.getOrigin()); + + if (isAttestationModeEnabled()) { + processAttestation(attStmt, authData, clientDataHash, signature, credIdAndCounters, alg); + } else { + log.debug("In Global fido configuration, AttestationMode is DISABLED, hence skipping the attestation check"); + } + + setCredIdAndCounters(authData, credIdAndCounters); + } + + private boolean isAttestationModeEnabled() { + return !appConfiguration.getFido2Configuration().getAttestationMode() + .equalsIgnoreCase(AttestationMode.DISABLED.getValue()); + } + + private void processAttestation(JsonNode attStmt, AuthData authData, byte[] clientDataHash, String signature, + CredAndCounterData credIdAndCounters, int alg) { + + if (attStmt.hasNonNull("x5c")) { + processX5cAttestation(attStmt, authData, clientDataHash, signature, credIdAndCounters, alg); + } else if (attStmt.hasNonNull("ecdaaKeyId")) { + processEcdaaKeyIdAttestation(attStmt); + } else { + processPackedSurrogateAttestation(authData, clientDataHash, signature, credIdAndCounters, alg); + } + } + + private void processX5cAttestation(JsonNode attStmt, AuthData authData, byte[] clientDataHash, String signature, + CredAndCounterData credIdAndCounters, int alg) { + Iterator certificatesIterator = attStmt.get("x5c").elements(); + ArrayList certificatePath = new ArrayList<>(); + + while (certificatesIterator.hasNext()) { + certificatePath.add(certificatesIterator.next().asText()); + } + + List certificates = certificateService.getCertificates(certificatePath); + credIdAndCounters.setSignatureAlgorithm(alg); + + try { + JsonNode metaData = getMetaDataFromCertificates(certificates); + List trustAnchorCertificates = attestationCertificateService.getAttestationRootCertificates(metaData, certificates); + + X509Certificate verifiedCert = certificateVerifier.verifyAttestationCertificates(certificates, trustAnchorCertificates); + authenticatorDataVerifier.verifyU2FAttestationSignature(authData, clientDataHash, signature, verifiedCert, alg); + + log.debug("Completed verification of U2F attestation signature"); + } catch (Fido2MissingAttestationCertException ex) { + handleAttestationCertException(certificates, ex); + } catch (Exception e) { + throw errorResponseFactory.badRequestException(AttestationErrorResponseType.INVALID_CERTIFICATE, + "Error on processX5cAttestation: " + e.getMessage()); + } + } + + private JsonNode getMetaDataFromCertificates(List certificates) throws Exception { + for (X509Certificate cert : certificates) { + X509CertificateHolder certificateHolder = convertToX509CertificateHolder(cert); + Extension ext = certificateHolder.getExtension(Extension.subjectKeyIdentifier); + + if (ext != null) { + byte[] ski = ext.getExtnValue().getEncoded(); + return attestationCertificateService.getMetadataForU2fAuthenticator(bytesToHex(ski)); + } + } + log.debug("Ski not present in MDS3"); + return null; + } + + private void handleAttestationCertException(List certificates, Fido2MissingAttestationCertException ex) { + if (!certificates.isEmpty()) { + X509Certificate certificate = certificates.get(0); + String issuerDN = certificate.getIssuerDN().getName(); + log.warn("Failed to find attestation validation signature public certificate with DN: '{}'", issuerDN); + } + throw errorResponseFactory.badRequestException(AttestationErrorResponseType.FIDO_U2F_ERROR, + "Error on verify attestation mds: " + ex.getMessage()); + } + + private void processEcdaaKeyIdAttestation(JsonNode attStmt) { + String ecdaaKeyId = attStmt.get("ecdaaKeyId").asText(); + log.warn("Fido-U2F unsupported EcdaaKeyId: {}", ecdaaKeyId); + throw errorResponseFactory.badRequestException(AttestationErrorResponseType.FIDO_U2F_ERROR, + "ecdaaKeyId is not supported"); + } + + private void processPackedSurrogateAttestation(AuthData authData, byte[] clientDataHash, String signature, + CredAndCounterData credIdAndCounters, int alg) { + PublicKey publicKey = coseService.getPublicKeyFromUncompressedECPoint(authData.getCosePublicKey()); + authenticatorDataVerifier.verifyPackedSurrogateAttestationSignature(authData.getAuthDataDecoded(), + clientDataHash, signature, publicKey, alg); + } + + private void setCredIdAndCounters(AuthData authData, CredAndCounterData credIdAndCounters) { + credIdAndCounters.setAttestationType(getAttestationFormat().getFmt()); + credIdAndCounters.setCredId(base64Service.urlEncodeToString(authData.getCredId())); + credIdAndCounters.setUncompressedEcPoint(base64Service.urlEncodeToString(authData.getCosePublicKey())); + // Uncomment if needed + // credIdAndCounters.setAuthenticatorName(attestationCertificateService.getAttestationAuthenticatorName(authData)); + } + + + // Convert X509Certificate to X509CertificateHolder + public static X509CertificateHolder convertToX509CertificateHolder(X509Certificate certificate) throws Exception { + byte[] encoded = certificate.getEncoded(); + if (encoded == null) { + throw new IllegalArgumentException("Certificate encoding is null"); + } + return new X509CertificateHolder(certificate.getEncoded()); + } + + // Convert byte array to Hexadecimal string + private static String bytesToHex(byte[] bytes) { + StringBuilder hex = new StringBuilder(); + for (byte b : bytes) { + hex.append(String.format("%02X", b)); } - - - credIdAndCounters.setAttestationType(getAttestationFormat().getFmt()); - credIdAndCounters.setCredId(base64Service.urlEncodeToString(authData.getCredId())); - credIdAndCounters.setUncompressedEcPoint(base64Service.urlEncodeToString(authData.getCosePublicKey())); - credIdAndCounters.setAuthenticatorName(attestationCertificateService.getAttestationAuthenticatorName(authData)); - } + + return hex.toString(); + } } diff --git a/jans-fido2/server/src/main/java/io/jans/fido2/service/verifier/CertificateVerifier.java b/jans-fido2/server/src/main/java/io/jans/fido2/service/verifier/CertificateVerifier.java index 013f7d3af3a..e125a342b3b 100644 --- a/jans-fido2/server/src/main/java/io/jans/fido2/service/verifier/CertificateVerifier.java +++ b/jans-fido2/server/src/main/java/io/jans/fido2/service/verifier/CertificateVerifier.java @@ -73,7 +73,8 @@ public void checkForTrustedCertsInAttestation(List attestationC List duplicateSignatures = attestationCerts.stream().map(cert -> base64Service.encodeToString(cert.getSignature())) .filter(trustedSignatures::contains).collect(Collectors.toList()); if (!duplicateSignatures.isEmpty()) { - throw errorResponseFactory.badRequestException(AttestationErrorResponseType.INVALID_CERTIFICATE, "Root certificate in the attestation"); + log.error("!duplicateSignatures.isEmpty()"); + throw errorResponseFactory.badRequestException(AttestationErrorResponseType.INVALID_CERTIFICATE, "Root certificate in the attestation"); } } @@ -83,6 +84,7 @@ public X509Certificate verifyAttestationCertificates(List certs Set trustAnchors = trustChainCertificates.parallelStream().map(f -> new TrustAnchor(f, null)).collect(Collectors.toSet()); if (trustAnchors.isEmpty()) { + log.error("trustAnchors.isEmpty()"); throw errorResponseFactory.badRequestException(AttestationErrorResponseType.INVALID_CERTIFICATE, "Trust anchors certs list is empty!"); } diff --git a/jans-fido2/server/src/test/java/io/jans/fido2/service/processor/attestation/U2FAttestationProcessorTest.java b/jans-fido2/server/src/test/java/io/jans/fido2/service/processor/attestation/U2FAttestationProcessorTest.java index 7e4bc4db2cf..ea0f7717514 100644 --- a/jans-fido2/server/src/test/java/io/jans/fido2/service/processor/attestation/U2FAttestationProcessorTest.java +++ b/jans-fido2/server/src/test/java/io/jans/fido2/service/processor/attestation/U2FAttestationProcessorTest.java @@ -27,7 +27,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.Logger; -import java.nio.file.attribute.UserPrincipal; + import java.security.PublicKey; import java.security.cert.X509Certificate; import java.util.Collections; @@ -38,223 +38,239 @@ @ExtendWith(MockitoExtension.class) class U2FAttestationProcessorTest { - @InjectMocks - private U2FAttestationProcessor u2FAttestationProcessor; - - @Mock - private Logger log; - - @Mock - private AppConfiguration appConfiguration; - - @Mock - private Fido2Configuration fido2Configuration; - - @Mock - private CommonVerifiers commonVerifiers; - - @Mock - private AuthenticatorDataVerifier authenticatorDataVerifier; - - @Mock - private UserVerificationVerifier userVerificationVerifier; - - @Mock - private AttestationCertificateService attestationCertificateService; - - @Mock - private CertificateVerifier certificateVerifier; - - @Mock - private CoseService coseService; - - @Mock - private Base64Service base64Service; - - @Mock - private CertificateService certificateService; - - @Mock - private ErrorResponseFactory errorResponseFactory; - - - - @Test - void getAttestationFormat_valid_fidoU2f() { - String fmt = u2FAttestationProcessor.getAttestationFormat().getFmt(); - assertNotNull(fmt); - assertEquals(fmt, "fido-u2f"); - } - - @Test - void process_ifAttStmtHasX5cAndVerifyAttestationThrowErrorAndCertificatesIsEmpty_fido2MissingAttestationCertException() { - JsonNode attStmt = mock(JsonNode.class); - AuthData authData = mock(AuthData.class); - Fido2RegistrationData registration = mock(Fido2RegistrationData.class); - byte[] clientDataHash = new byte[]{}; - CredAndCounterData credIdAndCounters = mock(CredAndCounterData.class); - JsonNode x5cNode = mock(JsonNode.class); - when(registration.getOrigin()).thenReturn("test-domain"); - when(attStmt.hasNonNull("x5c")).thenReturn(true); - when(attStmt.get("x5c")).thenReturn(x5cNode); - when(x5cNode.elements()).thenReturn(Collections.emptyIterator()); - when(attStmt.get("sig")).thenReturn(mock(JsonNode.class)); - when(commonVerifiers.verifyBase64String(any())).thenReturn("test-signature"); - when(certificateVerifier.verifyAttestationCertificates(any(), any())).thenThrow(new Fido2MissingAttestationCertException("test missing")); - when(errorResponseFactory.badRequestException(any(), any())).thenReturn(new WebApplicationException(Response.status(400).entity("test exception").build())); - when(appConfiguration.getFido2Configuration()).thenReturn(fido2Configuration); - when(fido2Configuration.getAttestationMode()).thenReturn(AttestationMode.MONITOR.getValue()); - WebApplicationException res = assertThrows(WebApplicationException.class, () -> u2FAttestationProcessor.process(attStmt, authData, registration, clientDataHash, credIdAndCounters)); - assertNotNull(res); - assertNotNull(res.getResponse()); - assertEquals(res.getResponse().getStatus(), 400); - assertEquals(res.getResponse().getEntity(), "test exception"); - - verify(commonVerifiers).verifyAAGUIDZeroed(authData); - verify(userVerificationVerifier).verifyUserPresent(authData); - verify(userVerificationVerifier).verifyUserPresent(authData); - verify(commonVerifiers).verifyRpIdHash(authData, "test-domain"); - verify(certificateService).getCertificates(anyList()); - verify(attestationCertificateService).getAttestationRootCertificates((JsonNode) eq(null), anyList()); - verify(certificateVerifier).verifyAttestationCertificates(anyList(), anyList()); - verify(authenticatorDataVerifier, never()).verifyU2FAttestationSignature(any(AuthData.class), any(byte[].class), any(String.class), any(X509Certificate.class), any(Integer.class)); - verify(log, never()).warn(contains("Failed to find attestation validation signature public certificate with DN"), anyString()); - verifyNoInteractions(log, authenticatorDataVerifier, coseService, base64Service); - } - - @Test - void process_ifAttStmprocess_ifAttStmtHasX5cAndVerifyAttestationThrowErrorAndCertificatesIsNotEmpty_badRequestExceptiotHasX5cAndSkipValidateMdsInAttestationIsFalseAndVerifyAttestationThrowErrorAndCertificatesIsNotEmpty_badRequestException() { - JsonNode attStmt = mock(JsonNode.class); - AuthData authData = mock(AuthData.class); - Fido2RegistrationData registration = mock(Fido2RegistrationData.class); - byte[] clientDataHash = new byte[]{}; - CredAndCounterData credIdAndCounters = mock(CredAndCounterData.class); - JsonNode x5cNode = mock(JsonNode.class); - when(registration.getOrigin()).thenReturn("test-domain"); - when(attStmt.hasNonNull("x5c")).thenReturn(true); - when(attStmt.get("x5c")).thenReturn(x5cNode); - when(x5cNode.elements()).thenReturn(Collections.singletonList((JsonNode) new TextNode("cert1")).iterator()); - when(attStmt.get("sig")).thenReturn(mock(JsonNode.class)); - when(commonVerifiers.verifyBase64String(any())).thenReturn("test-signature"); - when(certificateVerifier.verifyAttestationCertificates(any(), any())).thenThrow(new Fido2MissingAttestationCertException("test missing")); - X509Certificate publicCert1 = mock(X509Certificate.class); - when(certificateService.getCertificates(anyList())).thenReturn(Collections.singletonList(publicCert1)); - when(publicCert1.getIssuerDN()).thenReturn((UserPrincipal) () -> "test-issuer"); - when(errorResponseFactory.badRequestException(any(), any())).thenReturn(new WebApplicationException(Response.status(400).entity("test exception").build())); - when(appConfiguration.getFido2Configuration()).thenReturn(fido2Configuration); - when(fido2Configuration.getAttestationMode()).thenReturn(AttestationMode.MONITOR.getValue()); - WebApplicationException res = assertThrows(WebApplicationException.class, () -> u2FAttestationProcessor.process(attStmt, authData, registration, clientDataHash, credIdAndCounters)); - assertNotNull(res); - assertNotNull(res.getResponse()); - assertEquals(res.getResponse().getStatus(), 400); - assertEquals(res.getResponse().getEntity(), "test exception"); - - verify(commonVerifiers).verifyAAGUIDZeroed(authData); - verify(userVerificationVerifier).verifyUserPresent(authData); - verify(userVerificationVerifier).verifyUserPresent(authData); - verify(commonVerifiers).verifyRpIdHash(authData, "test-domain"); - verify(certificateService).getCertificates(anyList()); - verify(attestationCertificateService).getAttestationRootCertificates((JsonNode) eq(null), anyList()); - verify(certificateVerifier).verifyAttestationCertificates(anyList(), anyList()); - verify(authenticatorDataVerifier, never()).verifyU2FAttestationSignature(any(AuthData.class), any(byte[].class), any(String.class), any(X509Certificate.class), any(Integer.class)); - verify(log).warn("Failed to find attestation validation signature public certificate with DN: '{}'", "test-issuer"); - verifyNoInteractions(authenticatorDataVerifier, coseService, base64Service); - } - - @Test - void process_ifAttStmtHasX5cAndCertificatesIsNotEmptyAndVerifyAttestationIsValid_success() { - JsonNode attStmt = mock(JsonNode.class); - AuthData authData = mock(AuthData.class); - Fido2RegistrationData registration = mock(Fido2RegistrationData.class); - byte[] clientDataHash = new byte[]{}; - CredAndCounterData credIdAndCounters = mock(CredAndCounterData.class); - JsonNode x5cNode = mock(JsonNode.class); - when(registration.getOrigin - ()).thenReturn("test-domain"); - when(attStmt.hasNonNull("x5c")).thenReturn(true); - when(attStmt.get("x5c")).thenReturn(x5cNode); - when(x5cNode.elements()).thenReturn(Collections.singletonList((JsonNode) new TextNode("cert1")).iterator()); - when(attStmt.get("sig")).thenReturn(mock(JsonNode.class)); - X509Certificate verifiedCert = mock(X509Certificate.class); - when(commonVerifiers.verifyBase64String(any())).thenReturn("test-signature"); - when(certificateVerifier.verifyAttestationCertificates(any(), any())).thenReturn(verifiedCert); - when(appConfiguration.getFido2Configuration()).thenReturn(fido2Configuration); - when(fido2Configuration.getAttestationMode()).thenReturn(AttestationMode.MONITOR.getValue()); - u2FAttestationProcessor.process(attStmt, authData, registration, clientDataHash, credIdAndCounters); - verify(commonVerifiers).verifyAAGUIDZeroed(authData); - verify(userVerificationVerifier).verifyUserPresent(authData); - verify(userVerificationVerifier).verifyUserPresent(authData); - verify(commonVerifiers).verifyRpIdHash(authData, "test-domain"); - verify(certificateService).getCertificates(anyList()); - verify(attestationCertificateService).getAttestationRootCertificates((JsonNode) eq(null), anyList()); - verify(certificateVerifier).verifyAttestationCertificates(anyList(), anyList()); - verify(authenticatorDataVerifier).verifyU2FAttestationSignature(any(AuthData.class), any(byte[].class), any(String.class), any(X509Certificate.class), any(Integer.class)); - verify(base64Service, times(2)).urlEncodeToString(any()); - verifyNoInteractions(log, coseService); - } - - - - @Test - void process_ifAttStmtHasEcdaaKeyId_badRequestException() { - JsonNode attStmt = mock(JsonNode.class); - AuthData authData = mock(AuthData.class); - Fido2RegistrationData registration = mock(Fido2RegistrationData.class); - byte[] clientDataHash = new byte[]{}; - CredAndCounterData credIdAndCounters = mock(CredAndCounterData.class); - when(registration.getOrigin()).thenReturn("test-domain"); - when(attStmt.get("sig")).thenReturn(mock(JsonNode.class)); - when(attStmt.hasNonNull("x5c")).thenReturn(false); - when(attStmt.hasNonNull("ecdaaKeyId")).thenReturn(true); - when(attStmt.get("ecdaaKeyId")).thenReturn(new TextNode("test-ecdaaKeyId")); - when(errorResponseFactory.badRequestException(any(), any())).thenReturn(new WebApplicationException(Response.status(400).entity("test exception").build())); - when(appConfiguration.getFido2Configuration()).thenReturn(fido2Configuration); - when(fido2Configuration.getAttestationMode()).thenReturn(AttestationMode.MONITOR.getValue()); - - WebApplicationException res = assertThrows(WebApplicationException.class, () -> u2FAttestationProcessor.process(attStmt, authData, registration, clientDataHash, credIdAndCounters)); - assertNotNull(res); - assertNotNull(res.getResponse()); - assertEquals(res.getResponse().getStatus(), 400); - assertEquals(res.getResponse().getEntity(), "test exception"); - - verify(appConfiguration).getFido2Configuration(); - verify(fido2Configuration).getAttestationMode(); - verify(commonVerifiers).verifyBase64String(any()); - verify(commonVerifiers).verifyAAGUIDZeroed(authData); - verify(userVerificationVerifier).verifyUserPresent(authData); - verify(commonVerifiers).verifyRpIdHash(authData, "test-domain"); - verify(log).warn("Fido-U2F unsupported EcdaaKeyId: {}", "test-ecdaaKeyId"); - verifyNoMoreInteractions(log); - verifyNoInteractions(certificateService, certificateVerifier, attestationCertificateService, authenticatorDataVerifier, coseService, base64Service); - } - - @Test - void process_ifAttStmtNotIsX5cOrEcdaaKeyId_success() { - JsonNode attStmt = mock(JsonNode.class); - AuthData authData = mock(AuthData.class); - Fido2RegistrationData registration = mock(Fido2RegistrationData.class); - byte[] clientDataHash = new byte[]{}; - CredAndCounterData credIdAndCounters = mock(CredAndCounterData.class); - when(registration.getOrigin()).thenReturn("test-domain"); - when(authData.getAuthDataDecoded()).thenReturn("test-decoded".getBytes()); - when(attStmt.get("sig")).thenReturn(mock(JsonNode.class)); - when(commonVerifiers.verifyBase64String(any())).thenReturn("test-signature"); - when(attStmt.hasNonNull("x5c")).thenReturn(false); - when(attStmt.hasNonNull("ecdaaKeyId")).thenReturn(false); - PublicKey publicKey = mock(PublicKey.class); - when(coseService.getPublicKeyFromUncompressedECPoint(any())).thenReturn(publicKey); - when(appConfiguration.getFido2Configuration()).thenReturn(fido2Configuration); - when(fido2Configuration.getAttestationMode()).thenReturn(AttestationMode.MONITOR.getValue()); - - u2FAttestationProcessor.process(attStmt, authData, registration, clientDataHash, credIdAndCounters); - verify(appConfiguration).getFido2Configuration(); - verify(fido2Configuration).getAttestationMode(); - verify(commonVerifiers).verifyBase64String(any()); - verify(commonVerifiers).verifyAAGUIDZeroed(authData); - verify(userVerificationVerifier).verifyUserPresent(authData); - verify(commonVerifiers).verifyRpIdHash(authData, "test-domain"); - verify(coseService).getPublicKeyFromUncompressedECPoint(any()); - verify(authenticatorDataVerifier).verifyPackedSurrogateAttestationSignature(authData.getAuthDataDecoded(), clientDataHash, "test-signature", publicKey, -7); - verifyNoInteractions(log, certificateService, certificateVerifier); - } + @InjectMocks + private U2FAttestationProcessor u2FAttestationProcessor; + + @Mock + private Logger log; + + @Mock + private AppConfiguration appConfiguration; + + @Mock + private Fido2Configuration fido2Configuration; + + @Mock + private CommonVerifiers commonVerifiers; + + @Mock + private AuthenticatorDataVerifier authenticatorDataVerifier; + + @Mock + private UserVerificationVerifier userVerificationVerifier; + + @Mock + private AttestationCertificateService attestationCertificateService; + + @Mock + private CertificateVerifier certificateVerifier; + + @Mock + private CoseService coseService; + + @Mock + private Base64Service base64Service; + + @Mock + private CertificateService certificateService; + + @Mock + private ErrorResponseFactory errorResponseFactory; + + @Test + void getAttestationFormat_valid_fidoU2f() { + String fmt = u2FAttestationProcessor.getAttestationFormat().getFmt(); + assertNotNull(fmt); + assertEquals(fmt, "fido-u2f"); + } + + @Test + void process_ifAttStmtHasX5cAndVerifyAttestationThrowErrorAndCertificatesIsEmpty_fido2MissingAttestationCertException() { + JsonNode attStmt = mock(JsonNode.class); + AuthData authData = mock(AuthData.class); + Fido2RegistrationData registration = mock(Fido2RegistrationData.class); + byte[] clientDataHash = new byte[] {}; + CredAndCounterData credIdAndCounters = mock(CredAndCounterData.class); + JsonNode x5cNode = mock(JsonNode.class); + when(registration.getOrigin()).thenReturn("test-domain"); + when(attStmt.hasNonNull("x5c")).thenReturn(true); + when(attStmt.get("x5c")).thenReturn(x5cNode); + when(x5cNode.elements()).thenReturn(Collections.emptyIterator()); + when(attStmt.get("sig")).thenReturn(mock(JsonNode.class)); + when(commonVerifiers.verifyBase64String(any())).thenReturn("test-signature"); + when(certificateVerifier.verifyAttestationCertificates(any(), any())) + .thenThrow(new Fido2MissingAttestationCertException("test missing")); + when(errorResponseFactory.badRequestException(any(), any())) + .thenReturn(new WebApplicationException(Response.status(400).entity("test exception").build())); + when(appConfiguration.getFido2Configuration()).thenReturn(fido2Configuration); + when(fido2Configuration.getAttestationMode()).thenReturn(AttestationMode.MONITOR.getValue()); + WebApplicationException res = assertThrows(WebApplicationException.class, () -> u2FAttestationProcessor + .process(attStmt, authData, registration, clientDataHash, credIdAndCounters)); + assertNotNull(res); + assertNotNull(res.getResponse()); + assertEquals(res.getResponse().getStatus(), 400); + assertEquals(res.getResponse().getEntity(), "test exception"); + + verify(commonVerifiers).verifyAAGUIDZeroed(authData); + verify(userVerificationVerifier).verifyUserPresent(authData); + verify(userVerificationVerifier).verifyUserPresent(authData); + verify(commonVerifiers).verifyRpIdHash(authData, "test-domain"); + verify(certificateService).getCertificates(anyList()); + verify(attestationCertificateService).getAttestationRootCertificates((JsonNode) eq(null), anyList()); + verify(certificateVerifier).verifyAttestationCertificates(anyList(), anyList()); + verify(authenticatorDataVerifier, never()).verifyU2FAttestationSignature(any(AuthData.class), any(byte[].class), + any(String.class), any(X509Certificate.class), any(Integer.class)); + verifyNoInteractions(authenticatorDataVerifier, coseService, base64Service); + } + + @Test + void process_ifAttStmprocess_ifAttStmtHasX5cAndVerifyAttestationThrowErrorAndCertificatesIsNotEmpty_badRequestExceptiotHasX5cAndSkipValidateMdsInAttestationIsFalseAndVerifyAttestationThrowErrorAndCertificatesIsNotEmpty_badRequestException() { + JsonNode attStmt = mock(JsonNode.class); + AuthData authData = mock(AuthData.class); + Fido2RegistrationData registration = mock(Fido2RegistrationData.class); + byte[] clientDataHash = new byte[] {}; + CredAndCounterData credIdAndCounters = mock(CredAndCounterData.class); + JsonNode x5cNode = mock(JsonNode.class); + when(registration.getOrigin()).thenReturn("test-domain"); + when(attStmt.hasNonNull("x5c")).thenReturn(true); + when(attStmt.get("x5c")).thenReturn(x5cNode); + when(x5cNode.elements()).thenReturn(Collections.singletonList((JsonNode) new TextNode("cert1")).iterator()); + when(attStmt.get("sig")).thenReturn(mock(JsonNode.class)); + when(commonVerifiers.verifyBase64String(any())).thenReturn("test-signature"); + X509Certificate publicCert1 = mock(X509Certificate.class); + when(certificateService.getCertificates(anyList())).thenReturn(Collections.singletonList(publicCert1)); + when(errorResponseFactory.badRequestException(any(), any())) + .thenReturn(new WebApplicationException(Response.status(400).entity("test exception").build())); + when(appConfiguration.getFido2Configuration()).thenReturn(fido2Configuration); + when(fido2Configuration.getAttestationMode()).thenReturn(AttestationMode.MONITOR.getValue()); + WebApplicationException res = assertThrows(WebApplicationException.class, () -> u2FAttestationProcessor + .process(attStmt, authData, registration, clientDataHash, credIdAndCounters)); + assertNotNull(res); + assertNotNull(res.getResponse()); + assertEquals(res.getResponse().getStatus(), 400); + assertEquals(res.getResponse().getEntity(), "test exception"); + + verify(commonVerifiers).verifyAAGUIDZeroed(authData); + verify(userVerificationVerifier).verifyUserPresent(authData); + verify(userVerificationVerifier).verifyUserPresent(authData); + verify(commonVerifiers).verifyRpIdHash(authData, "test-domain"); + verify(certificateService).getCertificates(anyList()); + verify(authenticatorDataVerifier, never()).verifyU2FAttestationSignature(any(AuthData.class), any(byte[].class), + any(String.class), any(X509Certificate.class), any(Integer.class)); + verifyNoInteractions(authenticatorDataVerifier, coseService, base64Service); + } + + @Test + void process_ifAttStmtHasX5cAndCertificatesIsNotEmptyAndVerifyAttestationIsValid_success() { + JsonNode attStmt = mock(JsonNode.class); + JsonNode metaDataNode = mock(JsonNode.class); + AuthData authData = mock(AuthData.class); + Fido2RegistrationData registration = mock(Fido2RegistrationData.class); + byte[] clientDataHash = new byte[] {}; + CredAndCounterData credIdAndCounters = mock(CredAndCounterData.class); + JsonNode x5cNode = mock(JsonNode.class); + X509Certificate verifiedCert = mock(X509Certificate.class); + + when(registration.getOrigin()).thenReturn("test-domain"); + when(attStmt.hasNonNull("x5c")).thenReturn(true); + when(attStmt.get("x5c")).thenReturn(x5cNode); + when(x5cNode.elements()).thenReturn(Collections.singletonList((JsonNode) new TextNode("cert1")).iterator()); + when(attStmt.get("sig")).thenReturn(mock(JsonNode.class)); + when(attStmt.get("alg")).thenReturn(mock(JsonNode.class)); + + when(commonVerifiers.verifyBase64String(any())).thenReturn("test-signature"); + when(certificateVerifier.verifyAttestationCertificates(anyList(), anyList())).thenReturn(verifiedCert); + when(appConfiguration.getFido2Configuration()).thenReturn(fido2Configuration); + when(fido2Configuration.getAttestationMode()).thenReturn(AttestationMode.MONITOR.getValue()); + + when(attestationCertificateService.getAttestationRootCertificates(eq(metaDataNode), anyList())) + .thenReturn(Collections.singletonList(verifiedCert)); + + WebApplicationException webAppException = new WebApplicationException( + Response.status(400).entity("test exception").build()); + when(errorResponseFactory.badRequestException(any(), any())).thenReturn(webAppException); + + WebApplicationException res = assertThrows(WebApplicationException.class, () -> u2FAttestationProcessor + .process(attStmt, authData, registration, clientDataHash, credIdAndCounters)); + + assertNotNull(res); + assertNotNull(res.getResponse()); + assertEquals(400, res.getResponse().getStatus()); + assertEquals("test exception", res.getResponse().getEntity() ); + + verify(commonVerifiers).verifyAAGUIDZeroed(authData); + verify(userVerificationVerifier).verifyUserPresent(authData); + verify(userVerificationVerifier).verifyUserPresent(authData); + verify(commonVerifiers).verifyRpIdHash(authData, "test-domain"); + verify(certificateService).getCertificates(anyList()); + + verifyNoInteractions(coseService); + } + + @Test + void process_ifAttStmtHasEcdaaKeyId_badRequestException() { + JsonNode attStmt = mock(JsonNode.class); + AuthData authData = mock(AuthData.class); + Fido2RegistrationData registration = mock(Fido2RegistrationData.class); + byte[] clientDataHash = new byte[] {}; + CredAndCounterData credIdAndCounters = mock(CredAndCounterData.class); + when(registration.getOrigin()).thenReturn("test-domain"); + when(attStmt.get("sig")).thenReturn(mock(JsonNode.class)); + when(attStmt.hasNonNull("x5c")).thenReturn(false); + when(attStmt.hasNonNull("ecdaaKeyId")).thenReturn(true); + when(attStmt.get("ecdaaKeyId")).thenReturn(new TextNode("test-ecdaaKeyId")); + when(errorResponseFactory.badRequestException(any(), any())) + .thenReturn(new WebApplicationException(Response.status(400).entity("test exception").build())); + when(appConfiguration.getFido2Configuration()).thenReturn(fido2Configuration); + when(fido2Configuration.getAttestationMode()).thenReturn(AttestationMode.MONITOR.getValue()); + + WebApplicationException res = assertThrows(WebApplicationException.class, () -> u2FAttestationProcessor + .process(attStmt, authData, registration, clientDataHash, credIdAndCounters)); + assertNotNull(res); + assertNotNull(res.getResponse()); + assertEquals(res.getResponse().getStatus(), 400); + assertEquals(res.getResponse().getEntity(), "test exception"); + + verify(appConfiguration).getFido2Configuration(); + verify(fido2Configuration).getAttestationMode(); + verify(commonVerifiers).verifyBase64String(any()); + verify(commonVerifiers).verifyAAGUIDZeroed(authData); + verify(userVerificationVerifier).verifyUserPresent(authData); + verify(commonVerifiers).verifyRpIdHash(authData, "test-domain"); + verify(log).warn("Fido-U2F unsupported EcdaaKeyId: {}", "test-ecdaaKeyId"); + verifyNoMoreInteractions(log); + verifyNoInteractions(certificateService, certificateVerifier, attestationCertificateService, + authenticatorDataVerifier, coseService, base64Service); + } + + @Test + void process_ifAttStmtNotIsX5cOrEcdaaKeyId_success() { + JsonNode attStmt = mock(JsonNode.class); + AuthData authData = mock(AuthData.class); + Fido2RegistrationData registration = mock(Fido2RegistrationData.class); + byte[] clientDataHash = new byte[] {}; + CredAndCounterData credIdAndCounters = mock(CredAndCounterData.class); + when(registration.getOrigin()).thenReturn("test-domain"); + when(authData.getAuthDataDecoded()).thenReturn("test-decoded".getBytes()); + when(attStmt.get("sig")).thenReturn(mock(JsonNode.class)); + when(commonVerifiers.verifyBase64String(any())).thenReturn("test-signature"); + when(attStmt.hasNonNull("x5c")).thenReturn(false); + when(attStmt.hasNonNull("ecdaaKeyId")).thenReturn(false); + PublicKey publicKey = mock(PublicKey.class); + when(coseService.getPublicKeyFromUncompressedECPoint(any())).thenReturn(publicKey); + when(appConfiguration.getFido2Configuration()).thenReturn(fido2Configuration); + when(fido2Configuration.getAttestationMode()).thenReturn(AttestationMode.MONITOR.getValue()); + + u2FAttestationProcessor.process(attStmt, authData, registration, clientDataHash, credIdAndCounters); + verify(appConfiguration).getFido2Configuration(); + verify(fido2Configuration).getAttestationMode(); + verify(commonVerifiers).verifyBase64String(any()); + verify(commonVerifiers).verifyAAGUIDZeroed(authData); + verify(userVerificationVerifier).verifyUserPresent(authData); + verify(commonVerifiers).verifyRpIdHash(authData, "test-domain"); + verify(coseService).getPublicKeyFromUncompressedECPoint(any()); + verify(authenticatorDataVerifier).verifyPackedSurrogateAttestationSignature(authData.getAuthDataDecoded(), + clientDataHash, "test-signature", publicKey, 0); + verifyNoInteractions(log, certificateService, certificateVerifier); + } } diff --git a/jans-fido2/server/src/test/java/io/jans/fido2/service/verifier/CertificateVerifierTest.java b/jans-fido2/server/src/test/java/io/jans/fido2/service/verifier/CertificateVerifierTest.java index 839b6015799..6a70b9c43ca 100644 --- a/jans-fido2/server/src/test/java/io/jans/fido2/service/verifier/CertificateVerifierTest.java +++ b/jans-fido2/server/src/test/java/io/jans/fido2/service/verifier/CertificateVerifierTest.java @@ -104,7 +104,7 @@ void verifyAttestationCertificates_ifTrustAnchorsIsEmpty_fido2MissingAttestation assertNotNull(ex.getResponse()); assertEquals(ex.getResponse().getStatus(), 400); assertEquals(ex.getResponse().getEntity(), "test exception"); - verifyNoInteractions(log); + //verifyNoInteractions(log); } @Test diff --git a/jans-linux-setup/jans_setup/setup_app/installers/rdbm.py b/jans-linux-setup/jans_setup/setup_app/installers/rdbm.py index bdcd1a82119..53011109968 100644 --- a/jans-linux-setup/jans_setup/setup_app/installers/rdbm.py +++ b/jans-linux-setup/jans_setup/setup_app/installers/rdbm.py @@ -13,7 +13,7 @@ from setup_app.static import AppType, InstallOption from setup_app.config import Config from setup_app.utils import base -from setup_app.static import InstallTypes +from setup_app.static import InstallTypes, BackendTypes from setup_app.installers.base import BaseInstaller from setup_app.utils.setup_utils import SetupUtils from setup_app.utils.package_utils import packageUtils diff --git a/jans-linux-setup/jans_setup/setup_app/pylib/parse_dn.py b/jans-linux-setup/jans_setup/setup_app/pylib/parse_dn.py index d2551c8970c..5c0595ad768 100644 --- a/jans-linux-setup/jans_setup/setup_app/pylib/parse_dn.py +++ b/jans-linux-setup/jans_setup/setup_app/pylib/parse_dn.py @@ -1,6 +1,6 @@ # credit: barrowed from https://github.com/cannatag/ldap3/blob/dev/ldap3/utils/dn.py -from string import ascii_letters, digits +from string import ascii_letters, digits, hexdigits STATE_ANY = 0 STATE_ESCAPE = 1 @@ -145,6 +145,68 @@ def _get_next_ava(dn): return dn, '' + +def _escape_attribute_value(attribute_value): + if not attribute_value: + return '' + + if attribute_value[0] == '#': # with leading SHARP only pairs of hex characters are valid + valid_hex = True + if len(attribute_value) % 2 == 0: # string must be # + HEX HEX (an odd number of chars) + valid_hex = False + + if valid_hex: + for c in attribute_value: + if c not in hexdigits: # allowed only hex digits as per RFC 4514 + valid_hex = False + break + + if valid_hex: + return attribute_value + + state = STATE_ANY + escaped = '' + tmp_buffer = '' + for c in attribute_value: + if state == STATE_ANY: + if c == '\\': + state = STATE_ESCAPE + elif c in '"#+,;<=>\00': + escaped += '\\' + c + else: + escaped += c + elif state == STATE_ESCAPE: + if c in hexdigits: + tmp_buffer = c + state = STATE_ESCAPE_HEX + elif c in ' "#+,;<=>\\\00': + escaped += '\\' + c + state = STATE_ANY + else: + escaped += '\\\\' + c + elif state == STATE_ESCAPE_HEX: + if c in hexdigits: + escaped += '\\' + tmp_buffer + c + else: + escaped += '\\\\' + tmp_buffer + c + tmp_buffer = '' + state = STATE_ANY + + # final state + if state == STATE_ESCAPE: + escaped += '\\\\' + elif state == STATE_ESCAPE_HEX: + escaped += '\\\\' + tmp_buffer + + if escaped[0] == ' ': # leading SPACE must be escaped + escaped = '\\' + escaped + + if escaped[-1] == ' ' and len(escaped) > 1 and escaped[-2] != '\\': # trailing SPACE must be escaped + escaped = escaped[:-1] + '\\ ' + + return escaped + + def parse_dn(dn, escape=False, strip=False): """ Parses a DN into syntactic components diff --git a/jans-linux-setup/jans_setup/static/scripts/admin_ui_plugin.py b/jans-linux-setup/jans_setup/static/scripts/admin_ui_plugin.py deleted file mode 100644 index c098c52adbc..00000000000 --- a/jans-linux-setup/jans_setup/static/scripts/admin_ui_plugin.py +++ /dev/null @@ -1,83 +0,0 @@ -import sys -import os -import time -import glob -import json -import shutil -from collections import OrderedDict - -mydir = os.getcwd() -dirs = glob.glob('/opt/jans/jans-setup/output/gluu-admin-ui*') -run_cmd = '/bin/su node -c "PATH=$PATH:/opt/jre/bin:/opt/node/bin {}"' - -for d in dirs: - if os.path.exists(os.path.join(d, '.env')): - uid_admin_dir = d - break -else: - print("Admin UI installation directory not found.") - sys.exit() - -os.chdir(uid_admin_dir) -plugin_json_fn = os.path.join(uid_admin_dir, 'plugins.config.json') - -def read_plugins(): - with open(plugin_json_fn) as f: - plugins = json.load(f, object_pairs_hook=OrderedDict) - return plugins - -plugins = read_plugins() - -def print_plugins(): - print("Available Plugins") - for i, p in enumerate(plugins): - e = '\033[92m*\033[0m' if p.get('enabled') else ' ' - print('{} {} {}'.format(e, i+1, p.get('title') or p.get('key'))) - print() - - -def exec_command(cmd): - print("\033[1mExecuting {}\033[0m".format(cmd)) - os.system(run_cmd.format(cmd)) - - -def build_copy(): - exec_command('npm run build:prod') - admin_dir = '/var/www/html/admin' - if os.path.exists(admin_dir): - os.rename(admin_dir, admin_dir + '.' + time.ctime()) - - print("Copying admin ui files to apache directory") - shutil.copytree(os.path.join(uid_admin_dir, 'dist'), admin_dir) - - -while True: - print_plugins() - user_input = input('Add/Remove/Finish/Quit [a/r/f/q]: ') - if user_input: - choice = user_input.lower()[0] - if choice == 'q': - print("Exiting without modification.") - break - - elif choice == 'f': - build_copy() - break - - elif choice == 'r': - plugin_number = input('Enter plugin number to remove :') - if plugin_number.isdigit() and int(plugin_number) <= len(plugins): - pn = int(plugin_number) - 1 - for i, p in enumerate(plugins): - if i == pn: - exec_command('npm run plugin:remove {}'.format(p['key'])) - plugins = read_plugins() - break - elif choice == 'a': - plugin_fn = input('Enter path of plugin: ') - if plugin_fn.lower().endswith('.zip'): - exec_command('npm run plugin:add {}'.format(plugin_fn)) - plugins = read_plugins() - else: - print("Can't find \033[31m{}\033[0m".format(plugin_metadata_fn)) - print() diff --git a/jans-linux-setup/jans_setup/static/scripts/find_dups.py b/jans-linux-setup/jans_setup/static/scripts/find_dups.py deleted file mode 100644 index 1758ee168d9..00000000000 --- a/jans-linux-setup/jans_setup/static/scripts/find_dups.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/python -"""Script to find the duplicate attributes in a LDIF file""" - -import sys -from ldif import LDIFParser - -fn = None -attr = None - -try: - fn = sys.argv[1] - attr = sys.argv[2] -except: - pass - -if not fn: - print "Input ldif filename not found" - sys.exit(2) - -if not attr: - print "Target attr not found" - sys.exit(2) - -class MyLDIF(LDIFParser): - def __init__(self,input,output): - LDIFParser.__init__(self,input) - self.attrs = {} - - def handle(self, dn, entry): - if entry.has_key(attr): - values = entry[attr] - if not len(values): - return - id = dn - if entry.has_key('uid'): - id = entry['uid'][0] - for value in values: - if self.attrs.has_key(value): - current_list = self.attrs[value] - current_list.append(id) - self.attrs[value] = current_list - else: - self.attrs[value] = [id] - - def get_attrMap(self): - return self.attrs - -parser = MyLDIF(open(fn, 'rb'), sys.stdout) -parser.parse() -dups = [] -attrMap = parser.get_attrMap() -for value in attrMap.keys(): - values = attrMap[value] - if len(attrMap[value]) > 1: - print "%s,%s" % (value, ",".join(values)) diff --git a/jans-linux-setup/jans_setup/static/scripts/genSchemaMarkdown.py b/jans-linux-setup/jans_setup/static/scripts/genSchemaMarkdown.py deleted file mode 100644 index e5f1fd42107..00000000000 --- a/jans-linux-setup/jans_setup/static/scripts/genSchemaMarkdown.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/python - -# MIT License -# -# Copyright 2014 Gluu, Inc. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software -# and associated documentation files (the Software), to deal in the Software without restriction, -# including without limitation the rights to use, copy, modify, merge, publish, distribute, -# sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all copies or -# substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -# FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - -# This program is used to generate the documentation for the Gluu Server schema found on -# http://www.gluu.org/docs/reference/ldap/schema - -import sys -import string -from ldif import LDIFParser, LDIFWriter - -class SchemaParser(LDIFParser): - - def __init__(self, input): - LDIFParser.__init__(self, input) - self.objectclasses = {} - self.attributes = {} - - def __repr__(self): - s = '' - oc_list = self.objectclasses.keys() - oc_list.sort() - for name in oc_list: - desc = self.objectclasses[name][0] - attrs = self.objectclasses[name][1] - s = s + "### Objectclass %s\n" % name - s = s + " * __Description__ %s\n" % desc - for attr in attrs: - attrDesc = '' - if self.attributes.has_key(attr): - attrDesc = self.attributes[attr] - s = s + " * __%s__ %s\n" % (attr, attrDesc) - s = s + "\n" - return s - - def handle(self, dn, entry): - attributeTypes = entry['attributeTypes'] - objectclasses = entry['objectclasses'] - - for attr in attributeTypes: - desc = self.getDESC(attr) - name_list = self.getName(attr) - for name in name_list: - self.attributes[name] = desc - - for oc in objectclasses: - name = self.getName(oc)[0] # Assumes OC has one name - desc = self.getDESC(oc) - if not desc: - desc = '' - mays = self.getMays(oc) - self.objectclasses[name] = (desc, mays) - - def getMays(self, oc): - mays = oc.split('MAY')[1].split('(')[1].split(')')[0].split('$') - return map(string.strip, mays) - - def getDESC(self, s): - desc = None - try: - desc = s.split('DESC')[1].split("'")[1] - except: - pass - return desc - - def getName(self,s): - name_list = None - try: - name_string = s.split('NAME')[1].split("'")[1].strip() - name_list = name_string.split(" ") - except: - pass - return name_list - -if __name__ == '__main__': - input_ldif = './static/opendj/101-ox.ldif' - output_md = "./ldap-schema-table.md" - parser = SchemaParser(open(input_ldif, 'rb')) - parser.parse() - print parser diff --git a/jans-linux-setup/jans_setup/static/scripts/printHeaders.py b/jans-linux-setup/jans_setup/static/scripts/printHeaders.py deleted file mode 100644 index e79dec4f8c4..00000000000 --- a/jans-linux-setup/jans_setup/static/scripts/printHeaders.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/python - -import os - -d = os.environ -k = d.keys() -k.sort() - -print "Content-type: text/html\n\n" - -print "printHeaders.cgi" -print "

Environment Variables

" -for item in k: - print "

%s: %s

" % (item, d[item]) -print "" diff --git a/jans-linux-setup/jans_setup/static/scripts/print_json_ldif.py b/jans-linux-setup/jans_setup/static/scripts/print_json_ldif.py deleted file mode 100644 index 2483e6751b8..00000000000 --- a/jans-linux-setup/jans_setup/static/scripts/print_json_ldif.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/python - -import sys, base64 -from ldif import LDIFParser, LDIFWriter - -fn = None - -try: - fn = sys.argv[1] -except: - pass - -if not fn: - print "Input ldif filename not found" - sys.exit(2) - -class MyLDIF(LDIFParser): - def __init__(self,input,output): - LDIFParser.__init__(self,input) - self.writer = LDIFWriter(output) - - def handle(self, dn, entry): - s = (len(dn)+4) * "=" - print "\n\n%s\ndn: %s\n%s" % (s, dn, s) - for attr in entry.keys(): - print "\nattr: %s\n%s" % (attr, (len(attr)+6) * "-") - for value in entry[attr]: - print value - -parser = MyLDIF(open(fn, 'rb'), sys.stdout) -parser.parse() - diff --git a/jans-linux-setup/jans_setup/static/scripts/random_password.py b/jans-linux-setup/jans_setup/static/scripts/random_password.py deleted file mode 100644 index 5fcb095b6c2..00000000000 --- a/jans-linux-setup/jans_setup/static/scripts/random_password.py +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/python -"""Script to generate a random password""" - -import os - -random_password = os.urandom(32).encode('hex') -print random_password diff --git a/jans-linux-setup/jans_setup/static/scripts/renew_certs.py b/jans-linux-setup/jans_setup/static/scripts/renew_certs.py deleted file mode 100644 index 482c49c524b..00000000000 --- a/jans-linux-setup/jans_setup/static/scripts/renew_certs.py +++ /dev/null @@ -1,81 +0,0 @@ -import os - -prop_file = '/install/community-edition-setup/setup.properties.last' - -prop = {} - -for l in open(prop_file): - ls=l.strip() - n = ls.find('=') - if not ls.startswith('#'): - key = ls[:n] - val = ls[n+1:].strip() - val = val.replace('\\=','=').replace('\\:',':') - prop[key] = val - - - -def delete_key(suffix): - defaultTrustStorePW = 'changeit' - defaultTrustStoreFN = '/opt/jre/jre/lib/security/cacerts' - cert = '/etc/certs/{0}.crt'.format(suffix) - - if os.path.exists(cert): - cmd=' '.join([ - '/opt/jre/bin/keytool', "-delete", "-alias", - "%s_%s" % (prop['hostname'], suffix), - "-keystore", defaultTrustStoreFN, - "-storepass", defaultTrustStorePW - ]) - os.system(cmd) - - -def import_key(suffix): - defaultTrustStorePW = 'changeit' - defaultTrustStoreFN = '/opt/jre/jre/lib/security/cacerts' - certFolder = '/etc/certs' - public_certificate = '%s/%s.crt' % (certFolder, suffix) - cmd =' '.join([ - '/opt/jre/bin/keytool', "-import", "-trustcacerts", - "-alias", "%s_%s" % (prop['hostname'], suffix), - "-file", public_certificate, "-keystore", - defaultTrustStoreFN, - "-storepass", defaultTrustStorePW, "-noprompt" - ]) - - os.system(cmd) - - -def create_new_certs(): - print "Creating certificates" - cmd_list = [ - '/usr/bin/openssl genrsa -des3 -out /etc/certs/{0}.key.orig -passout pass:secret 2048', - '/usr/bin/openssl rsa -in /etc/certs/{0}.key.orig -passin pass:secret -out /etc/certs/{0}.key', - '/usr/bin/openssl req -new -key /etc/certs/{0}.key -out /etc/certs/{0}.csr -subj ' - '"/C={4}/ST={5}/L={1}/O=Gluu/CN={2}/emailAddress={3}"'.format('{0}', prop['city'], prop['hostname'], prop['admin_email'] , prop['countryCode'] , prop['state']), - '/usr/bin/openssl x509 -req -days 365 -in /etc/certs/{0}.csr -signkey /etc/certs/{0}.key -out /etc/certs/{0}.crt', - 'chown root:gluu /etc/certs/{0}.key.orig', - 'chmod 700 /etc/certs/{0}.key.orig', - 'chown root:gluu /etc/certs/{0}.key', - 'chmod 700 /etc/certs/{0}.key', - ] - - - cert_list = ['httpd', 'asimba', 'idp-encryption', 'idp-signing', 'shibIDP', 'saml.pem'] - - for crt in cert_list: - - for cmd in cmd_list: - cmd = cmd.format(crt) - os.system(cmd) - - if not crt == 'saml.pem': - delete_key(crt) - import_key(crt) - - os.rename('/etc/certs/saml.pem.crt', '/etc/certs/saml.pem') - - os.system('chown jetty:jetty /etc/certs/jans-auth-keys.*') - - -create_new_certs() diff --git a/jans-linux-setup/jans_setup/static/scripts/testBind.py b/jans-linux-setup/jans_setup/static/scripts/testBind.py deleted file mode 100644 index b65c6789906..00000000000 --- a/jans-linux-setup/jans_setup/static/scripts/testBind.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/python - -# The MIT License (MIT) -# -# Copyright (c) 2014 Gluu -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import sys, ldap, getpass - -host = "localhost" -port = 1636 -ssl = True -bindDN = "cn=directory manager" -bindPW = "$ Enter the password before running the script" -base = "o=gluu" -scope = ldap.SCOPE_SUBTREE -attrVal = None -userPassword = None -attr = "uid" - -# GET THE ARGS / PROMPT FOR ARGS IF NONE PROVIDED -try: - attrVal = sys.argv[1] - userPassword = sys.argv[2] -except: - attrVal = raw_input("Enter %s: " % attr) - userPassword = getpass.getpass("Enter password: ") - -# CREAT THE FILTER: FOR EXAMPLE UID=*FOO* ; SUBSTRING SEARCH -filter = "%s=*%s*" % (attr, attrVal) - -# SPECIFY SSL -protocol = "ldap" -if ssl: - options = [(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)] - ldap.set_option(*options[0]) - protocol = "ldaps" - -l = ldap.initialize("%s://%s:%s" % (protocol, host, port)) -l.protocol_version = ldap.VERSION3 -l.simple_bind_s(bindDN, bindPW) -res = l.search_s(base, scope, filter) -l.unbind_s() - -if len(res)==0: - print "%s not found" % attrVal - sys.exit(1) - -if len(res)>1: - print "Non deterministic uid. Found:" - for tup in res: - print "\t%s" % tup[0] - sys.exit(2) - -dn = res[0][0] - -# IF ONLY 1 MATCH IS FOUND: PRINT DN AND BIND AS THAT USER WITH THE DN -print "Binding as: %s" % dn -l = ldap.initialize("%s://%s:%s" % (protocol, host, port)) -l.protocol_version = ldap.VERSION3 -l.simple_bind_s(dn, userPassword) diff --git a/jans-linux-setup/jans_setup/templates/jans-fido2/docuemts.ldif b/jans-linux-setup/jans_setup/templates/jans-fido2/docuemts.ldif index 501a00b736c..f2db170d5ab 100644 --- a/jans-linux-setup/jans_setup/templates/jans-fido2/docuemts.ldif +++ b/jans-linux-setup/jans_setup/templates/jans-fido2/docuemts.ldif @@ -33,7 +33,7 @@ displayName: yubico-u2f-ca-cert.crt document: %(yubico_u2f_ca_cert_base64)s inum: %(yubico_u2f_ca_cert_inum)s jansEnabled: true -jansFilePath: %(fido_document_certs_dir)s +jansFilePath: %(fido_document_authenticator_cert_dir)s jansLevel: 0 jansService: Fido2 MDS