|
|
@@ -23,6 +23,25 @@ import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
|
|
|
import com.google.common.jimfs.Configuration;
|
|
|
import com.google.common.jimfs.Jimfs;
|
|
|
import org.apache.lucene.util.LuceneTestCase;
|
|
|
+import org.bouncycastle.bcpg.ArmoredOutputStream;
|
|
|
+import org.bouncycastle.bcpg.BCPGOutputStream;
|
|
|
+import org.bouncycastle.bcpg.HashAlgorithmTags;
|
|
|
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
|
|
+import org.bouncycastle.openpgp.PGPEncryptedData;
|
|
|
+import org.bouncycastle.openpgp.PGPException;
|
|
|
+import org.bouncycastle.openpgp.PGPKeyPair;
|
|
|
+import org.bouncycastle.openpgp.PGPPrivateKey;
|
|
|
+import org.bouncycastle.openpgp.PGPPublicKey;
|
|
|
+import org.bouncycastle.openpgp.PGPSecretKey;
|
|
|
+import org.bouncycastle.openpgp.PGPSignature;
|
|
|
+import org.bouncycastle.openpgp.PGPSignatureGenerator;
|
|
|
+import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
|
|
|
+import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder;
|
|
|
+import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
|
|
|
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
|
|
|
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
|
|
|
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
|
|
|
+import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
|
|
|
import org.elasticsearch.Build;
|
|
|
import org.elasticsearch.Version;
|
|
|
import org.elasticsearch.cli.ExitCodes;
|
|
|
@@ -44,6 +63,8 @@ import org.junit.After;
|
|
|
import org.junit.Before;
|
|
|
|
|
|
import java.io.BufferedReader;
|
|
|
+import java.io.ByteArrayInputStream;
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
import java.io.IOException;
|
|
|
import java.io.InputStream;
|
|
|
import java.io.StringReader;
|
|
|
@@ -66,13 +87,19 @@ import java.nio.file.attribute.PosixFileAttributeView;
|
|
|
import java.nio.file.attribute.PosixFileAttributes;
|
|
|
import java.nio.file.attribute.PosixFilePermission;
|
|
|
import java.nio.file.attribute.UserPrincipal;
|
|
|
+import java.security.KeyPair;
|
|
|
+import java.security.KeyPairGenerator;
|
|
|
import java.security.MessageDigest;
|
|
|
+import java.security.NoSuchAlgorithmException;
|
|
|
+import java.security.NoSuchProviderException;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.Arrays;
|
|
|
+import java.util.Date;
|
|
|
import java.util.HashSet;
|
|
|
import java.util.List;
|
|
|
import java.util.Locale;
|
|
|
import java.util.Set;
|
|
|
+import java.util.function.BiFunction;
|
|
|
import java.util.function.Function;
|
|
|
import java.util.stream.Collectors;
|
|
|
import java.util.stream.Stream;
|
|
|
@@ -800,8 +827,16 @@ public class InstallPluginCommandTests extends ESTestCase {
|
|
|
skipJarHellCommand.execute(terminal, pluginZip, isBatch, env.v2());
|
|
|
}
|
|
|
|
|
|
- void assertInstallPluginFromUrl(String pluginId, String name, String url, String stagingHash, boolean isSnapshot,
|
|
|
- String shaExtension, Function<byte[], String> shaCalculator) throws Exception {
|
|
|
+ void assertInstallPluginFromUrl(
|
|
|
+ final String pluginId,
|
|
|
+ final String name,
|
|
|
+ final String url,
|
|
|
+ final String stagingHash,
|
|
|
+ final boolean isSnapshot,
|
|
|
+ final String shaExtension,
|
|
|
+ final Function<byte[], String> shaCalculator,
|
|
|
+ final PGPSecretKey secretKey,
|
|
|
+ final BiFunction<byte[], PGPSecretKey, String> signature) throws Exception {
|
|
|
Tuple<Path, Environment> env = createEnv(fs, temp);
|
|
|
Path pluginDir = createPluginDir(temp);
|
|
|
Path pluginZip = createPlugin(name, pluginDir);
|
|
|
@@ -814,18 +849,61 @@ public class InstallPluginCommandTests extends ESTestCase {
|
|
|
return downloadedPath;
|
|
|
}
|
|
|
@Override
|
|
|
- URL openUrl(String urlString) throws Exception {
|
|
|
- String expectedUrl = url + shaExtension;
|
|
|
- if (expectedUrl.equals(urlString)) {
|
|
|
+ URL openUrl(String urlString) throws IOException {
|
|
|
+ if ((url + shaExtension).equals(urlString)) {
|
|
|
// calc sha an return file URL to it
|
|
|
Path shaFile = temp.apply("shas").resolve("downloaded.zip" + shaExtension);
|
|
|
byte[] zipbytes = Files.readAllBytes(pluginZip);
|
|
|
String checksum = shaCalculator.apply(zipbytes);
|
|
|
Files.write(shaFile, checksum.getBytes(StandardCharsets.UTF_8));
|
|
|
return shaFile.toUri().toURL();
|
|
|
+ } else if ((url + ".asc").equals(urlString)) {
|
|
|
+ final Path ascFile = temp.apply("asc").resolve("downloaded.zip" + ".asc");
|
|
|
+ final byte[] zipBytes = Files.readAllBytes(pluginZip);
|
|
|
+ final String asc = signature.apply(zipBytes, secretKey);
|
|
|
+ Files.write(ascFile, asc.getBytes(StandardCharsets.UTF_8));
|
|
|
+ return ascFile.toUri().toURL();
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
+
|
|
|
+ @Override
|
|
|
+ void verifySignature(Path zip, String urlString) throws IOException, PGPException {
|
|
|
+ if (InstallPluginCommand.OFFICIAL_PLUGINS.contains(name)) {
|
|
|
+ super.verifySignature(zip, urlString);
|
|
|
+ } else {
|
|
|
+ throw new UnsupportedOperationException("verify signature should not be called for unofficial plugins");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ InputStream pluginZipInputStream(Path zip) throws IOException {
|
|
|
+ return new ByteArrayInputStream(Files.readAllBytes(zip));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ String getPublicKeyId() {
|
|
|
+ return Long.toHexString(secretKey.getKeyID()).toUpperCase(Locale.ROOT);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ InputStream getPublicKey() {
|
|
|
+ try {
|
|
|
+ final ByteArrayOutputStream output = new ByteArrayOutputStream();
|
|
|
+ final ArmoredOutputStream armored = new ArmoredOutputStream(output);
|
|
|
+ secretKey.getPublicKey().encode(armored);
|
|
|
+ armored.close();
|
|
|
+ final String publicKey = new String(output.toByteArray(), "UTF-8");
|
|
|
+ int start = publicKey.indexOf("\n", 1 + publicKey.indexOf("\n"));
|
|
|
+ int end = publicKey.lastIndexOf("\n", publicKey.lastIndexOf("\n") - 1);
|
|
|
+ // strip the header (first two lines) and footer (last line)
|
|
|
+ final String substring = publicKey.substring(1 + start, end);
|
|
|
+ return new ByteArrayInputStream(substring.getBytes("UTF-8"));
|
|
|
+ } catch (final IOException e) {
|
|
|
+ throw new AssertionError(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
boolean urlExists(Terminal terminal, String urlString) throws IOException {
|
|
|
return urlString.equals(url);
|
|
|
@@ -851,11 +929,12 @@ public class InstallPluginCommandTests extends ESTestCase {
|
|
|
|
|
|
public void assertInstallPluginFromUrl(
|
|
|
final String pluginId, final String name, final String url, final String stagingHash, boolean isSnapshot) throws Exception {
|
|
|
- MessageDigest digest = MessageDigest.getInstance("SHA-512");
|
|
|
- assertInstallPluginFromUrl(pluginId, name, url, stagingHash, isSnapshot, ".sha512", checksumAndFilename(digest, url));
|
|
|
+ final MessageDigest digest = MessageDigest.getInstance("SHA-512");
|
|
|
+ assertInstallPluginFromUrl(
|
|
|
+ pluginId, name, url, stagingHash, isSnapshot, ".sha512", checksumAndFilename(digest, url), newSecretKey(), this::signature);
|
|
|
}
|
|
|
|
|
|
- public void testOfficalPlugin() throws Exception {
|
|
|
+ public void testOfficialPlugin() throws Exception {
|
|
|
String url = "https://artifacts.elastic.co/downloads/elasticsearch-plugins/analysis-icu/analysis-icu-" + Version.CURRENT + ".zip";
|
|
|
assertInstallPluginFromUrl("analysis-icu", "analysis-icu", url, null, false);
|
|
|
}
|
|
|
@@ -883,13 +962,13 @@ public class InstallPluginCommandTests extends ESTestCase {
|
|
|
e, hasToString(containsString("attempted to install release build of official plugin on snapshot build of Elasticsearch")));
|
|
|
}
|
|
|
|
|
|
- public void testOfficalPluginStaging() throws Exception {
|
|
|
+ public void testOfficialPluginStaging() throws Exception {
|
|
|
String url = "https://staging.elastic.co/" + Version.CURRENT + "-abc123/downloads/elasticsearch-plugins/analysis-icu/analysis-icu-"
|
|
|
+ Version.CURRENT + ".zip";
|
|
|
assertInstallPluginFromUrl("analysis-icu", "analysis-icu", url, "abc123", false);
|
|
|
}
|
|
|
|
|
|
- public void testOfficalPlatformPlugin() throws Exception {
|
|
|
+ public void testOfficialPlatformPlugin() throws Exception {
|
|
|
String url = "https://artifacts.elastic.co/downloads/elasticsearch-plugins/analysis-icu/analysis-icu-" + Platforms.PLATFORM_NAME +
|
|
|
"-" + Version.CURRENT + ".zip";
|
|
|
assertInstallPluginFromUrl("analysis-icu", "analysis-icu", url, null, false);
|
|
|
@@ -905,7 +984,7 @@ public class InstallPluginCommandTests extends ESTestCase {
|
|
|
assertInstallPluginFromUrl("analysis-icu", "analysis-icu", url, "abc123", true);
|
|
|
}
|
|
|
|
|
|
- public void testOfficalPlatformPluginStaging() throws Exception {
|
|
|
+ public void testOfficialPlatformPluginStaging() throws Exception {
|
|
|
String url = "https://staging.elastic.co/" + Version.CURRENT + "-abc123/downloads/elasticsearch-plugins/analysis-icu/analysis-icu-"
|
|
|
+ Platforms.PLATFORM_NAME + "-"+ Version.CURRENT + ".zip";
|
|
|
assertInstallPluginFromUrl("analysis-icu", "analysis-icu", url, "abc123", false);
|
|
|
@@ -924,7 +1003,7 @@ public class InstallPluginCommandTests extends ESTestCase {
|
|
|
public void testMavenSha1Backcompat() throws Exception {
|
|
|
String url = "https://repo1.maven.org/maven2/mygroup/myplugin/1.0.0/myplugin-1.0.0.zip";
|
|
|
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
|
|
- assertInstallPluginFromUrl("mygroup:myplugin:1.0.0", "myplugin", url, null, false, ".sha1", checksum(digest));
|
|
|
+ assertInstallPluginFromUrl("mygroup:myplugin:1.0.0", "myplugin", url, null, false, ".sha1", checksum(digest), null, (b, p) -> null);
|
|
|
assertTrue(terminal.getOutput(), terminal.getOutput().contains("sha512 not found, falling back to sha1"));
|
|
|
}
|
|
|
|
|
|
@@ -932,7 +1011,7 @@ public class InstallPluginCommandTests extends ESTestCase {
|
|
|
String url = "https://artifacts.elastic.co/downloads/elasticsearch-plugins/analysis-icu/analysis-icu-" + Version.CURRENT + ".zip";
|
|
|
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
|
|
UserException e = expectThrows(UserException.class, () ->
|
|
|
- assertInstallPluginFromUrl("analysis-icu", "analysis-icu", url, null, false, ".sha1", checksum(digest)));
|
|
|
+ assertInstallPluginFromUrl("analysis-icu", "analysis-icu", url, null, false, ".sha1", checksum(digest), null, (b, p) -> null));
|
|
|
assertEquals(ExitCodes.IO_ERROR, e.exitCode);
|
|
|
assertEquals("Plugin checksum missing: " + url + ".sha512", e.getMessage());
|
|
|
}
|
|
|
@@ -940,7 +1019,8 @@ public class InstallPluginCommandTests extends ESTestCase {
|
|
|
public void testMavenShaMissing() throws Exception {
|
|
|
String url = "https://repo1.maven.org/maven2/mygroup/myplugin/1.0.0/myplugin-1.0.0.zip";
|
|
|
UserException e = expectThrows(UserException.class, () ->
|
|
|
- assertInstallPluginFromUrl("mygroup:myplugin:1.0.0", "myplugin", url, null, false, ".dne", bytes -> null));
|
|
|
+ assertInstallPluginFromUrl(
|
|
|
+ "mygroup:myplugin:1.0.0", "myplugin", url, null, false, ".dne", bytes -> null, null, (b, p) -> null));
|
|
|
assertEquals(ExitCodes.IO_ERROR, e.exitCode);
|
|
|
assertEquals("Plugin checksum missing: " + url + ".sha1", e.getMessage());
|
|
|
}
|
|
|
@@ -948,8 +1028,9 @@ public class InstallPluginCommandTests extends ESTestCase {
|
|
|
public void testInvalidShaFileMissingFilename() throws Exception {
|
|
|
String url = "https://artifacts.elastic.co/downloads/elasticsearch-plugins/analysis-icu/analysis-icu-" + Version.CURRENT + ".zip";
|
|
|
MessageDigest digest = MessageDigest.getInstance("SHA-512");
|
|
|
- UserException e = expectThrows(UserException.class, () ->
|
|
|
- assertInstallPluginFromUrl("analysis-icu", "analysis-icu", url, null, false, ".sha512", checksum(digest)));
|
|
|
+ UserException e = expectThrows(UserException.class,
|
|
|
+ () -> assertInstallPluginFromUrl(
|
|
|
+ "analysis-icu", "analysis-icu", url, null, false, ".sha512", checksum(digest), null, (b, p) -> null));
|
|
|
assertEquals(ExitCodes.IO_ERROR, e.exitCode);
|
|
|
assertTrue(e.getMessage(), e.getMessage().startsWith("Invalid checksum file"));
|
|
|
}
|
|
|
@@ -965,7 +1046,9 @@ public class InstallPluginCommandTests extends ESTestCase {
|
|
|
null,
|
|
|
false,
|
|
|
".sha512",
|
|
|
- checksumAndString(digest, " repository-s3-" + Version.CURRENT + ".zip")));
|
|
|
+ checksumAndString(digest, " repository-s3-" + Version.CURRENT + ".zip"),
|
|
|
+ null,
|
|
|
+ (b, p) -> null));
|
|
|
assertEquals(ExitCodes.IO_ERROR, e.exitCode);
|
|
|
assertThat(e, hasToString(matches("checksum file at \\[.*\\] is not for this plugin")));
|
|
|
}
|
|
|
@@ -981,7 +1064,9 @@ public class InstallPluginCommandTests extends ESTestCase {
|
|
|
null,
|
|
|
false,
|
|
|
".sha512",
|
|
|
- checksumAndString(digest, " analysis-icu-" + Version.CURRENT + ".zip\nfoobar")));
|
|
|
+ checksumAndString(digest, " analysis-icu-" + Version.CURRENT + ".zip\nfoobar"),
|
|
|
+ null,
|
|
|
+ (b, p) -> null));
|
|
|
assertEquals(ExitCodes.IO_ERROR, e.exitCode);
|
|
|
assertTrue(e.getMessage(), e.getMessage().startsWith("Invalid checksum file"));
|
|
|
}
|
|
|
@@ -996,7 +1081,9 @@ public class InstallPluginCommandTests extends ESTestCase {
|
|
|
null,
|
|
|
false,
|
|
|
".sha512",
|
|
|
- bytes -> "foobar analysis-icu-" + Version.CURRENT + ".zip"));
|
|
|
+ bytes -> "foobar analysis-icu-" + Version.CURRENT + ".zip",
|
|
|
+ null,
|
|
|
+ (b, p) -> null));
|
|
|
assertEquals(ExitCodes.IO_ERROR, e.exitCode);
|
|
|
assertTrue(e.getMessage(), e.getMessage().contains("SHA-512 mismatch, expected foobar"));
|
|
|
}
|
|
|
@@ -1004,11 +1091,77 @@ public class InstallPluginCommandTests extends ESTestCase {
|
|
|
public void testSha1Mismatch() throws Exception {
|
|
|
String url = "https://repo1.maven.org/maven2/mygroup/myplugin/1.0.0/myplugin-1.0.0.zip";
|
|
|
UserException e = expectThrows(UserException.class, () ->
|
|
|
- assertInstallPluginFromUrl("mygroup:myplugin:1.0.0", "myplugin", url, null, false, ".sha1", bytes -> "foobar"));
|
|
|
+ assertInstallPluginFromUrl(
|
|
|
+ "mygroup:myplugin:1.0.0", "myplugin", url, null, false, ".sha1", bytes -> "foobar", null, (b, p) -> null));
|
|
|
assertEquals(ExitCodes.IO_ERROR, e.exitCode);
|
|
|
assertTrue(e.getMessage(), e.getMessage().contains("SHA-1 mismatch, expected foobar"));
|
|
|
}
|
|
|
|
|
|
+ public void testPublicKeyIdMismatchToExpectedPublicKeyId() throws Exception {
|
|
|
+ final String icu = "analysis-icu";
|
|
|
+ final String url =
|
|
|
+ "https://artifacts.elastic.co/downloads/elasticsearch-plugins/analysis-icu/" + icu + "-" + Version.CURRENT + ".zip";
|
|
|
+ final MessageDigest digest = MessageDigest.getInstance("SHA-512");
|
|
|
+ /*
|
|
|
+ * To setup a situation where the expected public key ID does not match the public key ID used for signing, we generate a new public
|
|
|
+ * key at the moment of signing (see the signature invocation). Note that this key will not match the key that we push down to the
|
|
|
+ * install plugin command.
|
|
|
+ */
|
|
|
+ final PGPSecretKey signingKey = newSecretKey(); // the actual key used for signing
|
|
|
+ final String actualID = Long.toHexString(signingKey.getKeyID()).toUpperCase(Locale.ROOT);
|
|
|
+ final BiFunction<byte[], PGPSecretKey, String> signature = (b, p) -> signature(b, signingKey);
|
|
|
+ final PGPSecretKey verifyingKey = newSecretKey(); // the expected key used for signing
|
|
|
+ final String expectedID = Long.toHexString(verifyingKey.getKeyID()).toUpperCase(Locale.ROOT);
|
|
|
+ final IllegalStateException e = expectThrows(
|
|
|
+ IllegalStateException.class,
|
|
|
+ () ->
|
|
|
+ assertInstallPluginFromUrl(
|
|
|
+ icu, icu, url, null, false, ".sha512", checksumAndFilename(digest, url), verifyingKey, signature));
|
|
|
+ assertThat(e, hasToString(containsString("key id [" + actualID + "] does not match expected key id [" + expectedID + "]")));
|
|
|
+ }
|
|
|
+
|
|
|
+ public void testFailedSignatureVerification() throws Exception {
|
|
|
+ final String icu = "analysis-icu";
|
|
|
+ final String url =
|
|
|
+ "https://artifacts.elastic.co/downloads/elasticsearch-plugins/analysis-icu/" + icu + "-" + Version.CURRENT + ".zip";
|
|
|
+ final MessageDigest digest = MessageDigest.getInstance("SHA-512");
|
|
|
+ /*
|
|
|
+ * To setup a situation where signature verification fails, we will mutate the input byte array by modifying a single byte to some
|
|
|
+ * random byte value other than the actual value. This is enough to change the signature and cause verification to intentionally
|
|
|
+ * fail.
|
|
|
+ */
|
|
|
+ final BiFunction<byte[], PGPSecretKey, String> signature = (b, p) -> {
|
|
|
+ final byte[] bytes = Arrays.copyOf(b, b.length);
|
|
|
+ bytes[0] = randomValueOtherThan(b[0], ESTestCase::randomByte);
|
|
|
+ return signature(bytes, p);
|
|
|
+ };
|
|
|
+ final IllegalStateException e = expectThrows(
|
|
|
+ IllegalStateException.class,
|
|
|
+ () ->
|
|
|
+ assertInstallPluginFromUrl(
|
|
|
+ icu, icu, url, null, false, ".sha512", checksumAndFilename(digest, url), newSecretKey(), signature));
|
|
|
+ assertThat(e, hasToString(equalTo("java.lang.IllegalStateException: signature verification for [" + url + "] failed")));
|
|
|
+ }
|
|
|
+
|
|
|
+ public PGPSecretKey newSecretKey() throws NoSuchAlgorithmException, NoSuchProviderException, PGPException {
|
|
|
+ final KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
|
|
|
+ kpg.initialize(2048);
|
|
|
+ final KeyPair pair = kpg.generateKeyPair();
|
|
|
+ final PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(HashAlgorithmTags.SHA1);
|
|
|
+ final PGPKeyPair pkp = new JcaPGPKeyPair(PGPPublicKey.RSA_GENERAL, pair, new Date());
|
|
|
+ return new PGPSecretKey(
|
|
|
+ PGPSignature.DEFAULT_CERTIFICATION,
|
|
|
+ pkp,
|
|
|
+ "example@example.com",
|
|
|
+ sha1Calc,
|
|
|
+ null,
|
|
|
+ null,
|
|
|
+ new JcaPGPContentSignerBuilder(pkp.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1),
|
|
|
+ new JcePBESecretKeyEncryptorBuilder(PGPEncryptedData.CAST5, sha1Calc)
|
|
|
+ .setProvider(new BouncyCastleProvider())
|
|
|
+ .build("passphrase".toCharArray()));
|
|
|
+ }
|
|
|
+
|
|
|
private Function<byte[], String> checksum(final MessageDigest digest) {
|
|
|
return checksumAndString(digest, "");
|
|
|
}
|
|
|
@@ -1022,6 +1175,32 @@ public class InstallPluginCommandTests extends ESTestCase {
|
|
|
return bytes -> MessageDigests.toHexString(digest.digest(bytes)) + s;
|
|
|
}
|
|
|
|
|
|
+ private String signature(final byte[] bytes, final PGPSecretKey secretKey) {
|
|
|
+ try {
|
|
|
+ final PGPPrivateKey privateKey
|
|
|
+ = secretKey.extractPrivateKey(
|
|
|
+ new BcPBESecretKeyDecryptorBuilder(
|
|
|
+ new JcaPGPDigestCalculatorProviderBuilder().build()).build("passphrase".toCharArray()));
|
|
|
+ final PGPSignatureGenerator generator =
|
|
|
+ new PGPSignatureGenerator(
|
|
|
+ new BcPGPContentSignerBuilder(privateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512));
|
|
|
+ generator.init(PGPSignature.BINARY_DOCUMENT, privateKey);
|
|
|
+ final ByteArrayOutputStream output = new ByteArrayOutputStream();
|
|
|
+ try (BCPGOutputStream pout = new BCPGOutputStream(new ArmoredOutputStream(output));
|
|
|
+ InputStream is = new ByteArrayInputStream(bytes)) {
|
|
|
+ final byte[] buffer = new byte[1024];
|
|
|
+ int read;
|
|
|
+ while ((read = is.read(buffer)) != -1) {
|
|
|
+ generator.update(buffer, 0, read);
|
|
|
+ }
|
|
|
+ generator.generate().encode(pout);
|
|
|
+ }
|
|
|
+ return new String(output.toByteArray(), "UTF-8");
|
|
|
+ } catch (IOException | PGPException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// checks the plugin requires a policy confirmation, and does not install when that is rejected by the user
|
|
|
// the plugin is installed after this method completes
|
|
|
private void assertPolicyConfirmation(Tuple<Path, Environment> env, String pluginZip, String... warnings) throws Exception {
|