From 4b5c02cb3f9554332631a8b19253fe519a586257 Mon Sep 17 00:00:00 2001 From: Joze RIHTARSIC <joze.RIHTARSIC@ext.ec.europa.eu> Date: Wed, 8 Jun 2022 09:04:53 +0200 Subject: [PATCH] Add unit tests --- .../smp/services/ui/UITruststoreService.java | 57 ++++++-- .../smp/services/ui/UIUserService.java | 15 +- .../services/ui/UITruststoreServiceTest.java | 50 ++++++- .../ui/UIUserServiceIntegrationTest.java | 128 ++++++++++++++++++ .../edelivery/smp/testutil/TestROUtils.java | 36 +++-- .../testutil/X509CertificateTestUtils.java | 44 ++---- .../smp/auth/SMPAuthenticationProvider.java | 26 ++-- 7 files changed, 281 insertions(+), 75 deletions(-) diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UITruststoreService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UITruststoreService.java index 747391c1d..ee0b595a5 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UITruststoreService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UITruststoreService.java @@ -1,5 +1,6 @@ package eu.europa.ec.edelivery.smp.services.ui; +import eu.europa.ec.edelivery.security.cert.CertificateValidator; import eu.europa.ec.edelivery.security.utils.X509CertificateUtils; import eu.europa.ec.edelivery.smp.data.dao.UserDao; import eu.europa.ec.edelivery.smp.data.model.DBUser; @@ -72,7 +73,7 @@ public class UITruststoreService { Map<String, X509Certificate> truststoreCertificates = new HashMap(); List<CertificateRO> certificateROList = new ArrayList<>(); - long lastUpdateTrustoreFileTime = 0; + long lastUpdateTrustStoreFileTime = 0; File lastUpdateTrustStoreFile = null; TrustManager[] trustManagers; KeyStore trustStore = null; @@ -164,7 +165,7 @@ public class UITruststoreService { normalizedTrustedList.addAll(tmpList); truststoreCertificates.putAll(hmCertificates); - lastUpdateTrustoreFileTime = truststoreFile.lastModified(); + lastUpdateTrustStoreFileTime = truststoreFile.lastModified(); lastUpdateTrustStoreFile = truststoreFile; // clear list to reload RO when required certificateROList.clear(); @@ -188,7 +189,7 @@ public class UITruststoreService { CertificateRO cro; try { cert = X509CertificateUtils.getX509Certificate(buff); - } catch ( Throwable e) { + } catch (Throwable e) { LOG.debug("Error occurred while parsing the certificate ", e); LOG.warn("Can not parse the certificate with error:[{}]!", ExceptionUtils.getRootCauseMessage(e)); cro = new CertificateRO(); @@ -222,9 +223,33 @@ public class UITruststoreService { return cro; } + public void validateCertificateWithTruststore(X509Certificate x509Certificate) throws CertificateException { + KeyStore truststore = getTrustStore(); + + if (x509Certificate == null) { + LOG.warn("The X509Certificate is null (Is the client cert header enabled?)! Skip trust validation against the truststore!"); + return; + } + + if (truststore == null) { + LOG.warn("Truststore is not configured! Skip trust validation against the truststore!"); + return; + } + + Pattern subjectRegExp = configurationService.getCertificateSubjectRegularExpression(); + List<String> allowedCertificatePolicies = configurationService.getAllowedCertificatePolicies(); + CertificateValidator certificateValidator = new CertificateValidator( + null, truststore, + subjectRegExp != null ? subjectRegExp.pattern() : null, + configurationService.getAllowedCertificatePolicies()); + LOG.debug("Validate certificate with truststore, subject regexp [{}] and allowed certificate policies [{}]", subjectRegExp, allowedCertificatePolicies); + certificateValidator.validateCertificate(x509Certificate); + } + public void checkFullCertificateValidity(X509Certificate cert) throws CertificateException { // test if certificate is valid cert.checkValidity(); + // check if certificate or its issuer is on trusted list // check only issuer because using Client-cert header we do not have whole chain. // if the truststore is empty then truststore validation is ignored @@ -234,8 +259,16 @@ public class UITruststoreService { throw new CertificateNotTrustedException("Certificate is not trusted!"); } - validateCertificatePolicyMatch(cert); - validateCertificateSubjectExpression(cert); + + + if (trustStore!=null) { + validateCertificateWithTruststore(cert); + } else { + LOG.warn("Use legacy certificate validation without truststore. Please configure truststore to increase security"); + validateCertificatePolicyMatchLegacy(cert); + validateCertificateSubjectExpressionLegacy(cert); + } + // check CRL - it is using only HTTP or https crlVerifierService.verifyCertificateCRLs(cert); } @@ -253,6 +286,7 @@ public class UITruststoreService { public void checkFullCertificateValidity(CertificateRO cert) throws CertificateException { // trust data in database + Date currentDate = Calendar.getInstance().getTime(); if (cert.getValidFrom() != null && currentDate.before(cert.getValidFrom())) { throw new CertificateNotYetValidException("Certificate: " + cert.getCertificateId() + " is valid from: " @@ -294,7 +328,7 @@ public class UITruststoreService { boolean isTruststoreChanged() { File file = getTruststoreFile(); return !Objects.equals(lastUpdateTrustStoreFile, file) || - file != null && file.lastModified() != lastUpdateTrustoreFileTime; + file != null && file.lastModified() != lastUpdateTrustStoreFileTime; } public File getTruststoreFile() { @@ -528,7 +562,14 @@ public class UITruststoreService { .collect(Collectors.toList()); } - protected void validateCertificatePolicyMatch(X509Certificate certificate) throws CertificateException { + /** + * Method validates if the certificate contains one of allowed Certificate policy. At the moment it does not validates + * the whole chain. Because in some configuration cases does not use the truststore + * + * @param certificate + * @throws CertificateException + */ + protected void validateCertificatePolicyMatchLegacy(X509Certificate certificate) throws CertificateException { // allowed list List<String> allowedCertificatePolicyOIDList = configurationService.getAllowedCertificatePolicies(); @@ -552,7 +593,7 @@ public class UITruststoreService { throw new CertificateException(excMessage); } - protected void validateCertificateSubjectExpression(X509Certificate signingCertificate) throws CertificateException { + protected void validateCertificateSubjectExpressionLegacy(X509Certificate signingCertificate) throws CertificateException { LOG.debug("Validate certificate subject"); diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java index 0ac759a0d..481581aef 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java @@ -77,6 +77,7 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { * @return ServiceResult with list */ @Transactional + @Override public ServiceResult<UserRO> getTableList(int page, int pageSize, String sortField, String sortOrder, Object filter) { ServiceResult<UserRO> resUsers = super.getTableList(page, pageSize, sortField, sortOrder, filter); resUsers.getServiceEntities().forEach(this::updateUserStatus); @@ -191,10 +192,12 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { LOG.error("Can not update user because user for id [{}] does not exist!", userId); throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "UserId", "Can not find user id!"); } - dbUser.setEmailAddress(user.getEmailAddress()); - if (user.getCertificate() != null && (dbUser.getCertificate() == null - || !StringUtils.equals(dbUser.getCertificate().getCertificateId(), user.getCertificate().getCertificateId()))) { + + if (user.getCertificate() != null && + (dbUser.getCertificate() == null + || !StringUtils.equals(dbUser.getCertificate().getCertificateId(), user.getCertificate().getCertificateId()))) { + CertificateRO certRo = user.getCertificate(); if (dbUser.getCertificate() != null) { @@ -318,6 +321,12 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { } + /** + * User can be deleted only if it does not own any of the service groups. + * + * @param dev + * @return + */ public DeleteEntityValidation validateDeleteRequest(DeleteEntityValidation dev) { List<Long> idList = dev.getListIds().stream().map(encId -> SessionSecurityUtils.decryptEntityId(encId)).collect(Collectors.toList()); List<DBUserDeleteValidation> lstMessages = userDao.validateUsersForDelete(idList); diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UITruststoreServiceTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UITruststoreServiceTest.java index 2a30e0fbc..b3c72085f 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UITruststoreServiceTest.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UITruststoreServiceTest.java @@ -10,6 +10,8 @@ import eu.europa.ec.edelivery.smp.services.ConfigurationService; import eu.europa.ec.edelivery.smp.testutil.X509CertificateTestUtils; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +import org.hamcrest.CoreMatchers; +import org.hamcrest.MatcherAssert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -24,13 +26,11 @@ import org.springframework.test.util.ReflectionTestUtils; import javax.security.auth.x500.X500Principal; import java.io.File; import java.io.IOException; +import java.math.BigInteger; import java.nio.file.Path; import java.nio.file.Paths; import java.security.cert.*; -import java.util.Calendar; -import java.util.Collections; -import java.util.List; -import java.util.UUID; +import java.util.*; import static org.junit.Assert.*; @@ -38,6 +38,12 @@ import static org.junit.Assert.*; @RunWith(SpringJUnit4ClassRunner.class) public class UITruststoreServiceTest extends AbstractServiceIntegrationTest { + public static final String CERTIFICATE_POLICY_ANY="2.5.29.32.0"; + public static final String CERTIFICATE_POLICY_QCP_NATURAL = "0.4.0.194112.1.0"; + public static final String CERTIFICATE_POLICY_QCP_LEGAL = "0.4.0.194112.1.1"; + public static final String CERTIFICATE_POLICY_QCP_NATURAL_QSCD = "0.4.0.194112.1.2"; + public static final String CERTIFICATE_POLICY_QCP_LEGAL_QSCD = "0.4.0.194112.1.3"; + public static final String S_SUBJECT_PEPPOL = "CN=POP000004,OU=PEPPOL TEST AP,O=European Commission,C=BE"; public static final String S_SUBJECT_PEPPOL_EXPANDED = "serialNumber=12345,emailAddress=test@mail.com,CN=POP000004,OU=PEPPOL TEST AP,O=European Commission,street=My Street,C=BE"; public static final String S_SUBJECT_PEPPOL_NOT_TRUSTED = "CN=POP000005,OU=PEPPOL TEST AP,O=European Commission,C=BE"; @@ -178,12 +184,13 @@ public class UITruststoreServiceTest extends AbstractServiceIntegrationTest { @Test - public void testGetCertificateDataPEM() throws IOException, CertificateException { + public void testGetCertificateDataPEMAndFullValidationExpired() throws IOException{ // given + byte[] buff = IOUtils.toByteArray(UIUserServiceIntegrationTest.class.getResourceAsStream("/truststore/SMPtest.crt")); // when - CertificateRO cer = testInstance.getCertificateData(buff); + CertificateRO cer = testInstance.getCertificateData(buff, true); //then assertEquals("CN=SMP test,O=DIGIT,C=BE:0000000000000003", cer.getCertificateId()); @@ -193,6 +200,7 @@ public class UITruststoreServiceTest extends AbstractServiceIntegrationTest { assertNotNull(cer.getValidFrom()); assertNotNull(cer.getValidTo()); assertTrue(cer.getValidFrom().before(cer.getValidTo())); + assertEquals("Certificate is expired!",cer.getInvalidReason()); } @Test @@ -411,4 +419,34 @@ public class UITruststoreServiceTest extends AbstractServiceIntegrationTest { assertEquals("SMP Test", alias); } + @Test + public void testValidateCertificatePolicyLegacyMatchOk() throws Exception { + String certSubject = "CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; + X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest(certSubject, BigInteger.TEN, Arrays.asList(CERTIFICATE_POLICY_QCP_NATURAL)); + Mockito.doReturn( Arrays.asList(CERTIFICATE_POLICY_QCP_LEGAL, CERTIFICATE_POLICY_QCP_NATURAL)).when(configurationService).getAllowedCertificatePolicies(); + testInstance.validateCertificatePolicyMatchLegacy(certificate); + } + + @Test + public void testValidateCertificatePolicyLegacyMatchEmpty() throws Exception { + String certSubject = "CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; + X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest(certSubject,BigInteger.TEN, null); + Mockito.doReturn( Arrays.asList(CERTIFICATE_POLICY_QCP_LEGAL, CERTIFICATE_POLICY_QCP_NATURAL)).when(configurationService).getAllowedCertificatePolicies(); + + CertificateException result = assertThrows(CertificateException.class, + () -> testInstance.validateCertificatePolicyMatchLegacy(certificate)); + MatcherAssert.assertThat(result.getMessage(), CoreMatchers.startsWith("Certificate has empty CertificatePolicy extension. Certificate:")); + } + + @Test + public void testValidateCertificatePolicyLegacyMatchMismatch() throws Exception { + String certSubject = "CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; + X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest(certSubject,BigInteger.TEN, Arrays.asList(CERTIFICATE_POLICY_QCP_LEGAL_QSCD)); + Mockito.doReturn( Arrays.asList(CERTIFICATE_POLICY_QCP_LEGAL, CERTIFICATE_POLICY_QCP_NATURAL)).when(configurationService).getAllowedCertificatePolicies(); + + CertificateException result = assertThrows(CertificateException.class, + () -> testInstance.validateCertificatePolicyMatchLegacy(certificate)); + MatcherAssert.assertThat(result.getMessage(), CoreMatchers.startsWith("Certificate policy verification failed.")); + } + } \ No newline at end of file diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIUserServiceIntegrationTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIUserServiceIntegrationTest.java index c6d61ec30..2c2f2bf51 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIUserServiceIntegrationTest.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIUserServiceIntegrationTest.java @@ -9,16 +9,23 @@ import eu.europa.ec.edelivery.smp.data.ui.CertificateRO; import eu.europa.ec.edelivery.smp.data.ui.ServiceResult; import eu.europa.ec.edelivery.smp.data.ui.UserRO; import eu.europa.ec.edelivery.smp.data.ui.enums.EntityROStatus; +import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; import eu.europa.ec.edelivery.smp.services.AbstractServiceIntegrationTest; import eu.europa.ec.edelivery.smp.testutil.TestDBUtils; +import eu.europa.ec.edelivery.smp.testutil.TestROUtils; +import org.hamcrest.CoreMatchers; +import org.hamcrest.MatcherAssert; +import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.crypto.bcrypt.BCrypt; import org.springframework.test.context.ContextConfiguration; import org.springframework.transaction.annotation.Transactional; +import java.math.BigInteger; import java.time.OffsetDateTime; import java.time.temporal.ChronoUnit; import java.util.*; @@ -296,4 +303,125 @@ public class UIUserServiceIntegrationTest extends AbstractServiceIntegrationTest assertNotNull(result.getAccessTokenGeneratedOn()); } + @Test + public void testUpdateUserPasswordNotMatchReqExpression() { + long authorizedUserId = 1L; + long userToUpdateId = 1L; + String authorizedPassword="testPass"; + String newPassword="newPass"; + + SMPRuntimeException result = assertThrows(SMPRuntimeException.class, + () -> testInstance.updateUserPassword(authorizedUserId, userToUpdateId, authorizedPassword, newPassword)); + + MatcherAssert.assertThat(result.getMessage(), CoreMatchers.containsString("Invalid request PasswordChange.")); + } + + @Test + public void testUpdateUserPasswordUserNotExists() { + + long authorizedUserId = 1L; + long userToUpdateId = 1L; + String authorizedPassword="oldPass"; + String newPassword="TTTTtttt1111$$$$$"; + + SMPRuntimeException result = assertThrows(SMPRuntimeException.class, + () -> testInstance.updateUserPassword(authorizedUserId, userToUpdateId, authorizedPassword, newPassword)); + + MatcherAssert.assertThat(result.getMessage(), CoreMatchers.containsString("Invalid request UserId. Error: Can not find user id!")); + } + + @Test + public void testUpdateUserPasswordUserNotAuthorized() { + String userPassword = UUID.randomUUID().toString(); + DBUser user = new DBUser(); + user.setPassword(BCrypt.hashpw(userPassword, BCrypt.gensalt())); + user.setUsername(UUID.randomUUID().toString()); + user.setEmailAddress(UUID.randomUUID().toString()); + user.setRole("ROLE"); + userDao.persistFlushDetach(user); + + long authorizedUserId = user.getId(); + long userToUpdateId = 1L; + String authorizedPassword="oldPass"; + String newPassword="TTTTtttt1111$$$$$"; + + BadCredentialsException result = assertThrows(BadCredentialsException.class, + () -> testInstance.updateUserPassword(authorizedUserId, userToUpdateId, authorizedPassword, newPassword)); + + MatcherAssert.assertThat(result.getMessage(), CoreMatchers.containsString("Password change failed; Invalid current password!")); + } + + @Test + public void testUpdateUserPasswordOK() { + String userPassword = UUID.randomUUID().toString(); + DBUser user = new DBUser(); + user.setPassword(BCrypt.hashpw(userPassword, BCrypt.gensalt())); + user.setUsername(UUID.randomUUID().toString()); + user.setEmailAddress(UUID.randomUUID().toString()); + user.setRole("ROLE"); + userDao.persistFlushDetach(user); + + long authorizedUserId = user.getId(); + long userToUpdateId = user.getId(); + String authorizedPassword=userPassword; + String newPassword="TTTTtttt1111$$$$$"; + + testInstance.updateUserPassword(authorizedUserId, userToUpdateId, authorizedPassword, newPassword); + } + + @Test + public void testUpdateUserdataOK() { + String userPassword = UUID.randomUUID().toString(); + DBUser user = new DBUser(); + user.setPassword(BCrypt.hashpw(userPassword, BCrypt.gensalt())); + user.setUsername(UUID.randomUUID().toString()); + user.setEmailAddress(UUID.randomUUID().toString()); + user.setRole("ROLE"); + userDao.persistFlushDetach(user); + + UserRO userRO = new UserRO(); + userRO.setEmailAddress(UUID.randomUUID().toString()); + userRO.setUsername(UUID.randomUUID().toString()); + userRO.setAccessTokenId(UUID.randomUUID().toString()); + userRO.setRole(UUID.randomUUID().toString()); + + testInstance.updateUserdata(user.getId(), userRO); + + DBUser changedUser = userDao.findUser(user.getId()).get(); + // fields must not change + assertEquals(user.getUsername(), changedUser.getUsername()); + assertEquals(user.getAccessToken(), changedUser.getAccessToken()); + assertEquals(user.getRole(), changedUser.getRole()); + // changed + assertEquals(userRO.getEmailAddress(), changedUser.getEmailAddress()); + } + + @Test + public void testUpdateUserdataCertificateOK() throws Exception { + String certSubject = "CN="+UUID.randomUUID().toString()+",O=eDelivery,C=EU"; + String userPassword = UUID.randomUUID().toString(); + DBUser user = new DBUser(); + user.setPassword(BCrypt.hashpw(userPassword, BCrypt.gensalt())); + user.setUsername(UUID.randomUUID().toString()); + user.setEmailAddress(UUID.randomUUID().toString()); + user.setRole("ROLE"); + userDao.persistFlushDetach(user); + + CertificateRO certificateRO = TestROUtils.createCertificateRO(certSubject, BigInteger.TEN); + UserRO userRO = new UserRO(); + userRO.setCertificate(certificateRO);; + testInstance.updateUserdata(user.getId(), userRO); + + + DBUser changedUser = userDao.findUser(user.getId()).get(); + // fields must not change + assertNotNull(changedUser.getCertificate()); + assertNotNull(changedUser.getCertificate().getPemEncoding()); + assertNotNull(certificateRO.getCertificateId(),changedUser.getCertificate().getCertificateId()); + assertNotNull(certificateRO.getSubject(),changedUser.getCertificate().getSubject()); + assertNotNull(certificateRO.getIssuer(),changedUser.getCertificate().getIssuer()); + assertNotNull(certificateRO.getSerialNumber(),changedUser.getCertificate().getSerialNumber()); + + + } } diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestROUtils.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestROUtils.java index f107f79b3..c9158a142 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestROUtils.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestROUtils.java @@ -1,13 +1,13 @@ package eu.europa.ec.edelivery.smp.testutil; +import eu.europa.ec.edelivery.smp.conversion.X509CertificateToCertificateROConverter; import eu.europa.ec.edelivery.smp.data.model.DBDomain; -import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupDomainRO; -import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupValidationRO; -import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupRO; -import eu.europa.ec.edelivery.smp.data.ui.ServiceMetadataRO; +import eu.europa.ec.edelivery.smp.data.ui.*; import eu.europa.ec.edelivery.smp.data.ui.enums.EntityROStatus; import java.io.IOException; +import java.math.BigInteger; +import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.UUID; @@ -17,18 +17,20 @@ import static eu.europa.ec.edelivery.smp.testutil.TestConstants.SIMPLE_EXTENSION public class TestROUtils { + public static final X509CertificateToCertificateROConverter CERT_CONVERTER = new X509CertificateToCertificateROConverter(); - public static ServiceMetadataRO createServiceMetadataDomain(DBDomain domain, ServiceGroupRO sgo, String docid, String docSch){ + + public static ServiceMetadataRO createServiceMetadataDomain(DBDomain domain, ServiceGroupRO sgo, String docid, String docSch) { ServiceMetadataRO sgdmd = new ServiceMetadataRO(); sgdmd.setDomainCode(domain.getDomainCode()); sgdmd.setSmlSubdomain(domain.getSmlSubdomain()); sgdmd.setDocumentIdentifier(docid); sgdmd.setDocumentIdentifierScheme(docSch); - sgdmd.setXmlContent(generateServiceMetadata(sgo.getParticipantIdentifier(), sgo.getParticipantScheme(), docid,docSch )); + sgdmd.setXmlContent(generateServiceMetadata(sgo.getParticipantIdentifier(), sgo.getParticipantScheme(), docid, docSch)); return sgdmd; } - public static ServiceGroupDomainRO createServiceGroupDomain(DBDomain domain){ + public static ServiceGroupDomainRO createServiceGroupDomain(DBDomain domain) { ServiceGroupDomainRO sgd = new ServiceGroupDomainRO(); sgd.setDomainId(domain.getId()); @@ -41,8 +43,8 @@ public class TestROUtils { return createROServiceGroup(TestConstants.TEST_SG_ID_1, TestConstants.TEST_SG_SCHEMA_1); } - public static ServiceGroupRO createROServiceGroupForDomains(DBDomain ... domains) { - ServiceGroupRO sgo = createROServiceGroup(TestConstants.TEST_SG_ID_1, TestConstants.TEST_SG_SCHEMA_1); + public static ServiceGroupRO createROServiceGroupForDomains(DBDomain... domains) { + ServiceGroupRO sgo = createROServiceGroup(TestConstants.TEST_SG_ID_1, TestConstants.TEST_SG_SCHEMA_1); Arrays.asList(domains).forEach(domain -> { ServiceGroupDomainRO sgd = createServiceGroupDomain(domain); sgo.getServiceGroupDomains().add(sgd); @@ -50,8 +52,8 @@ public class TestROUtils { return sgo; } - public static ServiceGroupRO createROServiceGroupForDomains(String id, String sch, DBDomain ... domains) { - ServiceGroupRO sgo = createROServiceGroup(id, sch); + public static ServiceGroupRO createROServiceGroupForDomains(String id, String sch, DBDomain... domains) { + ServiceGroupRO sgo = createROServiceGroup(id, sch); Arrays.asList(domains).forEach(domain -> { ServiceGroupDomainRO sgd = createServiceGroupDomain(domain); sgo.getServiceGroupDomains().add(sgd); @@ -75,12 +77,12 @@ public class TestROUtils { return grp; } - public static String generateExtension(){ + public static String generateExtension() { return String.format(SIMPLE_EXTENSION_XML, UUID.randomUUID().toString()); } - public static String generateServiceMetadata(String partId, String partSch, String docId, String docSch){ - return String.format(SIMPLE_DOCUMENT_XML, partSch, partId,docSch, docId, UUID.randomUUID().toString() ); + public static String generateServiceMetadata(String partId, String partSch, String docId, String docSch) { + return String.format(SIMPLE_DOCUMENT_XML, partSch, partId, docSch, docId, UUID.randomUUID().toString()); } public static ServiceGroupValidationRO getValidExtension() throws IOException { @@ -116,4 +118,10 @@ public class TestROUtils { sg.setExtension(extension); return sg; } + + + public static CertificateRO createCertificateRO(String certSubject, BigInteger serial) throws Exception { + X509Certificate cert = X509CertificateTestUtils.createX509CertificateForTest(certSubject, serial, null); + return CERT_CONVERTER.convert(cert); + } } diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/X509CertificateTestUtils.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/X509CertificateTestUtils.java index 570354e4e..f02a0a01a 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/X509CertificateTestUtils.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/X509CertificateTestUtils.java @@ -1,5 +1,6 @@ package eu.europa.ec.edelivery.smp.testutil; +import eu.europa.ec.edelivery.security.utils.X509CertificateUtils; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.*; import org.bouncycastle.cert.X509v3CertificateBuilder; @@ -14,6 +15,7 @@ import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.X509Certificate; +import java.time.OffsetDateTime; import java.util.*; import java.util.stream.Collectors; @@ -77,40 +79,18 @@ public class X509CertificateTestUtils { return certs; } - /** - * Method generates certificate chain - * @param subjects - * @param certificatePoliciesOids - * @param startDate - * @param expiryDate - * @return - * @throws Exception - */ - public static X509Certificate[] createCertificateChain(String[] subjects, List<List<String>> certificatePoliciesOids, Date startDate, Date expiryDate) throws Exception { - String issuer = null; - PrivateKey issuerKey = null; - long iSerial = 10000; - X509Certificate[] certs = new X509Certificate[subjects.length]; - - int index = subjects.length; - for (String sbj: subjects){ - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - keyGen.initialize(1024); - KeyPair key = keyGen.generateKeyPair(); - - X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(new X500Name(issuer ==null? sbj:issuer), - BigInteger.valueOf(iSerial++), startDate, expiryDate, new X500Name(sbj), - SubjectPublicKeyInfo.getInstance(key.getPublic().getEncoded())); - - ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WITHRSA") - .setProvider("BC").build(issuerKey ==null?key.getPrivate():issuerKey); - certs[--index] = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certBuilder.build(sigGen)); - issuer= sbj; - issuerKey = key.getPrivate(); + public static X509Certificate createX509CertificateForTest( String subject, BigInteger serial, List<String> listOfPolicyOIDs) throws Exception { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); + keyGen.initialize(2048); + KeyPair key = keyGen.generateKeyPair(); + KeyUsage usage = new KeyUsage(244); + X509Certificate cert = X509CertificateUtils.createCertificate(serial, + key.getPublic(), subject, OffsetDateTime.now().minusDays(1L), + OffsetDateTime.now().plusYears(5L), (String)null, + key.getPrivate(), false, -1, usage, "SHA256withRSA",listOfPolicyOIDs); - } - return certs; + return cert; } } diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationProvider.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationProvider.java index fc4fa5f2d..dabd7ab4c 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationProvider.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationProvider.java @@ -1,11 +1,9 @@ package eu.europa.ec.edelivery.smp.auth; import eu.europa.ec.edelivery.security.PreAuthenticatedCertificatePrincipal; -import eu.europa.ec.edelivery.security.cert.CertificateValidator; import eu.europa.ec.edelivery.smp.data.dao.UserDao; import eu.europa.ec.edelivery.smp.data.model.DBCertificate; import eu.europa.ec.edelivery.smp.data.model.DBUser; -import eu.europa.ec.edelivery.smp.data.ui.UserRO; import eu.europa.ec.edelivery.smp.data.ui.auth.SMPAuthority; import eu.europa.ec.edelivery.smp.data.ui.enums.AlertSuspensionMomentEnum; import eu.europa.ec.edelivery.smp.data.ui.enums.CredentialTypeEnum; @@ -99,9 +97,9 @@ public class SMPAuthenticationProvider implements AuthenticationProvider { LOG.warn("Unknown or null PreAuthenticatedAuthenticationToken principal type: " + principal); } } else if (authenticationToken instanceof UsernamePasswordAuthenticationToken) { - LOG.info("try to authentication Token: [{}] with user:[{}]" , authenticationToken.getClass(), authenticationToken.getPrincipal()); - if (CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER.equalsIgnoreCase((String)authenticationToken.getPrincipal()) - || CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER.equalsIgnoreCase((String)authenticationToken.getPrincipal())){ + LOG.info("try to authentication Token: [{}] with user:[{}]", authenticationToken.getClass(), authenticationToken.getPrincipal()); + if (CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER.equalsIgnoreCase((String) authenticationToken.getPrincipal()) + || CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER.equalsIgnoreCase((String) authenticationToken.getPrincipal())) { LOG.debug("Ignore CAS authentication and leave it to cas authentication module"); return null; } @@ -133,13 +131,15 @@ public class SMPAuthenticationProvider implements AuthenticationProvider { X509Certificate x509Certificate = principal.getCertificate(); String userToken = principal.getName(); - if (truststore != null && x509Certificate != null) { - CertificateValidator certificateValidator = new CertificateValidator( - null, truststore, null); + + if (x509Certificate != null) { try { - certificateValidator.validateCertificate(x509Certificate); + truststoreService.validateCertificateWithTruststore(x509Certificate); } catch (CertificateException e) { - throw new BadCredentialsException("Certificate is not trusted!"); + String message = "Certificate is not trusted!"; + LOG.securityWarn(SMPMessageCode.SEC_USER_CERT_INVALID, userToken , message + + " The cert chain is not in truststore or either subject regexp or allowed cert policies does not match"); + throw new BadCredentialsException(message); } } @@ -163,6 +163,7 @@ public class SMPAuthenticationProvider implements AuthenticationProvider { DBCertificate certificate = user.getCertificate(); // check if certificate is valid Date currentDate = Calendar.getInstance().getTime(); + // this is legacy code because some setups does not have truststore configured // validate dates if (principal.getNotBefore() == null) { String msg = "Invalid certificate configuration: 'Not Before' value is missing!"; @@ -181,6 +182,7 @@ public class SMPAuthenticationProvider implements AuthenticationProvider { LOG.securityWarn(SMPMessageCode.SEC_USER_CERT_INVALID, userToken, msg); throw new AuthenticationServiceException(msg); } + // check if issuer or subject are in trusted list if (!(truststoreService.isSubjectOnTrustedList(principal.getSubjectOriginalDN()) || truststoreService.isSubjectOnTrustedList(principal.getIssuerDN()))) { @@ -217,7 +219,7 @@ public class SMPAuthenticationProvider implements AuthenticationProvider { public void delayResponse(long startTime) { - int delayInMS = configurationService.getAccessTokenLoginFailDelayInMilliSeconds() - (int) (Calendar.getInstance().getTimeInMillis() - startTime); + int delayInMS = configurationService.getAccessTokenLoginFailDelayInMilliSeconds() - (int) (Calendar.getInstance().getTimeInMillis() - startTime); if (delayInMS > 0) { try { LOG.debug("Delay response for [{}] ms to mask password/username login failures!", delayInMS); @@ -317,7 +319,7 @@ public class SMPAuthenticationProvider implements AuthenticationProvider { // the webservice authentication with corresponding web-service authority; SMPAuthority authority = SMPAuthority.getAuthorityByRoleName("WS_" + user.getRole()); // the webservice authentication does not support session set the session secret is null! - SMPUserDetails userDetails = new SMPUserDetails(user, null, Collections.singletonList(authority)); + SMPUserDetails userDetails = new SMPUserDetails(user, null, Collections.singletonList(authority)); SMPAuthenticationToken smpAuthenticationToken = new SMPAuthenticationToken(authenticationTokenId, authenticationTokenValue, -- GitLab