I have a webserver that serves OSGI bundles as self-signed jar files. These are downloaded and installed using the OSGI API on my devices. I have enabled security in knopflerfish OSGI and pointed it to a keystore with my public key. I want to verify that the code I download can not be tampered with in a MITM attack or otherwise.
To test this I tried to slightly change and recompile one of my bundles that I had previously signed and verified, unpacked the old, signed jar and the newly compiled jar using 7zip, and copied over the contents of META-INF folder, overwriting the still unsigned MANIFEST.MF and providing .SF and .RSA files. When I tried to download and install this jar file I indeed got an error:
[stderr] org.osgi.framework.BundleException: Failed to install bundle: java.io.IOException: MANIFEST.MF must be first in archive when using signatures.
[stderr] at org.knopflerfish.framework.Bundles.install0(Bundles.java:178)
[stderr] at org.knopflerfish.framework.SecurePermissionOps$14.run(SecurePermissionOps.java:727)
[stderr] at org.knopflerfish.framework.SecurePermissionOps$14.run(SecurePermissionOps.java:723)
[stderr] at java.security.AccessController.doPrivileged(Native Method)
[stderr] at org.knopflerfish.framework.SecurePermissionOps.callInstall0(SecurePermissionOps.java:722)
[stderr] at org.knopflerfish.framework.Bundles.install(Bundles.java:118)
[stderr] at org.knopflerfish.framework.BundleContextImpl.installBundle(BundleContextImpl.java:109)
[stderr] at no.aventi.sam.Activator.handleEvent(Activator.java:190)
[stderr] at org.knopflerfish.bundle.event.TrackedEventHandler.handleEventSubjectToFilter(TrackedEventHandler.java:71)
[stderr] at org.knopflerfish.bundle.event.InternalAdminEvent.deliverToHandles(InternalAdminEvent.java:153)
[stderr] at org.knopflerfish.bundle.event.InternalAdminEvent.deliver(InternalAdminEvent.java:114)
[stderr] at org.knopflerfish.bundle.event.QueueHandler.run(QueueHandler.java:120)
[stderr] Caused by: java.io.IOException: MANIFEST.MF must be first in archive when using signatures.
[stderr] at org.knopflerfish.framework.bundlestorage.file.Archive.downloadArchive(Archive.java:271)
[stderr] at org.knopflerfish.framework.bundlestorage.file.BundleArchiveImpl.<init>(BundleArchiveImpl.java:133)
[stderr] at org.knopflerfish.framework.bundlestorage.file.BundleStorageImpl.insertBundleJar(BundleStorageImpl.java:219)
[stderr] at org.knopflerfish.framework.Bundles.install0(Bundles.java:161)
[stderr] ... 11 more
I also tried the other way around, to copy the new .class files into the correctly signed jar, and got the same error.
I'm not sure what output to expect for actual verification of the security scheme. Does the error I got mean that java detected the wrongful signature and rejected it because of that? Or does it simply mean that 7zip did not keep the structure intact when I manually wrote to the jar file, and a better hacker than me could still easily tamper with my jar files?
jarsigner -verify
on the tampered jar files gives me
java.lang.SecurityException: SHA-256 digest error
which makes much more sense, can I expect this if I tamper with the jars 'correctly'?
The other answers were correct in that my jar file was invalidated by using 7zip without regard for file order, and thus security was not the issue I encountered when I got the error in the question. They did not directly answer how I should do it instead, so I will show how I got it working and accept my own answer.
I updated the signed jar with an edited .class file using the jar program in jdk instead of using 7zip:
jar.exe -uvf <jarfile.jar> </path/to/classfile.class>
this resulted in this error from java:
[stderr] java.lang.SecurityException: SHA-256 digest error for ****.class
[stderr] at sun.security.util.ManifestEntryVerifier.verify(ManifestEntryVerifier.java:223)
which seems like a reasonable error for a class file which differs from the signature, and matches that of jarsigner -verify