Allow 'java -jar' to work with signed nested jars
Fix RandomAccessJarFile to correctly read certificate information as jar entries are loaded. This change allows signed nested jars to be used as JCE providers.
This commit is contained in:
parent
5c6dd52e9a
commit
6220aba983
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader.data;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* {@link RandomAccessData} implementation backed by a byte array.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class ByteArrayRandomAccessData implements RandomAccessData {
|
||||
|
||||
private final byte[] bytes;
|
||||
|
||||
private final long offset;
|
||||
|
||||
private final long length;
|
||||
|
||||
public ByteArrayRandomAccessData(byte[] bytes) {
|
||||
this(bytes, 0, (bytes == null ? 0 : bytes.length));
|
||||
}
|
||||
|
||||
public ByteArrayRandomAccessData(byte[] bytes, long offset, long length) {
|
||||
this.bytes = (bytes == null ? new byte[0] : bytes);
|
||||
this.offset = offset;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() {
|
||||
return new ByteArrayInputStream(this.bytes, (int) this.offset, (int) this.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RandomAccessData getSubsection(long offset, long length) {
|
||||
return new ByteArrayRandomAccessData(this.bytes, this.offset + offset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize() {
|
||||
return this.length;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,25 +16,25 @@
|
|||
|
||||
package org.springframework.boot.loader.jar;
|
||||
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.jar.JarEntry;
|
||||
|
||||
import org.springframework.boot.loader.data.RandomAccessData;
|
||||
|
||||
/**
|
||||
* A {@link ZipEntry} returned from a {@link RandomAccessDataZipInputStream}.
|
||||
* A {@link JarEntry} returned from a {@link RandomAccessDataJarInputStream}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class RandomAccessDataZipEntry extends ZipEntry {
|
||||
public class RandomAccessDataJarEntry extends JarEntry {
|
||||
|
||||
private RandomAccessData data;
|
||||
|
||||
/**
|
||||
* Create new {@link RandomAccessDataZipEntry} instance.
|
||||
* @param entry the underying {@link ZipEntry}
|
||||
* Create new {@link RandomAccessDataJarEntry} instance.
|
||||
* @param entry the underlying {@link JarEntry}
|
||||
* @param data the entry data
|
||||
*/
|
||||
public RandomAccessDataZipEntry(ZipEntry entry, RandomAccessData data) {
|
||||
public RandomAccessDataJarEntry(JarEntry entry, RandomAccessData data) {
|
||||
super(entry);
|
||||
this.data = data;
|
||||
}
|
||||
|
|
@ -44,6 +44,6 @@ public class RandomAccessDataZipEntry extends ZipEntry {
|
|||
* @return the entry data
|
||||
*/
|
||||
public RandomAccessData getData() {
|
||||
return data;
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
|
|
@ -20,19 +20,19 @@ import java.io.FilterInputStream;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PushbackInputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarInputStream;
|
||||
|
||||
import org.springframework.boot.loader.data.RandomAccessData;
|
||||
|
||||
/**
|
||||
* A {@link ZipInputStream} backed by {@link RandomAccessData}. Parsed entries provide
|
||||
* A {@link JarInputStream} backed by {@link RandomAccessData}. Parsed entries provide
|
||||
* access to the underlying data {@link RandomAccessData#getSubsection(long, long)
|
||||
* subsection}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class RandomAccessDataZipInputStream extends ZipInputStream {
|
||||
public class RandomAccessDataJarInputStream extends JarInputStream {
|
||||
|
||||
private RandomAccessData data;
|
||||
|
||||
|
|
@ -41,8 +41,9 @@ public class RandomAccessDataZipInputStream extends ZipInputStream {
|
|||
/**
|
||||
* Create a new {@link RandomAccessData} instance.
|
||||
* @param data the source of the zip stream
|
||||
* @throws IOException
|
||||
*/
|
||||
public RandomAccessDataZipInputStream(RandomAccessData data) {
|
||||
public RandomAccessDataJarInputStream(RandomAccessData data) throws IOException {
|
||||
this(data, new TrackingInputStream(data.getInputStream()));
|
||||
}
|
||||
|
||||
|
|
@ -51,17 +52,18 @@ public class RandomAccessDataZipInputStream extends ZipInputStream {
|
|||
* {@link TrackingInputStream}.
|
||||
* @param data the source of the zip stream
|
||||
* @param trackingInputStream a tracking input stream
|
||||
* @throws IOException
|
||||
*/
|
||||
private RandomAccessDataZipInputStream(RandomAccessData data,
|
||||
TrackingInputStream trackingInputStream) {
|
||||
private RandomAccessDataJarInputStream(RandomAccessData data,
|
||||
TrackingInputStream trackingInputStream) throws IOException {
|
||||
super(trackingInputStream);
|
||||
this.data = data;
|
||||
this.trackingInputStream = trackingInputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RandomAccessDataZipEntry getNextEntry() throws IOException {
|
||||
ZipEntry entry = super.getNextEntry();
|
||||
public RandomAccessDataJarEntry getNextEntry() throws IOException {
|
||||
JarEntry entry = (JarEntry) super.getNextEntry();
|
||||
if (entry == null) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -69,7 +71,7 @@ public class RandomAccessDataZipInputStream extends ZipInputStream {
|
|||
closeEntry();
|
||||
int end = getPosition();
|
||||
RandomAccessData entryData = this.data.getSubsection(start, end - start);
|
||||
return new RandomAccessDataZipEntry(entry, entryData);
|
||||
return new RandomAccessDataJarEntry(entry, entryData);
|
||||
}
|
||||
|
||||
private int getPosition() throws IOException {
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package org.springframework.boot.loader.jar;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
|
|
@ -31,14 +31,15 @@ import java.util.Collections;
|
|||
import java.util.Enumeration;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.zip.Inflater;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import org.springframework.boot.loader.data.ByteArrayRandomAccessData;
|
||||
import org.springframework.boot.loader.data.RandomAccessData;
|
||||
import org.springframework.boot.loader.data.RandomAccessDataFile;
|
||||
|
||||
|
|
@ -63,6 +64,9 @@ import org.springframework.boot.loader.data.RandomAccessDataFile;
|
|||
*/
|
||||
public class RandomAccessJarFile extends JarFile {
|
||||
|
||||
private static final RandomAccessData EMPTY_DATA = new ByteArrayRandomAccessData(
|
||||
new byte[0]);
|
||||
|
||||
private final RandomAccessDataFile rootJarFile;
|
||||
|
||||
private RandomAccessData data;
|
||||
|
|
@ -113,19 +117,17 @@ public class RandomAccessJarFile extends JarFile {
|
|||
this.data = data;
|
||||
this.size = data.getSize();
|
||||
|
||||
RandomAccessDataZipInputStream inputStream = new RandomAccessDataZipInputStream(
|
||||
RandomAccessDataJarInputStream inputStream = new RandomAccessDataJarInputStream(
|
||||
data);
|
||||
try {
|
||||
RandomAccessDataZipEntry zipEntry = inputStream.getNextEntry();
|
||||
RandomAccessDataJarEntry zipEntry = inputStream.getNextEntry();
|
||||
while (zipEntry != null) {
|
||||
addJarEntry(zipEntry, filters);
|
||||
zipEntry = inputStream.getNextEntry();
|
||||
}
|
||||
this.manifest = findManifest();
|
||||
this.manifest = inputStream.getManifest();
|
||||
if (this.manifest != null) {
|
||||
for (JarEntry containedEntry : this.entries.values()) {
|
||||
((Entry) containedEntry).configure(this.manifest);
|
||||
}
|
||||
addManifestEntries(filters);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
|
|
@ -133,7 +135,36 @@ public class RandomAccessJarFile extends JarFile {
|
|||
}
|
||||
}
|
||||
|
||||
private void addJarEntry(RandomAccessDataZipEntry zipEntry, JarEntryFilter... filters) {
|
||||
private void addManifestEntries(JarEntryFilter... filters) throws IOException {
|
||||
|
||||
Map<String, JarEntry> originalEntries = this.entries;
|
||||
this.entries = new LinkedHashMap<String, JarEntry>();
|
||||
|
||||
ZipInputStream zipInputStream = new ZipInputStream(this.data.getInputStream());
|
||||
try {
|
||||
JarEntry entry;
|
||||
do {
|
||||
entry = new JarEntry(zipInputStream.getNextEntry());
|
||||
entry.setMethod(ZipEntry.STORED);
|
||||
RandomAccessData data = EMPTY_DATA;
|
||||
if (MANIFEST_NAME.equals(entry.getName())) {
|
||||
ByteArrayOutputStream manifestBytes = new ByteArrayOutputStream();
|
||||
this.manifest.write(manifestBytes);
|
||||
manifestBytes.close();
|
||||
data = new ByteArrayRandomAccessData(manifestBytes.toByteArray());
|
||||
}
|
||||
addJarEntry(new RandomAccessDataJarEntry(entry, data), filters);
|
||||
}
|
||||
while (!MANIFEST_NAME.equals(entry.getName()));
|
||||
|
||||
this.entries.putAll(originalEntries);
|
||||
}
|
||||
finally {
|
||||
zipInputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void addJarEntry(RandomAccessDataJarEntry zipEntry, JarEntryFilter... filters) {
|
||||
Entry jarEntry = new Entry(zipEntry);
|
||||
String name = zipEntry.getName();
|
||||
for (JarEntryFilter filter : filters) {
|
||||
|
|
@ -145,16 +176,6 @@ public class RandomAccessJarFile extends JarFile {
|
|||
}
|
||||
}
|
||||
|
||||
private Manifest findManifest() throws IOException {
|
||||
ZipEntry manifestEntry = getEntry(MANIFEST_NAME);
|
||||
if (manifestEntry != null) {
|
||||
BufferedInputStream inputStream = new BufferedInputStream(
|
||||
getInputStream(manifestEntry));
|
||||
return new Manifest(inputStream);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected final RandomAccessDataFile getRootJarFile() {
|
||||
return this.rootJarFile;
|
||||
}
|
||||
|
|
@ -305,17 +326,11 @@ public class RandomAccessJarFile extends JarFile {
|
|||
|
||||
private RandomAccessData entryData;
|
||||
|
||||
private Attributes attributes;
|
||||
|
||||
public Entry(RandomAccessDataZipEntry entry) {
|
||||
public Entry(RandomAccessDataJarEntry entry) {
|
||||
super(entry);
|
||||
this.entryData = entry.getData();
|
||||
}
|
||||
|
||||
void configure(Manifest manifest) {
|
||||
this.attributes = manifest.getAttributes(getName());
|
||||
}
|
||||
|
||||
void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
|
@ -325,11 +340,6 @@ public class RandomAccessJarFile extends JarFile {
|
|||
return (this.name == null ? super.getName() : this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attributes getAttributes() throws IOException {
|
||||
return this.attributes;
|
||||
}
|
||||
|
||||
public RandomAccessData getData() {
|
||||
return this.entryData;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.loader.data;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ByteArrayRandomAccessData}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class ByteArrayRandomAccessDataTest {
|
||||
|
||||
@Test
|
||||
public void testGetInputStream() throws Exception {
|
||||
byte[] bytes = new byte[] { 0, 1, 2, 3, 4, 5 };
|
||||
RandomAccessData data = new ByteArrayRandomAccessData(bytes);
|
||||
assertThat(FileCopyUtils.copyToByteArray(data.getInputStream()), equalTo(bytes));
|
||||
assertThat(data.getSize(), equalTo((long) bytes.length));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSubsection() throws Exception {
|
||||
byte[] bytes = new byte[] { 0, 1, 2, 3, 4, 5 };
|
||||
RandomAccessData data = new ByteArrayRandomAccessData(bytes);
|
||||
data = data.getSubsection(1, 4).getSubsection(1, 2);
|
||||
assertThat(FileCopyUtils.copyToByteArray(data.getInputStream()),
|
||||
equalTo(new byte[] { 2, 3 }));
|
||||
assertThat(data.getSize(), equalTo(2L));
|
||||
}
|
||||
}
|
||||
|
|
@ -32,15 +32,15 @@ import org.junit.Rule;
|
|||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.springframework.boot.loader.data.RandomAccessDataFile;
|
||||
import org.springframework.boot.loader.jar.RandomAccessDataZipEntry;
|
||||
import org.springframework.boot.loader.jar.RandomAccessDataZipInputStream;
|
||||
import org.springframework.boot.loader.jar.RandomAccessDataJarEntry;
|
||||
import org.springframework.boot.loader.jar.RandomAccessDataJarInputStream;
|
||||
|
||||
/**
|
||||
* Tests for {@link RandomAccessDataZipInputStream}.
|
||||
* Tests for {@link RandomAccessDataJarInputStream}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class RandomAccessDataZipInputStreamTests {
|
||||
public class RandomAccessDataJarInputStreamTests {
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
|
@ -76,11 +76,11 @@ public class RandomAccessDataZipInputStreamTests {
|
|||
|
||||
@Test
|
||||
public void entryData() throws Exception {
|
||||
RandomAccessDataZipInputStream z = new RandomAccessDataZipInputStream(
|
||||
RandomAccessDataJarInputStream z = new RandomAccessDataJarInputStream(
|
||||
new RandomAccessDataFile(file));
|
||||
try {
|
||||
RandomAccessDataZipEntry entry1 = z.getNextEntry();
|
||||
RandomAccessDataZipEntry entry2 = z.getNextEntry();
|
||||
RandomAccessDataJarEntry entry1 = z.getNextEntry();
|
||||
RandomAccessDataJarEntry entry2 = z.getNextEntry();
|
||||
assertThat(entry1.getName(), equalTo("a"));
|
||||
assertThat(entry1.getData().getSize(), equalTo(10L));
|
||||
assertThat(entry2.getName(), equalTo("b"));
|
||||
|
|
@ -25,6 +25,7 @@ import java.net.URL;
|
|||
import java.util.Enumeration;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
import org.junit.Before;
|
||||
|
|
@ -91,6 +92,13 @@ public class RandomAccessJarFileTests {
|
|||
equalTo("j1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getManifestEntry() throws Exception {
|
||||
ZipEntry entry = this.jarFile.getJarEntry("META-INF/MANIFEST.MF");
|
||||
Manifest manifest = new Manifest(this.jarFile.getInputStream(entry));
|
||||
assertThat(manifest.getMainAttributes().getValue("Built-By"), equalTo("j1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getEntries() throws Exception {
|
||||
Enumeration<JarEntry> entries = this.jarFile.entries();
|
||||
|
|
|
|||
Loading…
Reference in New Issue