Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions registration/registration-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>io.mosip.registration.controller.Initialization</mainClass>
</manifest>
</archive>
</configuration>
Expand Down Expand Up @@ -115,9 +114,6 @@
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.3</version>
<configuration>
<mainClass>io.mosip.registration.controller.Initialization</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import io.mosip.registration.context.SessionContext;
import io.mosip.registration.controller.ClientApplication;
import io.mosip.registration.controller.GenericController;
import io.mosip.registration.controller.Initialization;
import io.mosip.registration.dto.RegistrationDTO;
import io.mosip.registration.dto.mastersync.GenericDto;
import io.mosip.registration.dto.schema.UiFieldDTO;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import io.mosip.registration.dto.mastersync.GenericDto;
import io.mosip.registration.constants.RegistrationConstants;
import io.mosip.registration.controller.FXUtils;
import io.mosip.registration.controller.Initialization;
import io.mosip.registration.dto.schema.UiFieldDTO;
import io.mosip.registration.service.sync.MasterSyncService;
import io.mosip.registration.util.control.FxControl;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import io.mosip.registration.constants.Components;
import io.mosip.registration.constants.RegistrationConstants;
import io.mosip.registration.context.SessionContext;
import io.mosip.registration.controller.Initialization;
import io.mosip.registration.dto.schema.UiFieldDTO;
import io.mosip.registration.util.control.FxControl;
import javafx.scene.Node;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import io.mosip.registration.config.AppConfig;
import io.mosip.registration.constants.RegistrationConstants;
import io.mosip.registration.controller.FXUtils;
import io.mosip.registration.controller.Initialization;
import io.mosip.registration.controller.reg.DateValidation;
import io.mosip.registration.dto.schema.UiFieldDTO;
import io.mosip.registration.util.control.FxControl;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import io.mosip.registration.constants.RegistrationConstants;
import io.mosip.registration.controller.FXUtils;
import io.mosip.registration.controller.GenericController;
import io.mosip.registration.controller.Initialization;
import io.mosip.registration.controller.reg.Validations;
import io.mosip.registration.dto.schema.UiFieldDTO;
import io.mosip.registration.entity.Location;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ public class ClientIntegrityValidator {
private static final String PROPERTIES_FILE = "props/mosip-application.properties";
private static final String libFolder = "lib";
private static final String certPath = "provider.pem";
private static final String manifestFile = "MANIFEST.MF";
// Per-jar integrity hashes live in lib/MANIFEST.MF (bundled inside lib.zip), not the
// root orchestration MANIFEST.MF. See ClientSetupValidator for the corresponding split.
private static final String manifestFile = libFolder + File.separator + "MANIFEST.MF";


public static void verifyClientIntegrity() {
Expand All @@ -41,23 +43,26 @@ public static void verifyClientIntegrity() {
X509Certificate trustedCertificate = getCertificate();
Manifest localManifest = getLocalManifest();

if (localManifest != null) {
Map<String, Attributes> localAttributes = localManifest.getEntries();
for (Map.Entry<String, Attributes> entry : localAttributes.entrySet()) {
if(entry.getKey().toLowerCase().startsWith("registration-services") ||
entry.getKey().toLowerCase().startsWith("registration-client") ) {
File file = new File(libFolder + File.separator + entry.getKey());
if(trustedCertificate != null) {
verifyIntegrity(trustedCertificate, new JarFile(file));
logger.info("Integrity check passed -> {}", entry.getKey());
}
else {
logger.info("As provider.cer is not found, invoking verify with JarFile class : {}", entry.getKey());
try(JarFile jarFile = new JarFile(file, true)){
logger.info("Integrity check passed -> {}", entry.getKey());
}
}
// Fail closed: a missing lib manifest must abort startup, not silently skip the
// per-jar signature verification (which would disable client integrity enforcement).
if (localManifest == null) {
throw new SecurityException(manifestFile + " not found, cannot verify client integrity");
}

Map<String, Attributes> localAttributes = localManifest.getEntries();
for (Map.Entry<String, Attributes> entry : localAttributes.entrySet()) {
if(entry.getKey().toLowerCase().startsWith("registration-services") ||
entry.getKey().toLowerCase().startsWith("registration-client") ) {
File file = new File(libFolder + File.separator + entry.getKey());
// Fail closed: without the trusted provider certificate the jar signature
// cannot be pinned to the expected signer, so integrity cannot be enforced.
// (The old fallback opened the jar but never read its entries, so the lazy
// signature/digest check never ran and it logged a false success.)
if (trustedCertificate == null) {
throw new SecurityException("Trusted provider certificate is not found");
}
verifyIntegrity(trustedCertificate, new JarFile(file));
logger.info("Integrity check passed -> {}", entry.getKey());
}
}
} catch (Throwable t) {
Expand Down Expand Up @@ -195,13 +200,13 @@ public static X509Certificate getCertificate() {
}

private static Manifest getLocalManifest() {
try {
File localManifestFile = new File(manifestFile);
if (localManifestFile.exists()) {
return new Manifest(new FileInputStream(localManifestFile));
File localManifestFile = new File(manifestFile);
if (localManifestFile.exists()) {
try (FileInputStream fis = new FileInputStream(localManifestFile)) {
return new Manifest(fis);
} catch (IOException e) {
logger.error("Failed to load local manifest file", e);
}
} catch (IOException e) {
logger.error("Failed to load local manifest file", e);
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,15 @@ public class ClientSetupValidator {
private static final String manifestFile = "MANIFEST.MF";
private static final String libFolder = "lib";
private static final String SLASH = "/";
// Root MANIFEST.MF carries only version/orchestration info; the per-jar integrity hashes
// now live in lib/MANIFEST.MF (bundled inside lib.zip). Version compare/rewrite stays on
// the root manifest, the unknown-jar + checksum validation runs against the lib manifest.
private static final String libManifestFile = libFolder + File.separator + manifestFile;

private static String serverRegClientURL = null;
private static String latestVersion = null;
private static Manifest localManifest = null;
private static Manifest libManifest = null;
private static Manifest serverManifest = null;

private static String environment = null;
Expand All @@ -41,6 +46,7 @@ public ClientSetupValidator() throws RegBaseCheckedException {
latestVersion = properties.getProperty("mosip.reg.version");
environment = properties.getProperty("environment");
setLocalManifest();
setLibManifest();

Objects.requireNonNull(serverRegClientURL, "'mosip.reg.client.url' IS NOT SET");
Objects.requireNonNull(latestVersion, "'mosip.reg.version' IS NOT SET");
Expand All @@ -51,6 +57,7 @@ public ClientSetupValidator() throws RegBaseCheckedException {
}

Objects.requireNonNull(localManifest, manifestFile + " - Not found");
Objects.requireNonNull(libManifest, libManifestFile + " - Not found");
//SoftwareUpdateUtil.deleteUnknownJars(localManifest);

} catch (RegBaseCheckedException e) {
Expand Down Expand Up @@ -92,14 +99,14 @@ public void validateBuildSetup() throws RegBaseCheckedException {

SoftwareUpdateUtil.clearTempDirectory();

if(SoftwareUpdateUtil.deleteUnknownJars(localManifest)) {
if(SoftwareUpdateUtil.deleteUnknownJars(libManifest)) {
logger.info("Found unknown jars in the classpath !");
unknown_jars_found = true;
validation_failed = true;
}

Map<String, Attributes> localAttributes = localManifest.getEntries();
for (Map.Entry<String, Attributes> entry : localAttributes.entrySet()) {
Map<String, Attributes> libAttributes = libManifest.getEntries();
for (Map.Entry<String, Attributes> entry : libAttributes.entrySet()) {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
File file = new File(libFolder + File.separator + entry.getKey());
String url = serverRegClientURL + latestVersion + SLASH + libFolder + SLASH + entry.getKey();
if(!file.exists()) {
Expand Down Expand Up @@ -138,15 +145,24 @@ public boolean isUnknown_jars_found() {
}

private void setLocalManifest() throws RegBaseCheckedException {
try {
File localManifestFile = new File(manifestFile);
if (localManifestFile.exists()) {
localManifest = new Manifest(new FileInputStream(localManifestFile));
localManifest = loadManifest(manifestFile, "Local", "REG-BUILD-003");
}

private void setLibManifest() throws RegBaseCheckedException {
libManifest = loadManifest(libManifestFile, "Lib", "REG-BUILD-006");
}

private Manifest loadManifest(String path, String label, String errorCode) throws RegBaseCheckedException {
File file = new File(path);
if (file.exists()) {
try (FileInputStream fis = new FileInputStream(file)) {
return new Manifest(fis);
} catch (IOException e) {
logger.error("Failed to load {} manifest file", label.toLowerCase(), e);
throw new RegBaseCheckedException(errorCode, label + " Manifest not found", e);
}
} catch (IOException e) {
logger.error("Failed to load local manifest file", e);
throw new RegBaseCheckedException("REG-BUILD-003", "Local Manifest not found");
}
return null;
}

private void setServerManifest() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public class SoftwareUpdateUtil {
private static final String libFolder = "lib/";
private static final String UNKNOWN_JARS = ".UNKNOWN_JARS";
private static final String TEMP_DIRECTORY = ".TEMP";
private static final String MANIFEST_FILE_NAME = "MANIFEST.MF";
private static final String MANIFEST_SIG_FILE_NAME = MANIFEST_FILE_NAME + ".sig";

protected static boolean deleteUnknownJars(Manifest localManifest) throws IOException {
StringBuilder builder = new StringBuilder();
Expand All @@ -36,6 +38,12 @@ protected static boolean deleteUnknownJars(Manifest localManifest) throws IOExce
File[] libraries = dir.listFiles();
Map<String, Attributes> entries = localManifest.getEntries();
for (File file : libraries) {
// lib/MANIFEST.MF and its detached signature live inside lib/ but are created after
// the manifest is generated, so they are not self-listed entries. Preserve them
// instead of treating them as unknown jars (else the lib manifest deletes itself).
if(MANIFEST_FILE_NAME.equals(file.getName()) || MANIFEST_SIG_FILE_NAME.equals(file.getName())) {
continue;
}
if(!entries.containsKey(file.getName())) {
LOGGER.error("Unknown file found {}", file.getName());
deleteFile(file.getCanonicalPath());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@
import static org.mockito.Mockito.when;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.Map.Entry;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand Down Expand Up @@ -44,39 +47,66 @@ public class ClientIntegrityValidatorTest {
@InjectMocks
private ClientIntegrityValidator clientIntegrityValidator;

// Backup of a pre-existing working-dir lib/MANIFEST.MF (e.g. left by another test in the
// same fork), restored in teardown so these tests don't depend on external filesystem state.
private File libManifestBackup;

@Before
public void initialize() throws Exception {
PowerMockito.mockStatic(ApplicationContext.class, FileUtils.class);
PowerMockito.mockStatic(HMACUtils2.class);
// Start every test from a known-absent lib/MANIFEST.MF; back up any pre-existing one.
// Fail fast on any file-op failure so a silent error can't leave state inconsistent.
File libManifest = new File("lib", "MANIFEST.MF");
if (libManifest.exists()) {
libManifestBackup = new File("lib", "MANIFEST.MF.testbak");
if (libManifestBackup.exists() && !libManifestBackup.delete()) {
throw new IOException("Could not remove stale backup " + libManifestBackup);
}
if (!libManifest.renameTo(libManifestBackup)) {
throw new IOException("Could not back up " + libManifest);
}
} else {
libManifestBackup = null;
}
}

@SuppressWarnings({ "rawtypes", "unused" })
@Test
@After
public void restoreLibManifest() throws IOException {
File libDir = new File("lib");
File libManifest = new File(libDir, "MANIFEST.MF");
if (libManifest.exists() && !libManifest.delete()) {
throw new IOException("Could not delete test " + libManifest);
}
if (libManifestBackup != null && libManifestBackup.exists()
&& !libManifestBackup.renameTo(libManifest)) {
throw new IOException("Could not restore backup to " + libManifest);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
libDir.delete(); // removes lib/ only if empty (i.e. created by a test)
}

// In a non-LOCAL environment (test props use environment=TEST) with no lib/MANIFEST.MF on
// disk, integrity verification must fail closed with a SecurityException rather than skip.
@Test(expected = SecurityException.class)
public void verifyClientIntegrityTest() throws RegBaseCheckedException {
ClientIntegrityValidator.verifyClientIntegrity();

Manifest localManifest = Mockito.mock(Manifest.class);
// when(localManifest.getEntries()).thenReturn(null, null);

Map <String, Attributes> localAttributes = localManifest.getEntries();

localAttributes.entrySet();

java.util.Iterator<Entry<String, Attributes>> it = localAttributes.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry1 = (Map.Entry)it.next();

final String libFolder = "lib";
@SuppressWarnings("unused")
File file = new File(libFolder + File.separator + entry1.getKey());

@SuppressWarnings("static-access")
X509Certificate trustedCertificate = clientIntegrityValidator.getCertificate();


}

// Counterpart to verifyClientIntegrityTest: with lib/MANIFEST.MF present (and no
// registration-* entries to verify), integrity verification proceeds without throwing.
// Uses java.io (not java.nio) on purpose: under PowerMock + Java 21 the module system
// blocks the reflective access nio Path operations need (sun.nio.fs not opened).
// Setup/teardown (@Before/@After) own the lib/MANIFEST.MF backup + cleanup.
@Test
public void verifyClientIntegrityManifestPresentTest() throws Exception {
File libDir = new File("lib");
libDir.mkdirs();
try (FileOutputStream fos = new FileOutputStream(new File(libDir, "MANIFEST.MF"))) {
fos.write("Manifest-Version: 1.0\r\n\r\nName: logback.xml\r\nContent-Type: testhash\r\n\r\n"
.getBytes(StandardCharsets.UTF_8));
}


// Non-registration entry -> per-jar verification loop is a no-op -> returns normally.
ClientIntegrityValidator.verifyClientIntegrity();
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ public void initialize() throws Exception {
private void resetStaticFields() {
ReflectionTestUtils.setField(ClientSetupValidator.class, "environment", null);
ReflectionTestUtils.setField(ClientSetupValidator.class, "localManifest", null);
ReflectionTestUtils.setField(ClientSetupValidator.class, "libManifest", null);
ReflectionTestUtils.setField(ClientSetupValidator.class, "serverManifest", null);
ReflectionTestUtils.setField(ClientSetupValidator.class, "serverRegClientURL", null);
ReflectionTestUtils.setField(ClientSetupValidator.class, "latestVersion", null);
Expand Down Expand Up @@ -151,6 +152,7 @@ public void deleteUnknownJarsTest() throws RegBaseCheckedException, IOException,
Manifest manifest = getManifest();
ReflectionTestUtils.setField(clientSetupValidator, "serverManifest", manifest);
ReflectionTestUtils.setField(clientSetupValidator, "localManifest", manifest);
ReflectionTestUtils.setField(clientSetupValidator, "libManifest", manifest);
Mockito.when(HMACUtils2.digestAsPlainText(Mockito.any())).thenReturn("testing");
Mockito.when(ApplicationContext.getIntValueFromApplicationMap(CONNECTION_TIMEOUT)).thenReturn(null);
Mockito.when(ApplicationContext.getIntValueFromApplicationMap(READ_TIMEOUT)).thenReturn(null);
Expand All @@ -168,6 +170,7 @@ public void deleteUnknownJarsExceptionTest() throws RegBaseCheckedException, IOE
ReflectionTestUtils.setField(manifest, "entries", entries);
ReflectionTestUtils.setField(clientSetupValidator, "serverManifest", manifest);
ReflectionTestUtils.setField(clientSetupValidator, "localManifest", manifest);
ReflectionTestUtils.setField(clientSetupValidator, "libManifest", manifest);
Mockito.when(ApplicationContext.getIntValueFromApplicationMap(CONNECTION_TIMEOUT)).thenReturn(null);
Mockito.when(ApplicationContext.getIntValueFromApplicationMap(READ_TIMEOUT)).thenReturn(null);
clientSetupValidator.validateBuildSetup();
Expand All @@ -184,6 +187,7 @@ public void deleteUnknownJarsValidateCheckSumFalseTest() throws RegBaseCheckedEx
ReflectionTestUtils.setField(manifest, "entries", entries);
ReflectionTestUtils.setField(clientSetupValidator, "serverManifest", manifest);
ReflectionTestUtils.setField(clientSetupValidator, "localManifest", manifest);
ReflectionTestUtils.setField(clientSetupValidator, "libManifest", manifest);
Mockito.when(HMACUtils2.digestAsPlainText(Mockito.any())).thenReturn("testing");
Mockito.when(ApplicationContext.getIntValueFromApplicationMap(CONNECTION_TIMEOUT)).thenReturn(null);
Mockito.when(ApplicationContext.getIntValueFromApplicationMap(READ_TIMEOUT)).thenReturn(null);
Expand Down
Loading
Loading