Merge branch '2.3.x'
Closes gh-23265
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* Copyright 2012-2020 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.
|
||||
@@ -32,20 +32,21 @@ import java.util.jar.Manifest;
|
||||
*/
|
||||
class JarEntry extends java.util.jar.JarEntry implements FileHeader {
|
||||
|
||||
private final int index;
|
||||
|
||||
private final AsciiBytes name;
|
||||
|
||||
private final AsciiBytes headerName;
|
||||
|
||||
private Certificate[] certificates;
|
||||
|
||||
private CodeSigner[] codeSigners;
|
||||
|
||||
private final JarFile jarFile;
|
||||
|
||||
private long localHeaderOffset;
|
||||
|
||||
JarEntry(JarFile jarFile, CentralDirectoryFileHeader header, AsciiBytes nameAlias) {
|
||||
private volatile JarEntryCertification certification;
|
||||
|
||||
JarEntry(JarFile jarFile, int index, CentralDirectoryFileHeader header, AsciiBytes nameAlias) {
|
||||
super((nameAlias != null) ? nameAlias.toString() : header.getName().toString());
|
||||
this.index = index;
|
||||
this.name = (nameAlias != null) ? nameAlias : header.getName();
|
||||
this.headerName = header.getName();
|
||||
this.jarFile = jarFile;
|
||||
@@ -61,6 +62,10 @@ class JarEntry extends java.util.jar.JarEntry implements FileHeader {
|
||||
}
|
||||
}
|
||||
|
||||
int getIndex() {
|
||||
return this.index;
|
||||
}
|
||||
|
||||
AsciiBytes getAsciiBytesName() {
|
||||
return this.name;
|
||||
}
|
||||
@@ -87,23 +92,24 @@ class JarEntry extends java.util.jar.JarEntry implements FileHeader {
|
||||
|
||||
@Override
|
||||
public Certificate[] getCertificates() {
|
||||
if (this.jarFile.isSigned() && this.certificates == null) {
|
||||
this.jarFile.setupEntryCertificates(this);
|
||||
}
|
||||
return this.certificates;
|
||||
return getCertification().getCertificates();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeSigner[] getCodeSigners() {
|
||||
if (this.jarFile.isSigned() && this.codeSigners == null) {
|
||||
this.jarFile.setupEntryCertificates(this);
|
||||
}
|
||||
return this.codeSigners;
|
||||
return getCertification().getCodeSigners();
|
||||
}
|
||||
|
||||
void setCertificates(java.util.jar.JarEntry entry) {
|
||||
this.certificates = entry.getCertificates();
|
||||
this.codeSigners = entry.getCodeSigners();
|
||||
private JarEntryCertification getCertification() {
|
||||
if (!this.jarFile.isSigned()) {
|
||||
return JarEntryCertification.NONE;
|
||||
}
|
||||
JarEntryCertification certification = this.certification;
|
||||
if (certification == null) {
|
||||
certification = this.jarFile.getCertification(this);
|
||||
this.certification = certification;
|
||||
}
|
||||
return certification;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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.jar;
|
||||
|
||||
import java.security.CodeSigner;
|
||||
import java.security.cert.Certificate;
|
||||
|
||||
/**
|
||||
* {@link Certificate} and {@link CodeSigner} details for a {@link JarEntry} from a signed
|
||||
* {@link JarFile}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class JarEntryCertification {
|
||||
|
||||
static final JarEntryCertification NONE = new JarEntryCertification(null, null);
|
||||
|
||||
private final Certificate[] certificates;
|
||||
|
||||
private final CodeSigner[] codeSigners;
|
||||
|
||||
JarEntryCertification(Certificate[] certificates, CodeSigner[] codeSigners) {
|
||||
this.certificates = certificates;
|
||||
this.codeSigners = codeSigners;
|
||||
}
|
||||
|
||||
Certificate[] getCertificates() {
|
||||
return (this.certificates != null) ? this.certificates.clone() : null;
|
||||
}
|
||||
|
||||
CodeSigner[] getCodeSigners() {
|
||||
return (this.codeSigners != null) ? this.codeSigners.clone() : null;
|
||||
}
|
||||
|
||||
static JarEntryCertification from(java.util.jar.JarEntry certifiedEntry) {
|
||||
Certificate[] certificates = (certifiedEntry != null) ? certifiedEntry.getCertificates() : null;
|
||||
CodeSigner[] codeSigners = (certifiedEntry != null) ? certifiedEntry.getCodeSigners() : null;
|
||||
if (certificates == null && codeSigners == null) {
|
||||
return NONE;
|
||||
}
|
||||
return new JarEntryCertification(certificates, codeSigners);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -31,7 +31,6 @@ import java.util.Iterator;
|
||||
import java.util.Spliterator;
|
||||
import java.util.Spliterators;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.jar.JarInputStream;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
@@ -385,33 +384,15 @@ public class JarFile extends AbstractJarFile implements Iterable<java.util.jar.J
|
||||
return this.signed;
|
||||
}
|
||||
|
||||
void setupEntryCertificates(JarEntry entry) {
|
||||
// Fallback to JarInputStream to obtain certificates, not fast but hopefully not
|
||||
// happening that often.
|
||||
JarEntryCertification getCertification(JarEntry entry) {
|
||||
try {
|
||||
try (JarInputStream inputStream = new JarInputStream(getData().getInputStream())) {
|
||||
java.util.jar.JarEntry certEntry = inputStream.getNextJarEntry();
|
||||
while (certEntry != null) {
|
||||
inputStream.closeEntry();
|
||||
if (entry.getName().equals(certEntry.getName())) {
|
||||
setCertificates(entry, certEntry);
|
||||
}
|
||||
setCertificates(getJarEntry(certEntry.getName()), certEntry);
|
||||
certEntry = inputStream.getNextJarEntry();
|
||||
}
|
||||
}
|
||||
return this.entries.getCertification(entry);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void setCertificates(JarEntry entry, java.util.jar.JarEntry certEntry) {
|
||||
if (entry != null) {
|
||||
entry.setCertificates(certEntry);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearCache() {
|
||||
this.entries.clearCache();
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Attributes.Name;
|
||||
import java.util.jar.JarInputStream;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
@@ -94,14 +95,13 @@ class JarFileEntries implements CentralDirectoryVisitor, Iterable<JarEntry> {
|
||||
|
||||
private Boolean multiReleaseJar;
|
||||
|
||||
private JarEntryCertification[] certifications;
|
||||
|
||||
private final Map<Integer, FileHeader> entriesCache = Collections
|
||||
.synchronizedMap(new LinkedHashMap<Integer, FileHeader>(16, 0.75f, true) {
|
||||
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<Integer, FileHeader> eldest) {
|
||||
if (JarFileEntries.this.jarFile.isSigned()) {
|
||||
return false;
|
||||
}
|
||||
return size() >= ENTRY_CACHE_SIZE;
|
||||
}
|
||||
|
||||
@@ -320,7 +320,7 @@ class JarFileEntries implements CentralDirectoryVisitor, Iterable<JarEntry> {
|
||||
FileHeader entry = (cached != null) ? cached : CentralDirectoryFileHeader
|
||||
.fromRandomAccessData(this.centralDirectoryData, this.centralDirectoryOffsets[index], this.filter);
|
||||
if (CentralDirectoryFileHeader.class.equals(entry.getClass()) && type.equals(JarEntry.class)) {
|
||||
entry = new JarEntry(this.jarFile, (CentralDirectoryFileHeader) entry, nameAlias);
|
||||
entry = new JarEntry(this.jarFile, index, (CentralDirectoryFileHeader) entry, nameAlias);
|
||||
}
|
||||
if (cacheEntry && cached != entry) {
|
||||
this.entriesCache.put(index, entry);
|
||||
@@ -351,6 +351,41 @@ class JarFileEntries implements CentralDirectoryVisitor, Iterable<JarEntry> {
|
||||
return (this.filter != null) ? this.filter.apply(name) : name;
|
||||
}
|
||||
|
||||
JarEntryCertification getCertification(JarEntry entry) throws IOException {
|
||||
JarEntryCertification[] certifications = this.certifications;
|
||||
if (certifications == null) {
|
||||
certifications = new JarEntryCertification[this.size];
|
||||
// We fallback to use JarInputStream to obtain the certs. This isn't that
|
||||
// fast, but hopefully doesn't happen too often.
|
||||
try (JarInputStream certifiedJarStream = new JarInputStream(this.jarFile.getData().getInputStream())) {
|
||||
java.util.jar.JarEntry certifiedEntry = null;
|
||||
while ((certifiedEntry = certifiedJarStream.getNextJarEntry()) != null) {
|
||||
int index = getEntryIndex(certifiedEntry.getName());
|
||||
if (index != -1) {
|
||||
certifications[index] = JarEntryCertification.from(certifiedEntry);
|
||||
}
|
||||
certifiedJarStream.closeEntry();
|
||||
}
|
||||
}
|
||||
this.certifications = certifications;
|
||||
}
|
||||
JarEntryCertification certification = certifications[entry.getIndex()];
|
||||
return (certification != null) ? certification : JarEntryCertification.NONE;
|
||||
}
|
||||
|
||||
private int getEntryIndex(CharSequence name) {
|
||||
int hashCode = AsciiBytes.hashCode(name);
|
||||
int index = getFirstIndex(hashCode);
|
||||
while (index >= 0 && index < this.size && this.hashCodes[index] == hashCode) {
|
||||
CentralDirectoryFileHeader candidate = getEntry(index, CentralDirectoryFileHeader.class, false, null);
|
||||
if (candidate.hasName(name, NO_SUFFIX)) {
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator for contained entries.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user