Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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,57 @@ 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.
File libManifest = new File("lib", "MANIFEST.MF");
if (libManifest.exists()) {
libManifestBackup = new File("lib", "MANIFEST.MF.testbak");
libManifestBackup.delete();
libManifest.renameTo(libManifestBackup);
} else {
libManifestBackup = null;
}
}

@SuppressWarnings({ "rawtypes", "unused" })
@Test
@After
public void restoreLibManifest() {
File libDir = new File("lib");
new File(libDir, "MANIFEST.MF").delete();
if (libManifestBackup != null && libManifestBackup.exists()) {
libManifestBackup.renameTo(new File(libDir, "MANIFEST.MF"));
}
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