Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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,22 +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));
// 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());
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());
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
}
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());
}
}
}
}
}
Expand Down Expand Up @@ -195,13 +201,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");
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
}
} 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,8 +3,10 @@
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;
Expand Down Expand Up @@ -50,33 +52,33 @@ public void initialize() throws Exception {
PowerMockito.mockStatic(HMACUtils2.class);
}

@SuppressWarnings({ "rawtypes", "unused" })
@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).
@Test
public void verifyClientIntegrityManifestPresentTest() throws Exception {
File libDir = new File("lib");
File libManifest = new File(libDir, "MANIFEST.MF");
libDir.mkdirs();
try (FileOutputStream fos = new FileOutputStream(libManifest)) {
fos.write("Manifest-Version: 1.0\r\n\r\nName: logback.xml\r\nContent-Type: testhash\r\n\r\n"
.getBytes(StandardCharsets.UTF_8));
}
try {
// Non-registration entry -> per-jar verification loop is a no-op -> returns normally.
ClientIntegrityValidator.verifyClientIntegrity();
} finally {
libManifest.delete();
libDir.delete(); // removes lib/ only if empty
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
}


}

@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
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ public void mainTest() throws Exception {
Path.of("lib").toFile());
FileUtils.copyFile(Path.of(".","src", "test", "resources", "manifesttest", MANIFEST_FILE_NAME).toFile(),
Path.of(MANIFEST_FILE_NAME).toFile());
// Dual-manifest (T6): per-file hashes now live in lib/MANIFEST.MF. The manifest
// ManifestCreator generated over the lib folder is that lib manifest, so place it
// there too — ClientSetupValidator reads it for deleteUnknownJars + the checksum loop.
FileUtils.copyFile(Path.of(".","src", "test", "resources", "manifesttest", MANIFEST_FILE_NAME).toFile(),
Path.of("lib", MANIFEST_FILE_NAME).toFile());

ClientSetupValidator clientSetupValidator = new ClientSetupValidator();
clientSetupValidator.validateBuildSetup();
Expand Down
Loading