diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/SmpInfoRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/SmpInfoRO.java index 52889dc13f1505fbd9e45c2878931c47f54583b4..416602a8cd19f3894ed890f5f0c4080ee28d9599 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/SmpInfoRO.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/SmpInfoRO.java @@ -12,11 +12,11 @@ import java.util.List; */ public class SmpInfoRO implements Serializable { private static final long serialVersionUID = -49712226560325302L; - String version; - String ssoAuthenticationLabel; - String ssoAuthenticationURI; - String contextPath; - List<String> authTypes = new ArrayList<>(); + private String version; + private String ssoAuthenticationLabel; + private String ssoAuthenticationURI; + private String contextPath; + private List<String> authTypes = new ArrayList<>(); public String getVersion() { return version; diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/logging/SMPMessageCode.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/logging/SMPMessageCode.java index 284fe5132d6f94f8b8dd9bf845c652fb1a0282aa..5f0a72fcbf2809c35c6871167d2536e25c6e1fda 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/logging/SMPMessageCode.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/logging/SMPMessageCode.java @@ -52,8 +52,6 @@ public enum SMPMessageCode implements MessageCode { SEC_USER_SUSPENDED("SEC-008", "User [{}] is temporarily suspended."), SEC_INVALID_TOKEN("SEC-009", "User [{}] has invalid token value for token id: [{}]."), SEC_TRUSTSTORE_CERT_INVALID("SEC-010", "Truststore certificate with alias [{}] is invalid: [{}]."), - - ; String code; diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/PayloadValidatorService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/PayloadValidatorService.java index eed736b21bc704b0a721cbbc5ba85b5614a9d7f4..7f8cecbe019d4d5f9c9acb64fef3e36ce15cae93 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/PayloadValidatorService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/PayloadValidatorService.java @@ -7,7 +7,6 @@ import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; import eu.europa.ec.smp.spi.PayloadValidatorSpi; import eu.europa.ec.smp.spi.exceptions.PayloadValidatorSpiException; import org.apache.commons.lang3.exception.ExceptionUtils; -import org.slf4j.Marker; import org.springframework.stereotype.Service; import java.io.InputStream; @@ -53,8 +52,8 @@ public class PayloadValidatorService { validatorSpi.validatePayload(payload, mimeType); } } catch (PayloadValidatorSpiException e) { - LOG.error(SECURITY_MARKER,"Content validation failed: ["+ExceptionUtils.getRootCauseMessage(e)+"]" , e ); - throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST,"Upload payload", "Content validation failed"); + LOG.error(SECURITY_MARKER, "Content validation failed: [" + ExceptionUtils.getRootCauseMessage(e) + "]", e); + throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "Upload payload", "Content validation failed"); } } } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceGroupService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceGroupService.java index d9b6e893b8fa433aeecb8455656a132f7a5e47e7..ec52ce985f945a51386b6398ab11127a05b41b42 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceGroupService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceGroupService.java @@ -28,12 +28,6 @@ import org.oasis_open.docs.bdxr.ns.smp._2016._05.ServiceMetadata; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import javax.xml.XMLConstants; -import javax.xml.transform.*; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; -import java.io.StringReader; -import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; @@ -709,37 +703,4 @@ public class UIServiceGroupService extends UIServiceBase<DBServiceGroup, Service serviceGroupRO.getParticipantScheme(), ExceptionUtils.getRootCauseMessage(e)); } } - - /** - * Method - * - * @param sgExtension - * @return - */ - public ServiceGroupValidationRO formatExtension(ServiceGroupValidationRO sgExtension) { - if (sgExtension == null) { - throw new SMPRuntimeException(INVALID_REQUEST, "Format extension", "Missing Extension parameter"); - } else if (StringUtils.isBlank(sgExtension.getExtension())) { - sgExtension.setErrorMessage("Empty extension"); - } else { - try { - Source xmlInput = new StreamSource(new StringReader(sgExtension.getExtension())); - StringWriter stringWriter = new StringWriter(); - StreamResult xmlOutput = new StreamResult(stringWriter); - - TransformerFactory transformerFactory = TransformerFactory.newInstance(); - transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - transformerFactory.setAttribute("indent-number", 4); - - Transformer transformer = transformerFactory.newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.transform(xmlInput, xmlOutput); - sgExtension.setExtension(xmlOutput.getWriter().toString()); - } catch (TransformerException e) { - sgExtension.setErrorMessage(ExceptionUtils.getRootCauseMessage(e)); - } - } - return sgExtension; - } - } 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 3f9c262a3579259b4bf99914b5dee108e7ed9e5a..452dfa2a5855a289a83760e78d8ad85c34e4fc61 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 @@ -18,7 +18,6 @@ import org.bouncycastle.asn1.x509.CertificatePolicies; import org.bouncycastle.asn1.x509.PolicyInformation; import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.convert.ConversionService; import org.springframework.stereotype.Service; @@ -30,6 +29,7 @@ import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; +import javax.security.auth.x500.X500Principal; import java.io.*; import java.security.*; import java.security.cert.Certificate; @@ -58,20 +58,13 @@ public class UITruststoreService { new SimpleDateFormat("MMM d hh:mm:ss yyyy zzz", US) ); - @Autowired - private ConfigurationService configurationService; - - @Autowired - CRLVerifierService crlVerifierService; - - @Autowired - ConversionService conversionService; - - @Autowired - UserDao userDao; + // dependand beans + private final ConfigurationService configurationService; + private final CRLVerifierService crlVerifierService; + private final ConversionService conversionService; + private final UserDao userDao; List<String> normalizedTrustedList = new ArrayList<>(); - Map<String, X509Certificate> truststoreCertificates = new HashMap(); List<CertificateRO> certificateROList = new ArrayList<>(); long lastUpdateTrustStoreFileTime = 0; @@ -79,6 +72,12 @@ public class UITruststoreService { TrustManager[] trustManagers; KeyStore trustStore = null; + public UITruststoreService(ConfigurationService configurationService, CRLVerifierService crlVerifierService, ConversionService conversionService, UserDao userDao) { + this.configurationService = configurationService; + this.crlVerifierService = crlVerifierService; + this.conversionService = conversionService; + this.userDao = userDao; + } @PostConstruct public void init() { @@ -167,7 +166,7 @@ public class UITruststoreService { certificateROList.clear(); } - protected void validateAndLogError(X509Certificate x509Certificate, String alias){ + protected void validateAndLogError(X509Certificate x509Certificate, String alias) { try { x509Certificate.checkValidity(); } catch (CertificateExpiredException | CertificateNotYetValidException ex) { @@ -204,38 +203,42 @@ public class UITruststoreService { cro = convertToRo(cert); if (validate) { - // first expect the worst - cro.setInvalid(true); - cro.setInvalidReason("Certificate is not validated!"); - try { - checkFullCertificateValidity(cert); - validateCertificateNotUsed(cro); - cro.setInvalid(false); - cro.setInvalidReason(null); - } catch (CertificateExpiredException ex) { - LOG.securityError(SEC_USER_CERT_INVALID, cro.getCertificateId(), ex.getMessage()); - cro.setInvalidReason("Certificate is expired!"); - } catch (CertificateNotYetValidException ex) { - LOG.securityError(SEC_USER_CERT_INVALID, cro.getCertificateId(), ex.getMessage()); - cro.setInvalidReason("Certificate is not yet valid!"); - } catch (CertificateRevokedException ex) { - LOG.securityError(SEC_USER_CERT_INVALID, cro.getCertificateId(), ex.getMessage()); - cro.setInvalidReason("Certificate is revoked!"); - } catch (CertificateNotTrustedException ex) { - LOG.securityError(SEC_USER_CERT_INVALID, cro.getCertificateId(), ex.getMessage()); - cro.setInvalidReason("Certificate is not trusted!"); - } catch (CertificateException e) { - LOG.securityError(SEC_USER_CERT_INVALID, e, cro.getCertificateId(), e.getMessage()); - if (ExceptionUtils.getRootCause(e) instanceof CertPathValidatorException) { - cro.setInvalidReason("Certificate is not trusted! Invalid certificate policy path!"); - } else { - cro.setInvalidReason(e.getMessage()); - } - } + validateNewCertificate(cert, cro); } return cro; } + public void validateNewCertificate(X509Certificate cert, CertificateRO cro) { + // first expect the worst + cro.setInvalid(true); + cro.setInvalidReason("Certificate is not validated!"); + try { + checkFullCertificateValidity(cert); + validateCertificateNotUsed(cro); + cro.setInvalid(false); + cro.setInvalidReason(null); + } catch (CertificateExpiredException ex) { + LOG.securityError(SEC_USER_CERT_INVALID, cro.getCertificateId(), ex.getMessage()); + cro.setInvalidReason("Certificate is expired!"); + } catch (CertificateNotYetValidException ex) { + LOG.securityError(SEC_USER_CERT_INVALID, cro.getCertificateId(), ex.getMessage()); + cro.setInvalidReason("Certificate is not yet valid!"); + } catch (CertificateRevokedException ex) { + LOG.securityError(SEC_USER_CERT_INVALID, cro.getCertificateId(), ex.getMessage()); + cro.setInvalidReason("Certificate is revoked!"); + } catch (CertificateNotTrustedException ex) { + LOG.securityError(SEC_USER_CERT_INVALID, cro.getCertificateId(), ex.getMessage()); + cro.setInvalidReason("Certificate is not trusted!"); + } catch (CertificateException e) { + LOG.securityError(SEC_USER_CERT_INVALID, e, cro.getCertificateId(), e.getMessage()); + if (ExceptionUtils.getRootCause(e) instanceof CertPathValidatorException) { + cro.setInvalidReason("Certificate is not trusted! Invalid certificate policy path!"); + } else { + cro.setInvalidReason(e.getMessage()); + } + } + } + public void validateCertificateWithTruststore(X509Certificate x509Certificate) throws CertificateException { KeyStore truststore = getTrustStore(); @@ -289,8 +292,7 @@ public class UITruststoreService { public void validateCertificateNotUsed(CertificateRO cert) throws CertificateException { Optional<DBUser> user = userDao.findUserByCertificateId(cert.getCertificateId()); if (user.isPresent()) { - String msg = "Certificate: '" + cert.getCertificateId() + "'" + - " is already used!"; + String msg = "Certificate: [" + cert.getCertificateId() + "] is already used!"; LOG.debug("Certificate with id: [{}] is already used by user with username [{}]", user.get().getUsername()); throw new CertificateException(msg); } @@ -358,7 +360,7 @@ public class UITruststoreService { } - private KeyStore loadTruststore(File truststoreFile) { + protected KeyStore loadTruststore(File truststoreFile) { if (truststoreFile == null) { LOG.error("Truststore file is not configured! Update SMP configuration!"); @@ -501,7 +503,7 @@ public class UITruststoreService { alias = alias + "_" + iVal; } } catch (KeyStoreException e) { - LOG.error("Error occured while reading truststore for validating alias: " + alias, e); + LOG.error("Error occurred while reading truststore for validating existance of the alias: " + alias, e); } return alias; } @@ -596,7 +598,7 @@ public class UITruststoreService { throw new CertificateException(excMessage); } - Optional<String> result = certPolicyList.stream().filter(certPolicyOID -> allowedCertificatePolicyOIDList.contains(certPolicyOID)).findFirst(); + Optional<String> result = certPolicyList.stream().filter(allowedCertificatePolicyOIDList::contains).findFirst(); if (result.isPresent()) { LOG.info("Certificate [{}] is trusted with certificate policy [{}]", certificate, result.get()); return; @@ -607,20 +609,17 @@ public class UITruststoreService { protected void validateCertificateSubjectExpressionLegacy(X509Certificate signingCertificate) throws CertificateException { LOG.debug("Validate certificate subject"); - - - String subject = signingCertificate.getSubjectDN().getName(); Pattern certSubjectExpression = configurationService.getCertificateSubjectRegularExpression(); if (certSubjectExpression == null) { LOG.debug("Certificate subject regular expression is empty, verification is disabled."); return; } + String subject = signingCertificate.getSubjectX500Principal().getName(X500Principal.RFC2253); if (!certSubjectExpression.matcher(subject).matches()) { String excMessage = String.format("Certificate subject [%s] does not match the regular expression configured [%s]", subject, certSubjectExpression); LOG.error(excMessage); throw new CertificateException(excMessage); } } - } diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/cron/SMPDynamicCronTriggerTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/cron/SMPDynamicCronTriggerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..58bf9b89a64550a6926d57426048e9a02d396a4d --- /dev/null +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/cron/SMPDynamicCronTriggerTest.java @@ -0,0 +1,57 @@ +package eu.europa.ec.edelivery.smp.cron; + +import eu.europa.ec.edelivery.smp.data.ui.enums.SMPPropertyEnum; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.scheduling.TriggerContext; +import org.springframework.scheduling.support.CronExpression; + +import java.time.Clock; + +import static org.junit.Assert.*; + +public class SMPDynamicCronTriggerTest { + + @Test + public void nextExecutionTime() { + SMPPropertyEnum propertyEnum = SMPPropertyEnum.SMP_ALERT_CREDENTIALS_CRON; + SMPDynamicCronTrigger testInstance = new SMPDynamicCronTrigger(propertyEnum.getDefValue(), propertyEnum); + // not yet triggered + assertNull(testInstance.getNextExecutionDate()); + + TriggerContext triggerContext = Mockito.mock(TriggerContext.class); + Mockito.doReturn(Clock.systemDefaultZone()).when(triggerContext).getClock(); + testInstance.nextExecutionTime(triggerContext); + + assertNotNull(testInstance.getNextExecutionDate()); + } + + @Test + public void getExpression() { + SMPPropertyEnum propertyEnum = SMPPropertyEnum.SMP_ALERT_CREDENTIALS_CRON; + SMPDynamicCronTrigger testInstance = new SMPDynamicCronTrigger(propertyEnum.getDefValue(), propertyEnum); + + assertEquals(propertyEnum.getDefValue(), testInstance.getExpression()); + } + + @Test + public void updateCronExpression() { + String newCronExpression = "0 */10 * * * *"; + SMPPropertyEnum propertyEnum = SMPPropertyEnum.SMP_ALERT_CREDENTIALS_CRON; + SMPDynamicCronTrigger testInstance = new SMPDynamicCronTrigger(propertyEnum.getDefValue(), propertyEnum); + assertEquals(propertyEnum.getDefValue(), testInstance.getExpression()); + + testInstance.updateCronExpression(CronExpression.parse(newCronExpression)); + + assertEquals(newCronExpression, testInstance.getExpression()); + assertNotNull(testInstance.getNextExecutionDate()); + } + + @Test + public void getCronExpressionProperty() { + SMPPropertyEnum propertyEnum = SMPPropertyEnum.SMP_ALERT_CREDENTIALS_CRON; + SMPDynamicCronTrigger testInstance = new SMPDynamicCronTrigger(propertyEnum.getDefValue(), propertyEnum); + + assertEquals(propertyEnum, testInstance.getCronExpressionProperty()); + } +} \ No newline at end of file diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UITruststoreServiceIntegrationTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UITruststoreServiceIntegrationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..652e735f1144f5085fd2e26bef362ef412877468 --- /dev/null +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UITruststoreServiceIntegrationTest.java @@ -0,0 +1,463 @@ +package eu.europa.ec.edelivery.smp.services.ui; + +import eu.europa.ec.edelivery.smp.data.ui.CertificateRO; +import eu.europa.ec.edelivery.smp.exceptions.CertificateNotTrustedException; +import eu.europa.ec.edelivery.smp.exceptions.ErrorCode; +import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; +import eu.europa.ec.edelivery.smp.services.AbstractServiceIntegrationTest; +import eu.europa.ec.edelivery.smp.services.CRLVerifierService; +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; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +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.*; + +import static org.junit.Assert.*; + + +@RunWith(SpringJUnit4ClassRunner.class) +public class UITruststoreServiceIntegrationTest 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"; + + public static final String S_SUBJECT_TEST = "CN=SMP test,O=DIGIT,C=BE"; + + + Path resourceDirectory = Paths.get("src", "test", "resources", "truststore"); + Path targetDirectory = Paths.get("target", "truststore"); + + @Rule + public ExpectedException expectedEx = ExpectedException.none(); + + @Autowired + protected UITruststoreService testInstance; + + @Autowired + protected ConfigurationService configurationService; + + @Autowired + protected CRLVerifierService crlVerifierService; + + @Before + public void setup() throws IOException { + configurationService = Mockito.spy(configurationService); + crlVerifierService = Mockito.spy(crlVerifierService); + + ReflectionTestUtils.setField(testInstance, "crlVerifierService", crlVerifierService); + + ReflectionTestUtils.setField(testInstance, "configurationService", configurationService); + + File truststoreFile = new File(targetDirectory.toFile(), "smp-truststore.jks"); + Mockito.doReturn("test123").when(configurationService).getTruststoreCredentialToken(); + Mockito.doReturn(truststoreFile).when(configurationService).getTruststoreFile(); + Mockito.doReturn(targetDirectory.toFile()).when(configurationService).getConfigurationFolder(); + Mockito.doReturn(true).when(configurationService).forceCRLValidation(); + resetKeystore(); + + testInstance.refreshData(); + } + + public void resetKeystore() throws IOException { + FileUtils.deleteDirectory(targetDirectory.toFile()); + FileUtils.copyDirectory(resourceDirectory.toFile(), targetDirectory.toFile()); + } + + @Test + public void testGetKeystoreEntriesList() { + List<String> lst = testInstance.getNormalizedTrustedList(); + assertEquals(2, lst.size()); + assertEquals(S_SUBJECT_PEPPOL, lst.get(0)); + assertEquals(S_SUBJECT_TEST, lst.get(1)); + } + + @Test + public void testSubjectValid() { + // given when + // then + assertTrue(testInstance.isSubjectOnTrustedList(S_SUBJECT_PEPPOL)); + } + + @Test + public void testSubjectValidExpanded() { + // given when + // then + assertTrue(testInstance.isSubjectOnTrustedList(S_SUBJECT_PEPPOL_EXPANDED)); + } + + @Test + public void testSubjectNotTrusted() { + // given when + // then + assertFalse(testInstance.isSubjectOnTrustedList(S_SUBJECT_PEPPOL_NOT_TRUSTED)); + } + + @Test + public void testAddCertificate() throws Exception { + // given + String certSubject = "CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; + String alias = UUID.randomUUID().toString(); + X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest(certSubject); + int iSize = testInstance.getNormalizedTrustedList().size(); + assertFalse(testInstance.isSubjectOnTrustedList(certSubject)); + // when + testInstance.addCertificate(alias, certificate); + + // then + assertEquals(iSize + 1, testInstance.getNormalizedTrustedList().size()); + assertTrue(testInstance.isSubjectOnTrustedList(certSubject)); + } + + @Test + public void testAddCertificateRDN() throws Exception { + // given + String certSubject = "GIVENNAME=John+SERIALNUMBER=1+CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; + String alias = UUID.randomUUID().toString(); + X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest(certSubject); + String val = certificate.getSubjectX500Principal().getName(X500Principal.RFC2253); + int iSize = testInstance.getNormalizedTrustedList().size(); + assertFalse(testInstance.isSubjectOnTrustedList(certSubject)); + // when + testInstance.addCertificate(alias, certificate); + + // then + assertEquals(iSize + 1, testInstance.getNormalizedTrustedList().size()); + assertTrue(testInstance.isSubjectOnTrustedList(certSubject)); + } + + @Test + public void testDeleteCertificate() throws Exception { + // given + List<CertificateRO> list = testInstance.getCertificateROEntriesList(); + int iSize = list.size(); + assertTrue(list.size() > 0); + CertificateRO certificateRO = list.get(0); + assertTrue(testInstance.isSubjectOnTrustedList(certificateRO.getSubject())); + // when + testInstance.deleteCertificate(certificateRO.getAlias()); + + // then + assertEquals(iSize - 1, testInstance.getNormalizedTrustedList().size()); + assertFalse(testInstance.isSubjectOnTrustedList(certificateRO.getSubject())); + } + + @Test + public void testIsTruststoreChanged() throws Exception { + // given + String certSubject = "CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; + String alias = UUID.randomUUID().toString(); + X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest(certSubject); + testInstance.addCertificate(alias, certificate); + assertTrue(testInstance.isSubjectOnTrustedList(certSubject)); + // when rollback truststore + resetKeystore(); + // then it should detect file change and refresh certificates + assertFalse(testInstance.isSubjectOnTrustedList(certSubject)); + } + + + @Test + public void testGetCertificateDataPEMAndFullValidationExpired() throws IOException { + // given + + byte[] buff = IOUtils.toByteArray(UIUserServiceIntegrationTest.class.getResourceAsStream("/truststore/SMPtest.crt")); + + // when + CertificateRO cer = testInstance.getCertificateData(buff, true); + + //then + assertEquals("CN=SMP test,O=DIGIT,C=BE:0000000000000003", cer.getCertificateId()); + assertEquals("CN=Intermediate CA,O=DIGIT,C=BE", cer.getIssuer()); + assertEquals("1.2.840.113549.1.9.1=#160c736d7040746573742e636f6d,CN=SMP test,O=DIGIT,C=BE", cer.getSubject()); + assertEquals("3", cer.getSerialNumber()); + assertNotNull(cer.getValidFrom()); + assertNotNull(cer.getValidTo()); + assertTrue(cer.getValidFrom().before(cer.getValidTo())); + assertEquals("Certificate is expired!", cer.getInvalidReason()); + } + + @Test + public void testGetCertificateDataPEMWithHeader() throws IOException, CertificateException { + // given + byte[] buff = IOUtils.toByteArray(UIUserServiceIntegrationTest.class.getResourceAsStream("/truststore/pem-with-header.crt")); + + // when + CertificateRO cer = testInstance.getCertificateData(buff); + + //then + assertEquals("CN=alice,O=www.freelan.org,C=FR:0000000000000001", cer.getCertificateId()); + assertEquals("1.2.840.113549.1.9.1=#1613636f6e7461637440667265656c616e2e6f7267,CN=Freelan Sample Certificate Authority,OU=freelan,O=www.freelan.org,L=Strasbourg,ST=Alsace,C=FR", cer.getIssuer()); + assertEquals("1.2.840.113549.1.9.1=#1613636f6e7461637440667265656c616e2e6f7267,CN=alice,OU=freelan,O=www.freelan.org,ST=Alsace,C=FR", cer.getSubject()); + assertEquals("1", cer.getSerialNumber()); + assertNotNull(cer.getValidFrom()); + assertNotNull(cer.getValidTo()); + assertTrue(cer.getValidFrom().before(cer.getValidTo())); + } + + @Test + public void testGetCertificateDataSMime() throws IOException, CertificateException { + // given + byte[] buff = IOUtils.toByteArray(UIUserServiceIntegrationTest.class.getResourceAsStream("/certificates/cert-smime.pem")); + + // when + CertificateRO cer = testInstance.getCertificateData(buff); + + //then + assertEquals("CN=edelivery_sml,O=European Commission,C=BE:3cfe6b37e4702512c01e71f9b9175464", cer.getCertificateId()); + assertEquals("CN=PEPPOL SERVICE METADATA PUBLISHER TEST CA - G2,OU=FOR TEST ONLY,O=OpenPEPPOL AISBL,C=BE", cer.getIssuer()); + assertEquals("C=BE,O=European Commission,OU=PEPPOL TEST SMP,CN=edelivery_sml", cer.getSubject()); + assertEquals("3cfe6b37e4702512c01e71f9b9175464", cer.getSerialNumber()); + assertNotNull(cer.getValidFrom()); + assertNotNull(cer.getValidTo()); + assertTrue(cer.getValidFrom().before(cer.getValidTo())); + } + + @Test + public void testGetCertificateDataDER() throws IOException, CertificateException { + // given + byte[] buff = IOUtils.toByteArray(UIUserServiceIntegrationTest.class.getResourceAsStream("/truststore/NewPeppolAP.crt")); + + // when + CertificateRO cer = testInstance.getCertificateData(buff); + + //then + assertEquals("CN=POP000004,O=European Commission,C=BE:474980c51478cf62761667461aef5e8e", cer.getCertificateId()); + assertEquals("CN=PEPPOL ACCESS POINT TEST CA - G2,OU=FOR TEST ONLY,O=OpenPEPPOL AISBL,C=BE", cer.getIssuer()); + assertEquals("C=BE,O=European Commission,OU=PEPPOL TEST AP,CN=POP000004", cer.getSubject()); + assertEquals("474980c51478cf62761667461aef5e8e", cer.getSerialNumber()); + assertNotNull(cer.getValidFrom()); + assertNotNull(cer.getValidTo()); + assertTrue(cer.getValidFrom().before(cer.getValidTo())); + } + + @Test + public void testCheckFullCertificateValidityNotYetValid() throws Exception { + // given + String certSubject = "CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; + Calendar from = Calendar.getInstance(); + Calendar to = Calendar.getInstance(); + to.add(Calendar.DAY_OF_YEAR, 2); + from.add(Calendar.DAY_OF_YEAR, 1); + X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest( + "10af", certSubject, certSubject, from.getTime(), to.getTime(), Collections.emptyList()); + + //then + expectedEx.expect(CertificateNotYetValidException.class); + // when + testInstance.checkFullCertificateValidity(certificate); + } + + @Test + public void testCheckFullCertificateValidityExpired() throws Exception { + // given + String certSubject = "CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; + Calendar from = Calendar.getInstance(); + Calendar to = Calendar.getInstance(); + to.add(Calendar.DAY_OF_YEAR, -1); + from.add(Calendar.DAY_OF_YEAR, -2); + X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest( + "10af", certSubject, certSubject, from.getTime(), to.getTime(), Collections.emptyList()); + + //then + expectedEx.expect(CertificateExpiredException.class); + // when + testInstance.checkFullCertificateValidity(certificate); + } + + @Test + public void testCheckFullCertificateNotTrusted() throws Exception { + // given + String crlUrl = "https://localhost/crl"; + String revokedSerialFromList = "0011"; + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + X509CRL crl = (X509CRL) cf.generateCRL(getClass().getResourceAsStream("/certificates/smp-crl-test.crl")); + + Mockito.doReturn(crl).when(crlVerifierService).getCRLByURL(crlUrl); + + Calendar from = Calendar.getInstance(); + Calendar to = Calendar.getInstance(); + to.add(Calendar.DAY_OF_YEAR, 1); + from.add(Calendar.DAY_OF_YEAR, -2); + X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest( + revokedSerialFromList, S_SUBJECT_PEPPOL_NOT_TRUSTED, S_SUBJECT_PEPPOL_NOT_TRUSTED, from.getTime(), to.getTime(), Collections.singletonList(crlUrl)); + //then + expectedEx.expect(CertificateNotTrustedException.class); + // when + testInstance.checkFullCertificateValidity(certificate); + } + + + @Test + public void testCheckFullCertificateValidityRevoked() throws Exception { + // given + String crlUrl = "https://localhost/crl"; + String revokedSerialFromList = "0011"; + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + X509CRL crl = (X509CRL) cf.generateCRL(getClass().getResourceAsStream("/certificates/smp-crl-test.crl")); + + Mockito.doReturn(crl).when(crlVerifierService).downloadCRL(ArgumentMatchers.eq(crlUrl), ArgumentMatchers.anyBoolean()); + + String certSubject = "CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; + Calendar from = Calendar.getInstance(); + Calendar to = Calendar.getInstance(); + to.add(Calendar.DAY_OF_YEAR, 1); + from.add(Calendar.DAY_OF_YEAR, -2); + X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest( + revokedSerialFromList, certSubject, certSubject, from.getTime(), to.getTime(), Collections.singletonList(crlUrl)); + // add as trusted certificate + testInstance.addCertificate(UUID.randomUUID().toString(), certificate); + + + //then + expectedEx.expect(CertificateRevokedException.class); + // when + testInstance.checkFullCertificateValidity(certificate); + } + + @Test + public void testCheckFullCertificateValidityNotForceCRL() throws Exception { + // given + String crlUrl = "https://localhost/crl"; + String revokedSerialFromList = "0011"; + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + X509CRL crl = (X509CRL) cf.generateCRL(getClass().getResourceAsStream("/certificates/smp-crl-test.crl")); + Mockito.doReturn(false).when(configurationService).forceCRLValidation(); + Mockito.doThrow(new SMPRuntimeException(ErrorCode.CERTIFICATE_ERROR, "Error occurred while downloading CRL:" + crlUrl, "")).when(crlVerifierService).downloadURL(crlUrl); + + String certSubject = "CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; + Calendar from = Calendar.getInstance(); + Calendar to = Calendar.getInstance(); + to.add(Calendar.DAY_OF_YEAR, 1); + from.add(Calendar.DAY_OF_YEAR, -2); + X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest( + revokedSerialFromList, certSubject, certSubject, from.getTime(), to.getTime(), Collections.singletonList(crlUrl)); + // add as trusted certificate + testInstance.addCertificate(UUID.randomUUID().toString(), certificate); + + + //then should be thrown CertificateRevokedException but is not + // when + testInstance.checkFullCertificateValidity(certificate); + } + + @Test + public void testCheckFullCertificateValidityOK() throws Exception { + // given + String crlUrl = "https://localhost/crl"; + String serialNotInList = "20011FF"; + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + X509CRL crl = (X509CRL) cf.generateCRL(getClass().getResourceAsStream("/certificates/smp-crl-test.crl")); + + Mockito.doReturn(crl).when(crlVerifierService).downloadCRL(crlUrl, true); + + String certSubject = "CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; + Calendar from = Calendar.getInstance(); + Calendar to = Calendar.getInstance(); + to.add(Calendar.DAY_OF_YEAR, 1); + from.add(Calendar.DAY_OF_YEAR, -2); + X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest( + serialNotInList, certSubject, certSubject, from.getTime(), to.getTime(), Collections.singletonList(crlUrl)); + // add as trusted certificate + testInstance.addCertificate(UUID.randomUUID().toString(), certificate); + + // when + testInstance.checkFullCertificateValidity(certificate); + + // then + //no errors should be thrown + } + + @Test + public void testCreateAliasForCert() throws Exception { + // given + String certSubject = "CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; + X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest(certSubject); + // when + String alias = testInstance.createAliasFromCert(certificate, null); + + // then + assertEquals("SMP Test", alias); + } + + + @Test + public void testCreateAliasFoMultiValuerCert() throws Exception { + // given + String certSubject = "GIVENNAME=John+SERIALNUMBER=1+CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; + X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest(certSubject); + // when + String alias = testInstance.createAliasFromCert(certificate, null); + + // then + 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.")); + } + + @Test + public void validateCertificateNotUsed() throws CertificateException { + String certId = "cn=test" + UUID.randomUUID().toString() + ",o=test,c=eu:123456"; + CertificateRO certificateRO = new CertificateRO(); + certificateRO.setCertificateId(certId); + // when + testInstance.validateCertificateNotUsed(certificateRO); + //then no error is thrown because + + } + +} \ No newline at end of file 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 c032ccba16040df21a68f067b5fceee6f580b106..04e1068ef45cc862d00d8ae29ab5e9e2b5588bee 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 @@ -1,465 +1,204 @@ package eu.europa.ec.edelivery.smp.services.ui; +import eu.europa.ec.edelivery.smp.data.dao.UserDao; import eu.europa.ec.edelivery.smp.data.model.DBUser; import eu.europa.ec.edelivery.smp.data.ui.CertificateRO; import eu.europa.ec.edelivery.smp.exceptions.CertificateNotTrustedException; -import eu.europa.ec.edelivery.smp.exceptions.ErrorCode; -import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; -import eu.europa.ec.edelivery.smp.services.AbstractServiceIntegrationTest; import eu.europa.ec.edelivery.smp.services.CRLVerifierService; 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; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.core.convert.ConversionService; 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.KeyStore; +import java.security.Security; import java.security.cert.*; -import java.util.*; +import java.util.Optional; +import java.util.UUID; +import java.util.regex.Pattern; import static org.junit.Assert.*; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.*; +public class UITruststoreServiceTest { + ConfigurationService configurationService = Mockito.mock(ConfigurationService.class); + CRLVerifierService crlVerifierService = Mockito.mock(CRLVerifierService.class); + ConversionService conversionService = Mockito.mock(ConversionService.class); + UserDao userDao = Mockito.mock(UserDao.class); -@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"; - - public static final String S_SUBJECT_TEST = "CN=SMP test,O=DIGIT,C=BE"; - - - Path resourceDirectory = Paths.get("src", "test", "resources", "truststore"); - Path targetDirectory = Paths.get("target", "truststore"); - - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Autowired - protected UITruststoreService testInstance; - - @Autowired - protected ConfigurationService configurationService; - - @Autowired - protected CRLVerifierService crlVerifierService; + UITruststoreService testInstance = spy(new UITruststoreService(configurationService, crlVerifierService, conversionService, userDao)); @Before - public void setup() throws IOException { - configurationService = Mockito.spy(configurationService); - crlVerifierService = Mockito.spy(crlVerifierService); - - ReflectionTestUtils.setField(testInstance, "crlVerifierService", crlVerifierService); - - ReflectionTestUtils.setField(testInstance, "configurationService", configurationService); - - File truststoreFile = new File(targetDirectory.toFile(), "smp-truststore.jks"); - Mockito.doReturn("test123").when(configurationService).getTruststoreCredentialToken(); - Mockito.doReturn(truststoreFile).when(configurationService).getTruststoreFile(); - Mockito.doReturn(targetDirectory.toFile()).when(configurationService).getConfigurationFolder(); - Mockito.doReturn(true).when(configurationService).forceCRLValidation(); - resetKeystore(); - - testInstance.refreshData(); - } - - public void resetKeystore() throws IOException { - FileUtils.deleteDirectory(targetDirectory.toFile()); - FileUtils.copyDirectory(resourceDirectory.toFile(), targetDirectory.toFile()); + public void setup() { + Security.insertProviderAt(new org.bouncycastle.jce.provider.BouncyCastleProvider(), 1); } @Test - public void testGetKeystoreEntriesList() { - List<String> lst = testInstance.getNormalizedTrustedList(); - assertEquals(2, lst.size()); - assertEquals(S_SUBJECT_PEPPOL, lst.get(0)); - assertEquals(S_SUBJECT_TEST, lst.get(1)); - } - - @Test - public void testSubjectValid() { - // given when - // then - assertTrue(testInstance.isSubjectOnTrustedList(S_SUBJECT_PEPPOL)); - } - - @Test - public void testSubjectValidExpanded() { - // given when - // then - assertTrue(testInstance.isSubjectOnTrustedList(S_SUBJECT_PEPPOL_EXPANDED)); - } - - @Test - public void testSubjectNotTrusted() { - // given when - // then - assertFalse(testInstance.isSubjectOnTrustedList(S_SUBJECT_PEPPOL_NOT_TRUSTED)); - } - - @Test - public void testAddCertificate() throws Exception { - // given - String certSubject = "CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; - String alias = UUID.randomUUID().toString(); - X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest(certSubject); - int iSize = testInstance.getNormalizedTrustedList().size(); - assertFalse(testInstance.isSubjectOnTrustedList(certSubject)); + public void validateCertificateNotUsedOk() throws CertificateException { + String certId = "cn=test" + UUID.randomUUID().toString() + ",o=test,c=eu:123456"; + CertificateRO certificateRO = new CertificateRO(); + certificateRO.setCertificateId(certId); + doReturn(Optional.empty()).when(userDao).findUserByCertificateId(ArgumentMatchers.anyString()); // when - testInstance.addCertificate(alias, certificate); - - // then - assertEquals(iSize + 1, testInstance.getNormalizedTrustedList().size()); - assertTrue(testInstance.isSubjectOnTrustedList(certSubject)); + testInstance.validateCertificateNotUsed(certificateRO); + //then no error is thrown because + ArgumentCaptor<String> certIdCaptor = ArgumentCaptor.forClass(String.class); + verify(userDao, times(1)) + .findUserByCertificateId(certIdCaptor.capture()); + assertEquals(certId, certIdCaptor.getValue()); } @Test - public void testAddCertificateRDN() throws Exception { - // given - String certSubject = "GIVENNAME=John+SERIALNUMBER=1+CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; - String alias = UUID.randomUUID().toString(); - X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest(certSubject); - String val = certificate.getSubjectX500Principal().getName(X500Principal.RFC2253); - int iSize = testInstance.getNormalizedTrustedList().size(); - assertFalse(testInstance.isSubjectOnTrustedList(certSubject)); + public void validateCertificateNotUsedIsUsed() { + String certId = "cn=test" + UUID.randomUUID().toString() + ",o=test,c=eu:123456"; + CertificateRO certificateRO = new CertificateRO(); + certificateRO.setCertificateId(certId); + doReturn(Optional.of(new DBUser())).when(userDao).findUserByCertificateId(ArgumentMatchers.anyString()); // when - testInstance.addCertificate(alias, certificate); - - // then - assertEquals(iSize + 1, testInstance.getNormalizedTrustedList().size()); - assertTrue(testInstance.isSubjectOnTrustedList(certSubject)); + CertificateException result = assertThrows(CertificateException.class, () -> testInstance.validateCertificateNotUsed(certificateRO)); + assertEquals("Certificate: [" + certId + "] is already used!", result.getMessage()); } @Test - public void testDeleteCertificate() throws Exception { - // given - List<CertificateRO> list = testInstance.getCertificateROEntriesList(); - int iSize = list.size(); - assertTrue(list.size() > 0); - CertificateRO certificateRO = list.get(0); - assertTrue(testInstance.isSubjectOnTrustedList(certificateRO.getSubject())); - // when - testInstance.deleteCertificate(certificateRO.getAlias()); + public void validateNewCertificateOk() throws CertificateException { + X509Certificate cert = Mockito.mock(X509Certificate.class); + CertificateRO certData = new CertificateRO(); + doNothing().when(testInstance).checkFullCertificateValidity(cert); + doNothing().when(testInstance).validateCertificateNotUsed(certData); - // then - assertEquals(iSize - 1, testInstance.getNormalizedTrustedList().size()); - assertFalse(testInstance.isSubjectOnTrustedList(certificateRO.getSubject())); - } + testInstance.validateNewCertificate(cert, certData); - @Test - public void testIsTruststoreChanged() throws Exception { - // given - String certSubject = "CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; - String alias = UUID.randomUUID().toString(); - X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest(certSubject); - testInstance.addCertificate(alias, certificate); - assertTrue(testInstance.isSubjectOnTrustedList(certSubject)); - // when rollback truststore - resetKeystore(); - // then it should detect file change and refresh certificates - assertFalse(testInstance.isSubjectOnTrustedList(certSubject)); + assertFalse(certData.isInvalid()); + assertNull(certData.getInvalidReason()); } - @Test - public void testGetCertificateDataPEMAndFullValidationExpired() throws IOException{ - // given + public void validateNewCertificateCertificateExpiredException() throws CertificateException { + X509Certificate cert = Mockito.mock(X509Certificate.class); + CertificateRO certData = new CertificateRO(); + doThrow(new CertificateExpiredException("Expired")).when(testInstance).checkFullCertificateValidity(cert); - byte[] buff = IOUtils.toByteArray(UIUserServiceIntegrationTest.class.getResourceAsStream("/truststore/SMPtest.crt")); + testInstance.validateNewCertificate(cert, certData); - // when - CertificateRO cer = testInstance.getCertificateData(buff, true); - - //then - assertEquals("CN=SMP test,O=DIGIT,C=BE:0000000000000003", cer.getCertificateId()); - assertEquals("CN=Intermediate CA,O=DIGIT,C=BE", cer.getIssuer()); - assertEquals("1.2.840.113549.1.9.1=#160c736d7040746573742e636f6d,CN=SMP test,O=DIGIT,C=BE", cer.getSubject()); - assertEquals("3", cer.getSerialNumber()); - assertNotNull(cer.getValidFrom()); - assertNotNull(cer.getValidTo()); - assertTrue(cer.getValidFrom().before(cer.getValidTo())); - assertEquals("Certificate is expired!",cer.getInvalidReason()); + assertTrue(certData.isInvalid()); + assertEquals("Certificate is expired!", certData.getInvalidReason()); } @Test - public void testGetCertificateDataPEMWithHeader() throws IOException, CertificateException { - // given - byte[] buff = IOUtils.toByteArray(UIUserServiceIntegrationTest.class.getResourceAsStream("/truststore/pem-with-header.crt")); + public void validateNewCertificateCertificateCertificateNotYetValidException() throws CertificateException { + X509Certificate cert = Mockito.mock(X509Certificate.class); + CertificateRO certData = new CertificateRO(); + doThrow(new CertificateNotYetValidException("Error")).when(testInstance).checkFullCertificateValidity(cert); - // when - CertificateRO cer = testInstance.getCertificateData(buff); - - //then - assertEquals("CN=alice,O=www.freelan.org,C=FR:0000000000000001", cer.getCertificateId()); - assertEquals("1.2.840.113549.1.9.1=#1613636f6e7461637440667265656c616e2e6f7267,CN=Freelan Sample Certificate Authority,OU=freelan,O=www.freelan.org,L=Strasbourg,ST=Alsace,C=FR", cer.getIssuer()); - assertEquals("1.2.840.113549.1.9.1=#1613636f6e7461637440667265656c616e2e6f7267,CN=alice,OU=freelan,O=www.freelan.org,ST=Alsace,C=FR", cer.getSubject()); - assertEquals("1", cer.getSerialNumber()); - assertNotNull(cer.getValidFrom()); - assertNotNull(cer.getValidTo()); - assertTrue(cer.getValidFrom().before(cer.getValidTo())); - } + testInstance.validateNewCertificate(cert, certData); - @Test - public void testGetCertificateDataSMime() throws IOException, CertificateException { - // given - byte[] buff = IOUtils.toByteArray(UIUserServiceIntegrationTest.class.getResourceAsStream("/certificates/cert-smime.pem")); - - // when - CertificateRO cer = testInstance.getCertificateData(buff); - - //then - assertEquals("CN=edelivery_sml,O=European Commission,C=BE:3cfe6b37e4702512c01e71f9b9175464", cer.getCertificateId()); - assertEquals("CN=PEPPOL SERVICE METADATA PUBLISHER TEST CA - G2,OU=FOR TEST ONLY,O=OpenPEPPOL AISBL,C=BE", cer.getIssuer()); - assertEquals("C=BE,O=European Commission,OU=PEPPOL TEST SMP,CN=edelivery_sml", cer.getSubject()); - assertEquals("3cfe6b37e4702512c01e71f9b9175464", cer.getSerialNumber()); - assertNotNull(cer.getValidFrom()); - assertNotNull(cer.getValidTo()); - assertTrue(cer.getValidFrom().before(cer.getValidTo())); + assertTrue(certData.isInvalid()); + assertEquals("Certificate is not yet valid!", certData.getInvalidReason()); } @Test - public void testGetCertificateDataDER() throws IOException, CertificateException { - // given - byte[] buff = IOUtils.toByteArray(UIUserServiceIntegrationTest.class.getResourceAsStream("/truststore/NewPeppolAP.crt")); - - // when - CertificateRO cer = testInstance.getCertificateData(buff); - - //then - assertEquals("CN=POP000004,O=European Commission,C=BE:474980c51478cf62761667461aef5e8e", cer.getCertificateId()); - assertEquals("CN=PEPPOL ACCESS POINT TEST CA - G2,OU=FOR TEST ONLY,O=OpenPEPPOL AISBL,C=BE", cer.getIssuer()); - assertEquals("C=BE,O=European Commission,OU=PEPPOL TEST AP,CN=POP000004", cer.getSubject()); - assertEquals("474980c51478cf62761667461aef5e8e", cer.getSerialNumber()); - assertNotNull(cer.getValidFrom()); - assertNotNull(cer.getValidTo()); - assertTrue(cer.getValidFrom().before(cer.getValidTo())); - } + public void validateNewCertificateCertificateCertificateRevokedException() throws CertificateException { + X509Certificate cert = Mockito.mock(X509Certificate.class); + CertificateRO certData = new CertificateRO(); + doThrow(Mockito.mock(CertificateRevokedException.class)).when(testInstance).checkFullCertificateValidity(cert); - @Test - public void testCheckFullCertificateValidityNotYetValid() throws Exception { - // given - String certSubject = "CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; - Calendar from = Calendar.getInstance(); - Calendar to = Calendar.getInstance(); - to.add(Calendar.DAY_OF_YEAR, 2); - from.add(Calendar.DAY_OF_YEAR, 1); - X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest( - "10af", certSubject, certSubject, from.getTime(), to.getTime(), Collections.emptyList()); - - //then - expectedEx.expect(CertificateNotYetValidException.class); - // when - testInstance.checkFullCertificateValidity(certificate); - } + testInstance.validateNewCertificate(cert, certData); - @Test - public void testCheckFullCertificateValidityExpired() throws Exception { - // given - String certSubject = "CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; - Calendar from = Calendar.getInstance(); - Calendar to = Calendar.getInstance(); - to.add(Calendar.DAY_OF_YEAR, -1); - from.add(Calendar.DAY_OF_YEAR, -2); - X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest( - "10af", certSubject, certSubject, from.getTime(), to.getTime(), Collections.emptyList()); - - //then - expectedEx.expect(CertificateExpiredException.class); - // when - testInstance.checkFullCertificateValidity(certificate); + assertTrue(certData.isInvalid()); + assertEquals("Certificate is revoked!", certData.getInvalidReason()); } @Test - public void testCheckFullCertificateNotTrusted() throws Exception { - // given - String crlUrl = "https://localhost/crl"; - String revokedSerialFromList = "0011"; - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - X509CRL crl = (X509CRL) cf.generateCRL(getClass().getResourceAsStream("/certificates/smp-crl-test.crl")); - - Mockito.doReturn(crl).when(crlVerifierService).getCRLByURL(crlUrl); - - Calendar from = Calendar.getInstance(); - Calendar to = Calendar.getInstance(); - to.add(Calendar.DAY_OF_YEAR, 1); - from.add(Calendar.DAY_OF_YEAR, -2); - X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest( - revokedSerialFromList, S_SUBJECT_PEPPOL_NOT_TRUSTED, S_SUBJECT_PEPPOL_NOT_TRUSTED, from.getTime(), to.getTime(), Collections.singletonList(crlUrl)); - //then - expectedEx.expect(CertificateNotTrustedException.class); - // when - testInstance.checkFullCertificateValidity(certificate); - } - + public void validateNewCertificateCertificateCertificateNotTrustedException() throws CertificateException { + X509Certificate cert = Mockito.mock(X509Certificate.class); + CertificateRO certData = new CertificateRO(); + doThrow(Mockito.mock(CertificateNotTrustedException.class)).when(testInstance).checkFullCertificateValidity(cert); - @Test - public void testCheckFullCertificateValidityRevoked() throws Exception { - // given - String crlUrl = "https://localhost/crl"; - String revokedSerialFromList = "0011"; - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - X509CRL crl = (X509CRL) cf.generateCRL(getClass().getResourceAsStream("/certificates/smp-crl-test.crl")); - - Mockito.doReturn(crl).when(crlVerifierService).downloadCRL(ArgumentMatchers.eq(crlUrl), ArgumentMatchers.anyBoolean()); - - String certSubject = "CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; - Calendar from = Calendar.getInstance(); - Calendar to = Calendar.getInstance(); - to.add(Calendar.DAY_OF_YEAR, 1); - from.add(Calendar.DAY_OF_YEAR, -2); - X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest( - revokedSerialFromList, certSubject, certSubject, from.getTime(), to.getTime(), Collections.singletonList(crlUrl)); - // add as trusted certificate - testInstance.addCertificate(UUID.randomUUID().toString(), certificate); - - - //then - expectedEx.expect(CertificateRevokedException.class); - // when - testInstance.checkFullCertificateValidity(certificate); - } + testInstance.validateNewCertificate(cert, certData); - @Test - public void testCheckFullCertificateValidityNotForceCRL() throws Exception { - // given - String crlUrl = "https://localhost/crl"; - String revokedSerialFromList = "0011"; - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - X509CRL crl = (X509CRL) cf.generateCRL(getClass().getResourceAsStream("/certificates/smp-crl-test.crl")); - Mockito.doReturn(false).when(configurationService).forceCRLValidation(); - Mockito.doThrow(new SMPRuntimeException(ErrorCode.CERTIFICATE_ERROR, "Error occurred while downloading CRL:" + crlUrl, "")).when(crlVerifierService).downloadURL(crlUrl); - - String certSubject = "CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; - Calendar from = Calendar.getInstance(); - Calendar to = Calendar.getInstance(); - to.add(Calendar.DAY_OF_YEAR, 1); - from.add(Calendar.DAY_OF_YEAR, -2); - X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest( - revokedSerialFromList, certSubject, certSubject, from.getTime(), to.getTime(), Collections.singletonList(crlUrl)); - // add as trusted certificate - testInstance.addCertificate(UUID.randomUUID().toString(), certificate); - - - //then should be thrown CertificateRevokedException but is not - // when - testInstance.checkFullCertificateValidity(certificate); + assertTrue(certData.isInvalid()); + assertEquals("Certificate is not trusted!", certData.getInvalidReason()); } @Test - public void testCheckFullCertificateValidityOK() throws Exception { - // given - String crlUrl = "https://localhost/crl"; - String serialNotInList = "20011FF"; - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - X509CRL crl = (X509CRL) cf.generateCRL(getClass().getResourceAsStream("/certificates/smp-crl-test.crl")); - - Mockito.doReturn(crl).when(crlVerifierService).downloadCRL(crlUrl, true); - - String certSubject = "CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; - Calendar from = Calendar.getInstance(); - Calendar to = Calendar.getInstance(); - to.add(Calendar.DAY_OF_YEAR, 1); - from.add(Calendar.DAY_OF_YEAR, -2); - X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest( - serialNotInList, certSubject, certSubject, from.getTime(), to.getTime(), Collections.singletonList(crlUrl)); - // add as trusted certificate - testInstance.addCertificate(UUID.randomUUID().toString(), certificate); + public void validateNewCertificateCertPathValidatorException() throws CertificateException { + X509Certificate cert = Mockito.mock(X509Certificate.class); + CertificateRO certData = new CertificateRO(); + doThrow(new CertificateException(Mockito.mock(CertPathValidatorException.class))).when(testInstance).checkFullCertificateValidity(cert); - // when - testInstance.checkFullCertificateValidity(certificate); + testInstance.validateNewCertificate(cert, certData); - // then - //no errors should be thrown + assertTrue(certData.isInvalid()); + assertEquals("Certificate is not trusted! Invalid certificate policy path!", certData.getInvalidReason()); } @Test - public void testCreateAliasForCert() throws Exception { - // given - String certSubject = "CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; - X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest(certSubject); - // when - String alias = testInstance.createAliasFromCert(certificate, null); + public void validateNewCertificateCertificateException() throws CertificateException { + String errorMessage = "Error Message"; + X509Certificate cert = Mockito.mock(X509Certificate.class); + CertificateRO certData = new CertificateRO(); + doThrow(new CertificateException(errorMessage)).when(testInstance).checkFullCertificateValidity(cert); - // then - assertEquals("SMP Test", alias); + testInstance.validateNewCertificate(cert, certData); + + assertTrue(certData.isInvalid()); + assertEquals(errorMessage, certData.getInvalidReason()); } @Test - public void testCreateAliasFoMultiValuerCert() throws Exception { - // given - String certSubject = "GIVENNAME=John+SERIALNUMBER=1+CN=SMP Test,OU=eDelivery,O=DIGITAL,C=BE"; - X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest(certSubject); - // when - String alias = testInstance.createAliasFromCert(certificate, null); - - // then - assertEquals("SMP Test", alias); + public void validateCertificateSubjectExpressionLegacyIfNullSkip() throws CertificateException { + X509Certificate cert = Mockito.mock(X509Certificate.class); + doReturn(null).when(configurationService).getCertificateSubjectRegularExpression(); + testInstance.validateCertificateSubjectExpressionLegacy(cert); } @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); - } + public void validateCertificateSubjectExpressionLegacyValidatedNotMatch() throws Exception { + String regularExpression = ".*CN=SomethingNotExists.*"; + String subject = "CN=Something,O=test,C=EU"; + X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest(subject); + doReturn(Pattern.compile(regularExpression)).when(configurationService).getCertificateSubjectRegularExpression(); + CertificateException resultException = assertThrows(CertificateException.class, () -> testInstance.validateCertificateSubjectExpressionLegacy(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:")); + assertEquals("Certificate subject [" + +certificate.getSubjectX500Principal().getName(X500Principal.RFC2253) + +"] does not match the regular expression configured ["+regularExpression+"]", + resultException.getMessage()); } @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.")); + public void validateCertificateSubjectExpressionLegacyValidatedMatch() throws Exception { + String regularExpression = ".*CN=Something.*"; + String subject = "CN=Something,O=test,C=EU"; + X509Certificate certificate = X509CertificateTestUtils.createX509CertificateForTest(subject); + doReturn(Pattern.compile(regularExpression)).when(configurationService).getCertificateSubjectRegularExpression(); + + testInstance.validateCertificateSubjectExpressionLegacy(certificate); + // no error is thrown } @Test - public void validateCertificateNotUsed() throws CertificateException { - String certId = "cn=test"+UUID.randomUUID().toString()+",o=test,c=eu:123456"; - CertificateRO certificateRO = new CertificateRO(); - certificateRO.setCertificateId(certId); - // when - testInstance.validateCertificateNotUsed(certificateRO); - //then no error is thrown + public void loadTruststoreDoNotThrowError(){ + // test for null file + KeyStore result = testInstance.loadTruststore(null); + assertNull(result); + // test for file not exists + result = testInstance.loadTruststore(new File(UUID.randomUUID().toString())); + assertNull(result); + // test for file credentials not exist + Path resourceDirectory = Paths.get("src", "test", "resources", "truststore","smp-truststore.jks"); + assertTrue(resourceDirectory.toFile().exists()); + doReturn(null).when(configurationService).getTruststoreCredentialToken(); + result = testInstance.loadTruststore(resourceDirectory.toFile()); + assertNull(result); } - } \ No newline at end of file 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 1f358c73f183f013a7a322f267667d9a3e5ef117..a9bb61b5ed0c6cae2b94c52aed84954cd674f34f 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 @@ -27,7 +27,6 @@ import org.springframework.security.crypto.bcrypt.BCrypt; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.springframework.stereotype.Component; -import java.security.KeyStore; import java.security.cert.CertificateException; import java.security.cert.CertificateRevokedException; import java.security.cert.X509Certificate;