diff --git a/mid-java-client-core/pom.xml b/mid-java-client-core/pom.xml index 8bf9c2c..dcaa129 100644 --- a/mid-java-client-core/pom.xml +++ b/mid-java-client-core/pom.xml @@ -6,7 +6,7 @@ ch.mobileid.mid-java-client mid-java-client-parent - 1.5.6 + 1.5.7 mid-java-client-core @@ -31,6 +31,15 @@ org.bouncycastle bcpkix-jdk18on + + com.fasterxml.jackson.core + jackson-databind + + + org.apache.commons + commons-text + 1.12.0 + diff --git a/mid-java-client-core/src/main/java/ch/swisscom/mid/client/impl/SignatureValidatorImpl.java b/mid-java-client-core/src/main/java/ch/swisscom/mid/client/impl/SignatureValidatorImpl.java index ea40909..632eb78 100644 --- a/mid-java-client-core/src/main/java/ch/swisscom/mid/client/impl/SignatureValidatorImpl.java +++ b/mid-java-client-core/src/main/java/ch/swisscom/mid/client/impl/SignatureValidatorImpl.java @@ -3,9 +3,13 @@ import ch.swisscom.mid.client.SignatureValidator; import ch.swisscom.mid.client.config.ConfigurationException; import ch.swisscom.mid.client.config.SignatureValidationConfiguration; +import ch.swisscom.mid.client.model.DataToBeSignedTXNResponseType; import ch.swisscom.mid.client.model.SignatureValidationFailureReason; import ch.swisscom.mid.client.model.SignatureValidationResult; import ch.swisscom.mid.client.model.Traceable; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.cms.CMSException; @@ -32,6 +36,7 @@ import java.util.regex.Pattern; import static ch.swisscom.mid.client.utils.Utils.*; +import static org.apache.commons.text.StringEscapeUtils.unescapeJava; /** * Default implementation of {@link SignatureValidator}. @@ -43,15 +48,22 @@ public class SignatureValidatorImpl implements SignatureValidator { private static final Logger log = LoggerFactory.getLogger(Loggers.SIGNATURE_VALIDATOR); private final KeyStore validationTrustStore; + private ObjectMapper jacksonMapper; public SignatureValidatorImpl(SignatureValidationConfiguration config) { Security.addProvider(new BouncyCastleProvider()); this.validationTrustStore = loadValidationTruststore(config); + + jacksonMapper = new ObjectMapper(); + jacksonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); } public SignatureValidatorImpl(KeyStore validationTrustStore) { Security.addProvider(new BouncyCastleProvider()); this.validationTrustStore = validationTrustStore; + + jacksonMapper = new ObjectMapper(); + jacksonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); } @Override @@ -143,14 +155,45 @@ public SignatureValidationResult validateSignature(String base64SignatureContent } } catch (OperatorCreationException | CMSException e) { log.warn("Failed to validate the signature against the signer info " + - "during the signature CMS content validation{}", printTrace(trace), e); + "during the signature CMS content validation{}", printTrace(trace), e); result.setValidationException(e); result.setValidationFailureReason(SignatureValidationFailureReason.SIGNATURE_VALIDATION_FAILED); return result; } // verify the DTBS from the request vs the one from the response - if (requestedDtbs.equals(result.getSignedDtbs())) { + if (result.getSignedDtbs() == null) { + log.info("Failed to match the DTBS texts, requested=[{}] vs signed=[{}]{}", requestedDtbs, result.getSignedDtbs(), printTrace(trace)); + result.setValidationFailureReason(SignatureValidationFailureReason.DATA_TO_BE_SIGNED_NOT_MATCHING); + return result; + } + if (requestedDtbs.startsWith("{")) { + result.setDtbsMatching(false); + try { + // parse item + String[] dtbsArray = requestedDtbs.split("\"dtbd\":"); + String reqDtbsValueStr = ""; + if (dtbsArray.length > 0) { + String reqDtbsValueRaw = dtbsArray[1]; + reqDtbsValueStr = reqDtbsValueRaw.substring(0, reqDtbsValueRaw.length() - 1); + } + // fix response DTBS string + String escResultDtbs = unescapeJava(result.getSignedDtbs() + .replace("\"format_version\"", "\\\"format_version\\\"") + .replace("\"content_string\"", "\\\"content_string\\\"") + .replace("\"[", "[") + .replace("]\"", "]")); + + DataToBeSignedTXNResponseType resDtbs = jacksonMapper.readValue(escResultDtbs, DataToBeSignedTXNResponseType.class); + String finalResDtbs = jacksonMapper.writeValueAsString(resDtbs.getDtbd()); + result.setDtbsMatching(reqDtbsValueStr.equals(finalResDtbs)); + } catch (JsonProcessingException e) { + log.info("Failed to match the DTBS texts, requested=[{}] vs signed=[{}]{}", requestedDtbs, result.getSignedDtbs(), printTrace(trace)); + result.setValidationFailureReason(SignatureValidationFailureReason.DATA_TO_BE_SIGNED_NOT_MATCHING); + } + return result; + + } else if (requestedDtbs.equals(result.getSignedDtbs())) { result.setDtbsMatching(true); } else { log.info("Failed to match the DTBS texts, requested=[{}] vs signed=[{}]{}", requestedDtbs, result.getSignedDtbs(), printTrace(trace)); @@ -225,23 +268,23 @@ private KeyStore loadValidationTruststore(SignatureValidationConfiguration confi if (config.getTrustStoreFile() != null) { try (InputStream is = new FileInputStream(config.getTrustStoreFile())) { trustStore.load(is, config.getTrustStorePassword() == null ? - null : config.getTrustStorePassword().toCharArray()); + null : config.getTrustStorePassword().toCharArray()); } } else if (config.getTrustStoreClasspathFile() != null) { try (InputStream is = this.getClass().getResourceAsStream(config.getTrustStoreClasspathFile())) { trustStore.load(is, config.getTrustStorePassword() == null ? - null : config.getTrustStorePassword().toCharArray()); + null : config.getTrustStorePassword().toCharArray()); } } else { try (InputStream is = new ByteArrayInputStream(config.getTrustStoreBytes())) { trustStore.load(is, config.getTrustStorePassword() == null ? - null : config.getTrustStorePassword().toCharArray()); + null : config.getTrustStorePassword().toCharArray()); } } return trustStore; } catch (Exception e) { throw new ConfigurationException("Failed to initialize the digital signature validation truststore " + - "(Mobile ID CMS signature validator)", e); + "(Mobile ID CMS signature validator)", e); } } } diff --git a/mid-java-client-core/src/main/java/ch/swisscom/mid/client/model/DataToBeSigned.java b/mid-java-client-core/src/main/java/ch/swisscom/mid/client/model/DataToBeSigned.java index c74fa73..54fd346 100644 --- a/mid-java-client-core/src/main/java/ch/swisscom/mid/client/model/DataToBeSigned.java +++ b/mid-java-client-core/src/main/java/ch/swisscom/mid/client/model/DataToBeSigned.java @@ -15,6 +15,7 @@ */ package ch.swisscom.mid.client.model; +import static ch.swisscom.mid.client.utils.Utils.dataIsTXNApprovalRequestType; import static ch.swisscom.mid.client.utils.Utils.dataNotEmpty; public class DataToBeSigned { @@ -61,14 +62,17 @@ public void validateYourself() { dataNotEmpty(data, "The data in the DataToBeSigned cannot be null or empty"); dataNotEmpty(encoding, "The encoding in the DataToBeSigned cannot be null or empty (set it to \"UTF-8\", for example)"); dataNotEmpty(mimeType, "The mime type in the DataToBeSigned cannot be null or empty (set it to \"text/plain\", for example)"); + if (mimeType.equalsIgnoreCase("application/vnd.mobileid.txn-approval")) { + dataIsTXNApprovalRequestType(data, "The DataToBeSigned format is not valid for mime type 'application/vnd.mobileid.txn-approval'"); + } } @Override public String toString() { return "DataToBeSigned{" + - "data='" + data + '\'' + - ", encoding='" + encoding + '\'' + - ", mimeType='" + mimeType + '\'' + - '}'; + "data='" + data + '\'' + + ", encoding='" + encoding + '\'' + + ", mimeType='" + mimeType + '\'' + + '}'; } } diff --git a/mid-java-client-core/src/main/java/ch/swisscom/mid/client/model/DataToBeSignedTXNResponseType.java b/mid-java-client-core/src/main/java/ch/swisscom/mid/client/model/DataToBeSignedTXNResponseType.java new file mode 100644 index 0000000..157125b --- /dev/null +++ b/mid-java-client-core/src/main/java/ch/swisscom/mid/client/model/DataToBeSignedTXNResponseType.java @@ -0,0 +1,48 @@ +package ch.swisscom.mid.client.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + + +public class DataToBeSignedTXNResponseType { + @JsonProperty("format_version") + private String formatVersion; + + @JsonProperty("content_string") + private List> dtbd = new ArrayList<>(); + + public DataToBeSignedTXNResponseType() { + } + + public DataToBeSignedTXNResponseType(String formatVersion, List> dtbd) { + this.formatVersion = formatVersion; + this.dtbd = dtbd; + } + + public String getFormatVersion() { + return formatVersion; + } + + public void setFormatVersion(String formatVersion) { + this.formatVersion = formatVersion; + } + + public List> getDtbd() { + return dtbd; + } + + public void setDtbd(List> dtbd) { + this.dtbd = dtbd; + } + + @Override + public String toString() { + return "DataToBeSignedTXNResponseType{" + + "formatVersion='" + formatVersion + '\'' + + ", content_string=" + dtbd + + '}'; + } +} diff --git a/mid-java-client-core/src/main/java/ch/swisscom/mid/client/model/GeofencingAdditionalService.java b/mid-java-client-core/src/main/java/ch/swisscom/mid/client/model/GeofencingAdditionalService.java index 183bee9..1bcca15 100644 --- a/mid-java-client-core/src/main/java/ch/swisscom/mid/client/model/GeofencingAdditionalService.java +++ b/mid-java-client-core/src/main/java/ch/swisscom/mid/client/model/GeofencingAdditionalService.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Swisscom (Schweiz) AG + * Copyright 2021-2025 Swisscom (Schweiz) AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -88,10 +88,12 @@ public void setMaxAccuracyMeters(String maxAccuracyMeters) { public boolean isDefined() { - if (countryWhiteList != null && !countryWhiteList.isEmpty()) return true; - if(countryBlackList!=null && !countryBlackList.isEmpty()) return true; - if (minDeviceConfidence != null && !minDeviceConfidence.isEmpty() && !minDeviceConfidence.equalsIgnoreCase("0")) return true; - if (minLocationConfidence != null && !minLocationConfidence.isEmpty() && !minLocationConfidence.equalsIgnoreCase("0")) return true; + if (countryWhiteList != null && !countryWhiteList.isEmpty()) return true; + if (countryBlackList != null && !countryBlackList.isEmpty()) return true; + if (minDeviceConfidence != null && !minDeviceConfidence.isEmpty() && !minDeviceConfidence.equalsIgnoreCase("0")) + return true; + if (minLocationConfidence != null && !minLocationConfidence.isEmpty() && !minLocationConfidence.equalsIgnoreCase("0")) + return true; if (maxAccuracyMeters != null && !maxAccuracyMeters.isEmpty()) return true; if (maxTimestampMinutes != null && !maxTimestampMinutes.isEmpty()) return true; return false; diff --git a/mid-java-client-core/src/main/java/ch/swisscom/mid/client/model/StatusCode.java b/mid-java-client-core/src/main/java/ch/swisscom/mid/client/model/StatusCode.java index d427a7f..40fe2d6 100644 --- a/mid-java-client-core/src/main/java/ch/swisscom/mid/client/model/StatusCode.java +++ b/mid-java-client-core/src/main/java/ch/swisscom/mid/client/model/StatusCode.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Swisscom (Schweiz) AG + * Copyright 2021-2025 Swisscom (Schweiz) AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,57 +15,58 @@ */ package ch.swisscom.mid.client.model; +import ch.swisscom.mid.client.impl.Loggers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import ch.swisscom.mid.client.impl.Loggers; - public enum StatusCode implements DocumentedEnum { REQUEST_OK(100, false, "The request from the AP has been accepted."), WRONG_PARAM(101, true, "The AP’s request contains wrong parameters."), MISSING_PARAM(102, true, "The AP’s request has missing parameters."), WRONG_DATA_LENGTH(103, true, "The AP’s request contains a DTBD message " - + "that is exceeding the max. allowed length."), + + "that is exceeding the max. allowed length."), UNAUTHORIZED_ACCESS(104, true, "AP is not authorized to access the Mobile ID API. " - + "This is typically due to a wrong AP_ID value or missing X509 client certificate."), + + "This is typically due to a wrong AP_ID value or missing X509 client certificate."), UNKNOWN_CLIENT(105, true, "The MSISDN value is unknown to the MID service. " - + "There is no Mobile ID user with that MSISDN value."), + + "There is no Mobile ID user with that MSISDN value."), INAPPROPRIATE_DATA(107, true, "The AP’s request was not accepted due to inappropriate data. " - + "Typically, the DTBD message does not contain the mandatory prefix string " - + "(see section 2.19 of the Reference Guide) that is a unique string for each AP."), + + "Typically, the DTBD message does not contain the mandatory prefix string " + + "(see section 2.19 of the Reference Guide) that is a unique string for each AP."), INCOMPATIBLE_INTERFACE(108, true, "The AP’s request contains bad data. " - + "Typically, a wrong MajorVersion or MinorVersion value has been set in the request."), + + "Typically, a wrong MajorVersion or MinorVersion value has been set in the request."), UNSUPPORTED_PROFILE(109, true, "Either the AP has specified an MSS signature profile value " - + "that the MSSP does not support or the AP is not authorized to use the " - + "Signature Profile. See section 3.2.1 of the Reference Guide."), + + "that the MSSP does not support or the AP is not authorized to use the " + + "Signature Profile. See section 3.2.1 of the Reference Guide."), EXPIRED_TRANSACTION(208, true, "The transaction timed out. The AP may try again."), OTA_ERROR(209, true, "A Problem related to the MSSP internal Over-The-Air (OTA) communication " - + "with the Mobile ID user’s SIM. " - + "Typically, there is a temporary problem with SMS communication."), + + "with the Mobile ID user’s SIM. " + + "Typically, there is a temporary problem with SMS communication."), USER_CANCEL(401, true, "The user cancelled the request at the mobile phone."), PIN_NR_BLOCKED(402, true, "The Mobile ID PIN of the SIM method is blocked. " - + "The user must reactivate the Mobile ID SIM card on the Mobile ID selfcare portal."), + + "The user must reactivate the Mobile ID SIM card on the Mobile ID selfcare portal."), CARD_BLOCKED(403, true, "The Mobile ID user is currently suspended. " - + "Please contact Swisscom Support."), + + "Please contact Swisscom Support."), NO_KEY_FOUND(404, true, "The Mobile ID user exists but is not in an active state. " - + "The user must activate the account on the Mobile ID selfcare portal."), + + "The user must activate the account on the Mobile ID selfcare portal."), PB_SIGNATURE_PROCESS(406, true, "A signature transaction is already on-going. " - + "Please try again later."), + + "Please try again later."), NO_CERT_FOUND(422, true, "The Mobile ID user exists but is not in an active state. " - + "The user must activate the account on the Mobile ID selfcare portal."), + + "The user must activate the account on the Mobile ID selfcare portal."), + GEOFENCING_POLICY_VIOLATION(450, true, "Geo policy for referenced AP ID was violated. " + + "Please try again later or contact Swisscom Support, if the problem persists."), SIGNATURE(500, false, "The MSS Signature transaction was successful."), REVOKED_CERTIFICATE(501, false, "The Mobile ID user’s509 certificate has been revoked. " - + "The user must re-activate the account on the Mobile ID selfcare portal."), + + "The user must re-activate the account on the Mobile ID selfcare portal."), VALID_SIGNATURE(502, false, "The MSS Signature transaction was successful."), INVALID_SIGNATURE(503, false, "The MSS Signature transaction failed due to invalid signature data. " - + "The user must re-activate the account on the Mobile ID selfcare portal."), + + "The user must re-activate the account on the Mobile ID selfcare portal."), OUTSTANDING_TRANSACTION(504, false, "The MSS Signature transaction is outstanding. " - + "The AP must try again later."), + + "The AP must try again later."), CONNECTION_REFUSED(780, true, "The connection to the service was refused. " - + "The client did not present a valid TLS certificate"), + + "The client did not present a valid TLS certificate"), INTERNAL_ERROR(900, true, "An internal error on MSSP has occurred. " - + "Please try again later or contact Swisscom Support, if the problem persists."); + + "Please try again later or contact Swisscom Support, if the problem persists."); private static final Logger log = LoggerFactory.getLogger(Loggers.CLIENT); diff --git a/mid-java-client-core/src/main/java/ch/swisscom/mid/client/model/TXNApprovalReqType.java b/mid-java-client-core/src/main/java/ch/swisscom/mid/client/model/TXNApprovalReqType.java new file mode 100644 index 0000000..1852d24 --- /dev/null +++ b/mid-java-client-core/src/main/java/ch/swisscom/mid/client/model/TXNApprovalReqType.java @@ -0,0 +1,48 @@ +package ch.swisscom.mid.client.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class TXNApprovalReqType { + + @JsonProperty("type") + private String type; + + @JsonProperty("dtbd") + private List> dtbd = new ArrayList<>(); + + public TXNApprovalReqType() { + } + + public TXNApprovalReqType(String type, List> dtbd) { + this.type = type; + this.dtbd = dtbd; + } + + @Override + public String toString() { + return "TXNApprovalReqType{" + + "type='" + type + '\'' + + ", dtbd=" + dtbd + + '}'; + } + + public void setType(String type) { + this.type = type; + } + + public void setDtbd(List> dtbd) { + this.dtbd = dtbd; + } + + public String getType() { + return type; + } + + public List> getDtbd() { + return dtbd; + } +} diff --git a/mid-java-client-core/src/main/java/ch/swisscom/mid/client/utils/Utils.java b/mid-java-client-core/src/main/java/ch/swisscom/mid/client/utils/Utils.java index ff07d5c..1ab6efd 100644 --- a/mid-java-client-core/src/main/java/ch/swisscom/mid/client/utils/Utils.java +++ b/mid-java-client-core/src/main/java/ch/swisscom/mid/client/utils/Utils.java @@ -1,34 +1,40 @@ /* - * Copyright 2021 Swisscom (Schweiz) AG * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * * Copyright 2021-2025 Swisscom (Schweiz) AG + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. */ + package ch.swisscom.mid.client.utils; +import ch.swisscom.mid.client.config.ConfigurationException; +import ch.swisscom.mid.client.model.DataAssemblyException; +import ch.swisscom.mid.client.model.TXNApprovalReqType; +import ch.swisscom.mid.client.model.Traceable; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.GregorianCalendar; import java.util.List; import java.util.UUID; -import javax.xml.datatype.DatatypeConfigurationException; -import javax.xml.datatype.DatatypeFactory; -import javax.xml.datatype.XMLGregorianCalendar; - -import ch.swisscom.mid.client.config.ConfigurationException; -import ch.swisscom.mid.client.model.DataAssemblyException; -import ch.swisscom.mid.client.model.Traceable; - public class Utils { public static void configNotNull(Object target, String errorMessage) throws ConfigurationException { @@ -49,6 +55,21 @@ public static void dataNotNull(Object target, String errorMessage) throws DataAs } } + public static void dataIsTXNApprovalRequestType(String data, String errorMessage) throws DataAssemblyException { + final ObjectMapper jacksonMapper = new ObjectMapper(); + jacksonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + TXNApprovalReqType parsedDtbs = null; + try { + parsedDtbs = jacksonMapper.readValue(data, TXNApprovalReqType.class); + } catch (JsonProcessingException e) { + throw new DataAssemblyException(errorMessage); + } + if (parsedDtbs == null || parsedDtbs.getDtbd().isEmpty()) { + throw new DataAssemblyException(errorMessage); + } + + } + public static void dataNotEmpty(List list, String errorMessage) throws DataAssemblyException { if (list == null || list.size() == 0) { throw new DataAssemblyException(errorMessage); diff --git a/mid-java-client-rest/pom.xml b/mid-java-client-rest/pom.xml index 7a3c878..5ba56f9 100644 --- a/mid-java-client-rest/pom.xml +++ b/mid-java-client-rest/pom.xml @@ -6,7 +6,7 @@ ch.mobileid.mid-java-client mid-java-client-parent - 1.5.6 + 1.5.7 mid-java-client-rest @@ -77,7 +77,5 @@ 1.18.30 provided - - diff --git a/mid-java-client-rest/src/main/java/ch/swisscom/mid/client/rest/FaultProcessor.java b/mid-java-client-rest/src/main/java/ch/swisscom/mid/client/rest/FaultProcessor.java index d63f3f9..ed01806 100644 --- a/mid-java-client-rest/src/main/java/ch/swisscom/mid/client/rest/FaultProcessor.java +++ b/mid-java-client-rest/src/main/java/ch/swisscom/mid/client/rest/FaultProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Swisscom (Schweiz) AG + * Copyright 2021-2025 Swisscom (Schweiz) AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,20 +15,18 @@ */ package ch.swisscom.mid.client.rest; -import org.apache.hc.client5.http.HttpHostConnectException; - -import java.io.IOException; -import java.net.ConnectException; -import java.net.SocketTimeoutException; - -import javax.net.ssl.SSLException; - import ch.swisscom.mid.client.model.FailureReason; import ch.swisscom.mid.client.model.Fault; import ch.swisscom.mid.client.model.StatusCode; import ch.swisscom.mid.client.rest.model.fault.Code; import ch.swisscom.mid.client.rest.model.fault.MSSFault; import ch.swisscom.mid.client.rest.model.fault.SubCode; +import org.apache.hc.client5.http.HttpHostConnectException; + +import javax.net.ssl.SSLException; +import java.io.IOException; +import java.net.ConnectException; +import java.net.SocketTimeoutException; public class FaultProcessor { diff --git a/mid-java-client-rest/src/test/java/ch/swisscom/mid/client/rest/SyncSignatureTest.java b/mid-java-client-rest/src/test/java/ch/swisscom/mid/client/rest/SyncSignatureTest.java index 5281ff5..e0ef60b 100644 --- a/mid-java-client-rest/src/test/java/ch/swisscom/mid/client/rest/SyncSignatureTest.java +++ b/mid-java-client-rest/src/test/java/ch/swisscom/mid/client/rest/SyncSignatureTest.java @@ -15,33 +15,26 @@ */ package ch.swisscom.mid.client.rest; -import ch.swisscom.mid.client.config.TlsConfiguration; -import com.github.tomakehurst.wiremock.WireMockServer; -import com.github.tomakehurst.wiremock.http.MimeType; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - import ch.swisscom.mid.client.MIDClient; import ch.swisscom.mid.client.MIDFlowException; import ch.swisscom.mid.client.config.DefaultConfiguration; import ch.swisscom.mid.client.impl.MIDClientImpl; import ch.swisscom.mid.client.model.*; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.http.MimeType; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import static ch.swisscom.mid.client.rest.TestData.CUSTOM_AP_ID; import static ch.swisscom.mid.client.rest.TestData.CUSTOM_AP_PASSWORD; import static ch.swisscom.mid.client.rest.TestSupport.*; -import static ch.swisscom.mid.client.rest.TestSupport.fileToBytes; -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.containing; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.*; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.*; public class SyncSignatureTest { @@ -68,11 +61,11 @@ public static void tearDownThisClass() { @Test public void testSignature_success() { server.stubFor( - post(urlEqualTo(DefaultConfiguration.REST_ENDPOINT_SUB_URL)) - .willReturn( - aResponse() - .withHeader("Content-Type", MimeType.JSON.toString()) - .withBody(fileToString("/samples/rest-response-signature.json")))); + post(urlEqualTo(DefaultConfiguration.REST_ENDPOINT_SUB_URL)) + .willReturn( + aResponse() + .withHeader("Content-Type", MimeType.JSON.toString()) + .withBody(fileToString("/samples/rest-response-signature.json")))); SignatureRequest signatureRequest = buildSignatureRequest(); SignatureResponse response = client.requestSyncSignature(signatureRequest); @@ -87,13 +80,13 @@ public void testSignature_success() { @Test public void testSignature_success_overrideApIdAndApPassword() { server.stubFor( - post(urlEqualTo(DefaultConfiguration.REST_ENDPOINT_SUB_URL)) - .withRequestBody(containing("\"" + CUSTOM_AP_ID + "\"")) - .withRequestBody(containing("\"" + CUSTOM_AP_PASSWORD + "\"")) - .willReturn( - aResponse() - .withHeader("Content-Type", MimeType.JSON.toString()) - .withBody(fileToString("/samples/rest-response-signature.json")))); + post(urlEqualTo(DefaultConfiguration.REST_ENDPOINT_SUB_URL)) + .withRequestBody(containing("\"" + CUSTOM_AP_ID + "\"")) + .withRequestBody(containing("\"" + CUSTOM_AP_PASSWORD + "\"")) + .willReturn( + aResponse() + .withHeader("Content-Type", MimeType.JSON.toString()) + .withBody(fileToString("/samples/rest-response-signature.json")))); SignatureRequest signatureRequest = buildSignatureRequest(); signatureRequest.setOverrideApId(CUSTOM_AP_ID); @@ -113,12 +106,12 @@ public void testSignature_success_overrideApIdAndApPassword() { @Test public void testSignature_userCancel() { server.stubFor( - post(urlEqualTo("/rest/service")) - .willReturn( - aResponse() - .withStatus(500) - .withHeader("Content-Type", MimeType.JSON.toString()) - .withBody(fileToString("/samples/rest-response-fault-user-cancel.json")))); + post(urlEqualTo("/rest/service")) + .willReturn( + aResponse() + .withStatus(500) + .withHeader("Content-Type", MimeType.JSON.toString()) + .withBody(fileToString("/samples/rest-response-fault-user-cancel.json")))); SignatureRequest signatureRequest = buildSignatureRequest(); try { @@ -135,12 +128,12 @@ public void testSignature_userCancel() { @Test public void testSignature_conFailure_responseTimeout() { server.stubFor( - post(urlEqualTo("/rest/service")) - .willReturn( - aResponse() - .withStatus(200) - .withBody("") - .withFixedDelay(5000))); + post(urlEqualTo("/rest/service")) + .willReturn( + aResponse() + .withStatus(200) + .withBody("") + .withFixedDelay(5000))); SignatureRequest signatureRequest = buildSignatureRequest(); try { @@ -154,6 +147,46 @@ public void testSignature_conFailure_responseTimeout() { } } + @Test + public void testSignatureWithDtbsAsTXNApproval_success() { + server.stubFor( + post(urlEqualTo(DefaultConfiguration.REST_ENDPOINT_SUB_URL)) + .willReturn( + aResponse() + .withHeader("Content-Type", MimeType.JSON.toString()) + .withBody(fileToString("/samples/rest-response-signature.json")))); + + SignatureRequest signatureRequest = buildSignatureReqWithDTBDTXNApproval( + "{\"type\":\"Address Change\",\"dtbd\":[{\"key\":\"Client\",\"value\":\"#CLIENT#\"},{\"key\":\"FN\",\"value\":\"Test User\"},{\"key\":\"Country\",\"value\":\"Poland\"},{\"key\":\"Session\",\"value\":\"#SESSION#\"}]}" + ); + SignatureResponse response = client.requestSyncSignature(signatureRequest); + assertThat(response.getStatus().getStatusCode(), is(StatusCode.SIGNATURE)); + assertThat(response.getStatus().getStatusCodeString(), is("500")); + assertThat(response.getStatus().getStatusMessage(), is("SIGNATURE")); + assertThat(response.getSignatureProfile(), is(TestData.CUSTOM_SIGNATURE_PROFILE)); + assertThat(response.getBase64Signature(), is(notNullValue())); + assertThat(response.getBase64Signature().length(), is(TestData.BASE64_SIGNATURE_LENGTH)); + } + + @Test + public void testSignatureWithDtbsAsTXNApproval_invalidDtbsContent() { + server.stubFor( + post(urlEqualTo(DefaultConfiguration.REST_ENDPOINT_SUB_URL)) + .willReturn( + aResponse() + .withHeader("Content-Type", MimeType.JSON.toString()) + .withBody(fileToString("/samples/rest-response-signature.json")))); + + SignatureRequest signatureRequest = buildSignatureReqWithDTBDTXNApproval( + "{\"type2\":\"Address Change\",\"dtbd\":[{}]" + ); + + Exception exception = assertThrows(DataAssemblyException.class, () -> { + SignatureResponse response = client.requestSyncSignature(signatureRequest); + }); + assertEquals("The DataToBeSigned format is not valid for mime type 'application/vnd.mobileid.txn-approval'", exception.getMessage()); + } + // ---------------------------------------------------------------------------------------------------- private static SignatureRequest buildSignatureRequest() { @@ -167,4 +200,16 @@ private static SignatureRequest buildSignatureRequest() { request.addAdditionalService(new GeofencingAdditionalService()); return request; } + + private static SignatureRequest buildSignatureReqWithDTBDTXNApproval(String dtbs) { + SignatureRequest request = new SignatureRequest(); + request.setUserLanguage(UserLanguage.ENGLISH); + request.getDataToBeSigned().setData(dtbs); + request.getDataToBeSigned().setEncodingToUtf8(); + request.getDataToBeSigned().setMimeType("application/vnd.mobileid.txn-approval"); + request.getMobileUser().setMsisdn(TrialNumbers.ONE_THAT_GIVES_MISSING_PARAM); + request.setSignatureProfile(SignatureProfiles.DEFAULT_PROFILE); + request.addAdditionalService(new GeofencingAdditionalService()); + return request; + } } diff --git a/mid-java-client-soap/pom.xml b/mid-java-client-soap/pom.xml index 4ff3c8d..e69ea8a 100644 --- a/mid-java-client-soap/pom.xml +++ b/mid-java-client-soap/pom.xml @@ -6,7 +6,7 @@ ch.mobileid.mid-java-client mid-java-client-parent - 1.5.6 + 1.5.7 mid-java-client-soap diff --git a/mid-java-client-usage/pom.xml b/mid-java-client-usage/pom.xml index b6ed74d..394b9d4 100644 --- a/mid-java-client-usage/pom.xml +++ b/mid-java-client-usage/pom.xml @@ -6,7 +6,7 @@ ch.mobileid.mid-java-client mid-java-client-parent - 1.5.6 + 1.5.7 mid-java-client-usage @@ -125,7 +125,8 @@ assemble - ${project.build.directory}/release/mid-client-${project.version} + ${project.build.directory}/release/mid-client-${project.version} + ch.swisscom.mid.client.cli.Cli diff --git a/pom.xml b/pom.xml index 9879b89..1e4bd6e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ch.mobileid.mid-java-client mid-java-client-parent - 1.5.6 + 1.5.7 Mobile ID Java client Mobile ID client reference implementation in Java https://github.com/MobileID-Strong-Authentication/mobileid-client-java @@ -56,7 +56,7 @@ ch.qos.logback logback-classic - 1.4.12 + 1.5.18 com.fasterxml.jackson.core @@ -120,6 +120,11 @@ bcpkix-jdk18on 1.75 + + org.bouncycastle + bcutil-jdk18on + 1.78.1 + org.junit.jupiter junit-jupiter-api