Skip to content

Commit

Permalink
signing: Write META-INF signing resources immediately after manifest
Browse files Browse the repository at this point in the history
JarInputStream requires the META-INF signing resources to come
after the manifest and before any other resources. Otherwise,
JarInputStream does not consider the jar to be properly signed.

Later versions of Equinox now use JarInputStream to verify the
jar signing replacing Equinox's custom verification code. So Bnd
needs to properly generate signed jars to work with JarInputStream.

We also define a standard pattern for the META-INF signing resources
which is used by other classes that care.

Signed-off-by: BJ Hargrave <bj@hargrave.dev>
  • Loading branch information
bjhargrave committed May 28, 2022
1 parent 922b0a9 commit 37e7d2d
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 22 deletions.
16 changes: 8 additions & 8 deletions biz.aQute.bndlib/src/aQute/bnd/differ/DiffPluginImpl.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package aQute.bnd.differ;

import static aQute.bnd.osgi.Jar.METAINF_SIGNING_P;
import static aQute.bnd.service.diff.Delta.CHANGED;

import java.io.File;
Expand All @@ -13,7 +14,6 @@
import java.util.Set;
import java.util.TreeSet;
import java.util.jar.Manifest;
import java.util.regex.Pattern;

import aQute.bnd.header.Attrs;
import aQute.bnd.header.OSGiHeader;
Expand Down Expand Up @@ -129,8 +129,6 @@ private Element bundleElement(Analyzer analyzer) throws Exception {
/**
* Create an element representing all resources in the JAR
*/
private final static Pattern META_INF_P = Pattern.compile("META-INF/([^/]+\\.(MF|SF|DSA|RSA))|(SIG-.*)");

private Element resourcesElement(Analyzer analyzer) throws Exception {
Jar jar = analyzer.getJar();

Expand All @@ -139,16 +137,20 @@ private Element resourcesElement(Analyzer analyzer) throws Exception {
for (Map.Entry<String, Resource> entry : jar.getResources()
.entrySet()) {

String path = entry.getKey();
//
// The manifest and other (signer) files are ignored
// since they are extremely sensitive to time
//

if (META_INF_P.matcher(entry.getKey())
.matches())
if (jar.getManifestName()
.equals(path)
|| METAINF_SIGNING_P.matcher(path)
.matches()) {
continue;
}

if (localIgnore != null && localIgnore.matches(entry.getKey()))
if (localIgnore != null && localIgnore.matches(path))
continue;

//
Expand All @@ -159,8 +161,6 @@ private Element resourcesElement(Analyzer analyzer) throws Exception {
// directory with source code can be found.
//

String path = entry.getKey();

if (path.endsWith(Constants.EMPTY_HEADER))
continue;

Expand Down
40 changes: 30 additions & 10 deletions biz.aQute.bndlib/src/aQute/bnd/osgi/Jar.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import java.util.function.Predicate;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.regex.Matcher;
Expand Down Expand Up @@ -96,7 +97,6 @@ public enum Compression {
STORE
}

private static final String DEFAULT_MANIFEST_NAME = "META-INF/MANIFEST.MF";
private static final Pattern DEFAULT_DO_NOT_COPY = Pattern
.compile(Constants.DEFAULT_DO_NOT_COPY);

Expand All @@ -106,7 +106,7 @@ public enum Compression {
private Optional<Manifest> manifest;
private Optional<ModuleAttribute> moduleAttribute;
private boolean manifestFirst;
private String manifestName = DEFAULT_MANIFEST_NAME;
private String manifestName = JarFile.MANIFEST_NAME;
private String name;
private File source;
private ZipFile zipFile;
Expand All @@ -122,6 +122,8 @@ public enum Compression {
private boolean calculateFileDigest;
private int fileLength = -1;
private long zipEntryConstantTime = ZIP_ENTRY_CONSTANT_TIME;
public static final Pattern METAINF_SIGNING_P = Pattern
.compile("META-INF/([^/]+\\.(?:DSA|RSA|EC|SF)|SIG-[^/]+)", Pattern.CASE_INSENSITIVE);

public Jar(String name) {
this.name = name;
Expand Down Expand Up @@ -590,6 +592,8 @@ public void write(OutputStream to) throws Exception {
Set<String> done = new HashSet<>();

Set<String> directories = new HashSet<>();

// Write manifest first
if (doNotTouchManifest) {
Resource r = getResource(manifestName);
if (r != null) {
Expand All @@ -601,6 +605,22 @@ public void write(OutputStream to) throws Exception {
done.add(manifestName);
}

// Then write any signature info next since JarInputStream really cares!
Map<String, Resource> metainf = getDirectory("META-INF");
if (metainf != null) {
List<String> signing = metainf.keySet()
.stream()
.filter(path -> METAINF_SIGNING_P.matcher(path)
.matches())
.collect(toList());
for (String path : signing) {
if (done.add(path)) {
writeResource(jout, directories, path, metainf.get(path));
}
}
}

// Write all remaining entries
for (Map.Entry<String, Resource> entry : getResources().entrySet()) {
// Skip metainf contents
if (!done.contains(entry.getKey()))
Expand Down Expand Up @@ -1245,16 +1265,16 @@ public byte[] getTimelessDigest() throws Exception {
return md.digest();
}

private final static Pattern SIGNER_FILES_P = Pattern.compile("(.+\\.(SF|DSA|RSA))|(.*/SIG-.*)",
Pattern.CASE_INSENSITIVE);

public void stripSignatures() {
Map<String, Resource> map = getDirectory("META-INF");
if (map != null) {
for (String file : new HashSet<>(map.keySet())) {
if (SIGNER_FILES_P.matcher(file)
Map<String, Resource> metainf = getDirectory("META-INF");
if (metainf != null) {
List<String> signing = metainf.keySet()
.stream()
.filter(path -> METAINF_SIGNING_P.matcher(path)
.matches())
remove(file);
.collect(toList());
for (String path : signing) {
remove(path);
}
}
}
Expand Down
7 changes: 3 additions & 4 deletions biz.aQute.bndlib/src/aQute/bnd/signing/JartoolSigner.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package aQute.bnd.signing;

import static aQute.bnd.osgi.Jar.METAINF_SIGNING_P;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.jar.JarFile;

import org.slf4j.Logger;
Expand Down Expand Up @@ -102,8 +103,6 @@ public void setRegistry(Registry registry) {
processor = registry.getPlugin(Processor.class);
}

private static Pattern SIGNING_P = Pattern.compile("META-INF/([^/]*\\.(DSA|RSA|EC|SF|MF)|SIG-[^/]*)");

@Override
public void sign(Builder builder, String alias) throws Exception {
File f = builder.getFile(keystore);
Expand Down Expand Up @@ -194,7 +193,7 @@ public void sign(Builder builder, String alias) throws Exception {
builder.addClose(signed);

MapStream.of(signed.getDirectory("META-INF"))
.filterKey(path -> SIGNING_P.matcher(path)
.filterKey(path -> JarFile.MANIFEST_NAME.equals(path) || METAINF_SIGNING_P.matcher(path)
.matches())
.forEachOrdered(jar::putResource);
jar.setDoNotTouchManifest();
Expand Down

0 comments on commit 37e7d2d

Please sign in to comment.