diff --git a/app/build.xml b/app/build.xml
index a2926990222..b268b777842 100644
--- a/app/build.xml
+++ b/app/build.xml
@@ -101,6 +101,7 @@
     <copy todir="test-bin" overwrite="true" verbose="true">
       <fileset dir="test" includes="**/*.zip" />
       <fileset dir="test" includes="**/*.txt" />
+      <fileset dir="test" includes="**/*.txt.asc" />
       <fileset dir="test" includes="**/*.properties" />
       <fileset dir="test" includes="**/*.ino" />
       <fileset dir="test" includes="**/*.json*" />
diff --git a/app/src/cc/arduino/view/SplashScreenHelper.java b/app/src/cc/arduino/view/SplashScreenHelper.java
index 108c1c8b2f6..a67c3f4d48f 100644
--- a/app/src/cc/arduino/view/SplashScreenHelper.java
+++ b/app/src/cc/arduino/view/SplashScreenHelper.java
@@ -31,11 +31,18 @@
 
 package cc.arduino.view;
 
-import java.awt.*;
+import java.awt.Color;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.SplashScreen;
+import java.awt.Toolkit;
 import java.awt.geom.Rectangle2D;
+import java.io.File;
+import java.io.IOException;
 import java.util.Map;
 
 import processing.app.Theme;
+import processing.app.UpdateCheck;
 
 public class SplashScreenHelper {
 
@@ -54,6 +61,10 @@ public SplashScreenHelper(SplashScreen splash) {
     if (splash != null) {
       Toolkit tk = Toolkit.getDefaultToolkit();
       desktopHints = (Map) tk.getDesktopProperty("awt.font.desktophints");
+      File image = UpdateCheck.getUpdatedSplashImageFile();
+      if (image != null) {
+        splashImage(image);
+      }
     } else {
       desktopHints = null;
     }
@@ -120,4 +131,12 @@ private void printText(String str) {
     System.err.println(str);
   }
 
+  public void splashImage(File f) {
+    try {
+      splash.setImageURL(f.toURI().toURL());
+    } catch (NullPointerException | IllegalStateException | IOException e) {
+      e.printStackTrace();
+    }
+  }
+
 }
diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java
index 7af728fdd49..336487893c1 100644
--- a/app/src/processing/app/Base.java
+++ b/app/src/processing/app/Base.java
@@ -296,7 +296,7 @@ public Base(String[] args) throws Exception {
     pdeKeywords = new PdeKeywords();
     pdeKeywords.reload();
 
-    final GPGDetachedSignatureVerifier gpgDetachedSignatureVerifier = new GPGDetachedSignatureVerifier();
+    final SignatureVerifier gpgDetachedSignatureVerifier = new SignatureVerifier();
     contributionInstaller = new ContributionInstaller(BaseNoGui.getPlatform(), gpgDetachedSignatureVerifier);
     libraryInstaller = new LibraryInstaller(BaseNoGui.getPlatform(), gpgDetachedSignatureVerifier);
 
@@ -1886,8 +1886,16 @@ static public String[] headerListFromIncludePath(File path) throws IOException {
    */
   @SuppressWarnings("serial")
   public void handleAbout() {
-    final Image image = Theme.getLibImage("about", activeEditor,
-                                          Theme.scale(475), Theme.scale(300));
+    Image image;
+    File f = UpdateCheck.getUpdatedSplashImageFile();
+    if (f != null) {
+      Toolkit tk = Toolkit.getDefaultToolkit();
+      Image unscaled = tk.getImage(f.getAbsolutePath());
+      image = Theme.scale(unscaled, activeEditor);
+    } else {
+      image = Theme.getLibImage("about", activeEditor, //
+                                Theme.scale(475), Theme.scale(300));
+    }
     final Window window = new Window(activeEditor) {
       public void paint(Graphics graphics) {
         Graphics2D g = Theme.setupGraphics2D(graphics);
diff --git a/app/src/processing/app/Theme.java b/app/src/processing/app/Theme.java
index d38875b3597..24a7c2db934 100644
--- a/app/src/processing/app/Theme.java
+++ b/app/src/processing/app/Theme.java
@@ -575,23 +575,42 @@ static public Image getLibImage(String filename, Component who, int width,
       image = tk.getImage(imageFile.getUrl());
     }
 
+    image = rescaleImage(image, who, width, height);
+
+    return image;
+  }
+
+  public static Image rescaleImage(Image image, Component who, int width, int height) {
     MediaTracker tracker = new MediaTracker(who);
     try {
       tracker.addImage(image, 0);
       tracker.waitForAll();
     } catch (InterruptedException e) {
     }
+    if (image.getWidth(null) == width && image.getHeight(null) == height) {
+      return image;
+    }
 
-    if (image.getWidth(null) != width || image.getHeight(null) != height) {
-      image = image.getScaledInstance(width, height, Image.SCALE_SMOOTH);
-      try {
-        tracker.addImage(image, 1);
-        tracker.waitForAll();
-      } catch (InterruptedException e) {
-      }
+    Image rescaled = image.getScaledInstance(width, height, Image.SCALE_SMOOTH);
+    try {
+      tracker.addImage(rescaled, 1);
+      tracker.waitForAll();
+    } catch (InterruptedException e) {
     }
+    return rescaled;
+  }
 
-    return image;
+  public static Image scale(Image image, Component who) {
+    MediaTracker tracker = new MediaTracker(who);
+    try {
+      tracker.addImage(image, 0);
+      tracker.waitForAll();
+    } catch (InterruptedException e) {
+    }
+
+    int w = image.getWidth(null);
+    int h = image.getHeight(null);
+    return rescaleImage(image, who, scale(w), scale(h));
   }
 
   /**
diff --git a/app/src/processing/app/UpdateCheck.java b/app/src/processing/app/UpdateCheck.java
index cdca1b71783..ec303d424ba 100644
--- a/app/src/processing/app/UpdateCheck.java
+++ b/app/src/processing/app/UpdateCheck.java
@@ -22,18 +22,29 @@
 
 package processing.app;
 
-import org.apache.commons.compress.utils.IOUtils;
-import processing.app.legacy.PApplet;
+import static processing.app.I18n.tr;
 
-import javax.swing.*;
 import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.URL;
 import java.net.URLEncoder;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+import java.util.List;
 import java.util.Random;
+import java.util.stream.Collectors;
 
-import static processing.app.I18n.tr;
+import javax.swing.JOptionPane;
+
+import org.apache.commons.compress.utils.IOUtils;
+
+import cc.arduino.contributions.SignatureVerifier;
+import cc.arduino.utils.FileHash;
+import processing.app.legacy.PApplet;
 
 
 /**
@@ -51,10 +62,6 @@
  */
 public class UpdateCheck implements Runnable {
   Base base;
-  String downloadURL = tr("https://www.arduino.cc/latest.txt");
-
-  static final long ONE_DAY = 24 * 60 * 60 * 1000;
-
 
   public UpdateCheck(Base base) {
     Thread thread = new Thread(this);
@@ -62,21 +69,81 @@ public UpdateCheck(Base base) {
     thread.start();
   }
 
+  final long ONE_DAY = 24 * 60 * 60 * 1000;
 
   public void run() {
-    //System.out.println("checking for updates...");
+    // Ensure updates-check are made only once per day
+    Long when = PreferencesData.getLong("update.last");
+    long now = System.currentTimeMillis();
+    if (when != null && (now - when) < ONE_DAY) {
+      // don't annoy the shit outta people
+      return;
+    }
+    PreferencesData.setLong("update.last", now);
+
+    checkForIDEUpdates();
 
-    long id;
-    String idString = PreferencesData.get("update.id");
-    if (idString != null) {
-      id = Long.parseLong(idString);
-    } else {
+    checkForSplashImageUpdates();
+  }
+
+  private void checkForSplashImageUpdates() {
+    File tmp = null;
+    try {
+      tmp = File.createTempFile("arduino_splash_update", ".txt.asc");
+      // Check for updates of the splash screen
+      downloadFileFromURL("https://go.bug.st/latest_splash.txt.asc", tmp);
+      SignatureVerifier verifier = new SignatureVerifier();
+      if (!verifier.verifyCleartextSignature(tmp)) {
+        return;
+      }
+      String[] lines = verifier.extractTextFromCleartextSignature(tmp);
+      if (lines.length < 2) {
+        return;
+      }
+      String newSplashUrl = lines[0];
+      String checksum = lines[1];
+
+      // if the splash image has been changed download the new file
+      String oldSplashUrl = PreferencesData.get("splash.imageurl");
+      if (!newSplashUrl.equals(oldSplashUrl)) {
+        File tmpFile = BaseNoGui.getSettingsFile("splash.png.tmp");
+        downloadFileFromURL(newSplashUrl, tmpFile);
+
+        String algo = checksum.split(":")[0];
+        String crc = FileHash.hash(tmpFile, algo);
+        if (!crc.equalsIgnoreCase(checksum)) {
+          return;
+        }
+
+        File destFile = BaseNoGui.getSettingsFile("splash.png");
+        Files.move(tmpFile.toPath(), destFile.toPath(),
+                   StandardCopyOption.REPLACE_EXISTING);
+        PreferencesData.set("splash.imageurl", newSplashUrl);
+      }
+
+      // extend expiration by 24h
+      long now = System.currentTimeMillis();
+      PreferencesData.setLong("splash.expire", now + ONE_DAY);
+    } catch (Exception e) {
+      // e.printStackTrace();
+    } finally {
+      if (tmp != null) {
+        tmp.delete();
+      }
+    }
+  }
+
+  private void checkForIDEUpdates() {
+    // Set update id
+    Long id = PreferencesData.getLong("update.id");
+    if (id == null) {
       // generate a random id in case none exists yet
       Random r = new Random();
       id = r.nextLong();
-      PreferencesData.set("update.id", String.valueOf(id));
+      PreferencesData.setLong("update.id", id);
     }
 
+    // Check for updates of the IDE
     try {
       String info;
       info = URLEncoder.encode(id + "\t" +
@@ -87,18 +154,7 @@ public void run() {
                         System.getProperty("os.version") + "\t" +
                         System.getProperty("os.arch"), "UTF-8");
       
-      int latest = readInt(downloadURL + "?" + info);
-
-      String lastString = PreferencesData.get("update.last");
-      long now = System.currentTimeMillis();
-      if (lastString != null) {
-        long when = Long.parseLong(lastString);
-        if (now - when < ONE_DAY) {
-          // don't annoy the shit outta people
-          return;
-        }
-      }
-      PreferencesData.set("update.last", String.valueOf(now));
+      int latest = readIntFromURL("https://www.arduino.cc/latest.txt?" + info);
 
       String prompt =
         tr("A new version of Arduino is available,\n" +
@@ -116,7 +172,7 @@ public void run() {
                                                     options,
                                                     options[0]);
           if (result == JOptionPane.YES_OPTION) {
-            Base.openURL(tr("https://www.arduino.cc/en/Main/Software"));
+            Base.openURL("https://www.arduino.cc/en/Main/Software");
           }
         }
       }
@@ -126,15 +182,38 @@ public void run() {
     }
   }
 
+  public static File getUpdatedSplashImageFile() {
+    if (PreferencesData.has("splash.expire")) {
+      Long expire = PreferencesData.getLong("splash.expire");
+      long now = System.currentTimeMillis();
+      if (expire != null && now < expire) {
+        File f = BaseNoGui.getSettingsFile("splash.png");
+        if (f.isFile()) {
+          return f;
+        }
+      }
+    }
+    return null;
+  }
 
-  protected int readInt(String filename) throws IOException {
-    URL url = new URL(filename);
-    BufferedReader reader = null;
-    try {
-      reader = new BufferedReader(new InputStreamReader(url.openStream()));
-      return Integer.parseInt(reader.readLine());
-    } finally {
-      IOUtils.closeQuietly(reader);
+  protected int readIntFromURL(String _url) throws Exception {
+    List<String> lines = readFileFromURL(_url);
+    return Integer.parseInt(lines.get(0));
+  }
+
+  protected List<String> readFileFromURL(String _url) throws IOException {
+    URL url = new URL(_url);
+    try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));) {
+      return in.lines().collect(Collectors.toList());
+    }
+  }
+
+  protected void downloadFileFromURL(String _url, File dest) throws IOException {
+    URL url = new URL(_url);
+    try (InputStream in = url.openStream()) {
+      try (FileOutputStream out = new FileOutputStream(dest)) {
+        IOUtils.copy(in, out);
+      }
     }
   }
 }
diff --git a/app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java b/app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java
deleted file mode 100644
index 7dd7285a064..00000000000
--- a/app/test/cc/arduino/contributions/GPGDetachedSignatureVerifierTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * This file is part of Arduino.
- *
- * Copyright 2015 Arduino LLC (http://www.arduino.cc/)
- *
- * Arduino is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * As a special exception, you may use this file as part of a free software
- * library without restriction.  Specifically, if other files instantiate
- * templates or use macros or inline functions from this file, or you compile
- * this file and link it with other files to produce an executable, this
- * file does not by itself cause the resulting executable to be covered by
- * the GNU General Public License.  This exception does not however
- * invalidate any other reasons why the executable file might be covered by
- * the GNU General Public License.
- */
-
-package cc.arduino.contributions;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.File;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-public class GPGDetachedSignatureVerifierTest {
-
-  private GPGDetachedSignatureVerifier GPGDetachedSignatureVerifier;
-
-  @Before
-  public void setUp() throws Exception {
-    GPGDetachedSignatureVerifier = new GPGDetachedSignatureVerifier();
-  }
-
-  @Test
-  public void testSignatureSuccessfulVerification() throws Exception {
-    File signedFile = new File(GPGDetachedSignatureVerifierTest.class.getResource("./package_index.json").getFile());
-    File sign = new File(GPGDetachedSignatureVerifierTest.class.getResource("./package_index.json.sig").getFile());
-    File publickKey = new File(GPGDetachedSignatureVerifierTest.class.getResource("./public.gpg.key").getFile());
-    assertTrue(GPGDetachedSignatureVerifier.verify(signedFile, sign, publickKey));
-  }
-
-  @Test
-  public void testSignatureFailingVerification() throws Exception {
-    File fakeSignedFile = File.createTempFile("fakeSigned", "txt");
-    fakeSignedFile.deleteOnExit();
-    File sign = new File(GPGDetachedSignatureVerifierTest.class.getResource("./package_index.json.sig").getFile());
-    File publickKey = new File(GPGDetachedSignatureVerifierTest.class.getResource("./public.gpg.key").getFile());
-    assertFalse(GPGDetachedSignatureVerifier.verify(fakeSignedFile, sign, publickKey));
-  }
-}
diff --git a/app/test/cc/arduino/contributions/SignatureVerifierTest.java b/app/test/cc/arduino/contributions/SignatureVerifierTest.java
new file mode 100644
index 00000000000..d99c5e15494
--- /dev/null
+++ b/app/test/cc/arduino/contributions/SignatureVerifierTest.java
@@ -0,0 +1,85 @@
+/*
+ * This file is part of Arduino.
+ *
+ * Copyright 2015 Arduino LLC (http://www.arduino.cc/)
+ *
+ * Arduino is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * As a special exception, you may use this file as part of a free software
+ * library without restriction.  Specifically, if other files instantiate
+ * templates or use macros or inline functions from this file, or you compile
+ * this file and link it with other files to produce an executable, this
+ * file does not by itself cause the resulting executable to be covered by
+ * the GNU General Public License.  This exception does not however
+ * invalidate any other reasons why the executable file might be covered by
+ * the GNU General Public License.
+ */
+
+package cc.arduino.contributions;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class SignatureVerifierTest {
+
+  private SignatureVerifier verifier;
+
+  @Before
+  public void setUp() throws Exception {
+    verifier = new SignatureVerifier();
+    File keyRingFile = new File(SignatureVerifierTest.class.getResource("./test.public.gpg.key").getFile());
+    verifier.setKeyRingFile(keyRingFile);
+  }
+
+  @Test
+  public void testSignatureSuccessfulVerification() throws Exception {
+    File signedFile = new File(SignatureVerifierTest.class.getResource("./package_index.json").getFile());
+    File sign = new File(SignatureVerifierTest.class.getResource("./package_index.json.sig").getFile());
+    assertTrue(verifier.verify(signedFile, sign));
+  }
+
+  @Test
+  public void testSignatureFailingVerification() throws Exception {
+    File fakeSignedFile = File.createTempFile("fakeSigned", "txt");
+    fakeSignedFile.deleteOnExit();
+    File sign = new File(SignatureVerifierTest.class.getResource("./package_index.json.sig").getFile());
+    assertFalse(verifier.verify(fakeSignedFile, sign));
+  }
+
+  @Test
+  public void testClearTextSignatureVerification() throws Exception {
+    File signedFile = new File(SignatureVerifierTest.class.getResource("./text.txt.asc").getFile());
+    assertTrue(verifier.verifyCleartextSignature(signedFile));
+    File signedFile2 = new File(SignatureVerifierTest.class.getResource("./escaped-text.txt.asc").getFile());
+    assertTrue(verifier.verifyCleartextSignature(signedFile2));
+    File oneBlankLines = new File(SignatureVerifierTest.class.getResource("./one-blank-line.txt.asc").getFile());
+    assertTrue(verifier.verifyCleartextSignature(oneBlankLines));
+    File twoBlankLines = new File(SignatureVerifierTest.class.getResource("./two-blank-lines.txt.asc").getFile());
+    assertTrue(verifier.verifyCleartextSignature(twoBlankLines));
+    File noBlankLines = new File(SignatureVerifierTest.class.getResource("./no-blank-lines.txt.asc").getFile());
+    assertTrue(verifier.verifyCleartextSignature(noBlankLines));
+  }
+
+  @Test
+  public void testClearTextSignatureFailingVerification() throws Exception {
+    File signedFile = new File(SignatureVerifierTest.class.getResource("./wrong-text.txt.asc").getFile());
+    assertFalse(verifier.verifyCleartextSignature(signedFile));
+  }
+}
diff --git a/app/test/cc/arduino/contributions/escaped-text.txt.asc b/app/test/cc/arduino/contributions/escaped-text.txt.asc
new file mode 100644
index 00000000000..9c89d0720c4
--- /dev/null
+++ b/app/test/cc/arduino/contributions/escaped-text.txt.asc
@@ -0,0 +1,29 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+- -
+- - 
+- - abc
+- -- abc
+helllo!!!!
+
+
+
+
+
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAEBCgAdFiEEMmVnwcayiN8yywYalfpvQ+IRiMQFAl6CcaUACgkQlfpvQ+IR
+iMQD9w/+KA2ZusmFnpt7dXTy/6ohNCijMp3pFSVsBzGWBDpGJjYfwek2N8HssXIa
+Tq0uzCiTm1Z4kn+SWjIHxKzdihUi9mwkMCqdhNTZN0+FKXc+c358KUYXOlyigOmX
+256bG7Ep2P/3ZBzhvVC5WKLIwYhq6cj9fSnt26XP02qt/9ztczGJHDpEfIhPA0LI
+PYnUUo4KftQzHp41EPENIyLTkT1YzhypnIHCv2mG8qql+W9blx1eO8gIUGmzQQ0y
+CnTY0AIvmrAGd5WQWwKpKy5aLpWDmIW/zSSoDc7jC74i2V6n5Y+Fqq++SVDvIApd
+yP+BmL6pDfZf9cV8nDQOI9Jd+/JT3tUct5js9lDhj74g1ZGobx06kZPv/ojbSE42
+jJi75HQ3NDG8dBDMtYzrDeO0QhcpT94LNTdZ8IdR5jCf3I9SkpHB6sb27elVgcBb
+stXLRhrf0se2U2pI3CGDkjumm04cDOhY9tLt2CHMlL9yl9LZejyT3xUoLGAy1Ird
+Jw6knZM5O/bWzFq1bxlqgz6EspafNy+ZM8/1s+b9ecxKkds7UEZLpOVd+QYMO6i1
+lxm4ZQqRoTiIcVxzhwChd41zxPw9bMNq03prBMnetJ9tvb76qgGcCvRh/ANYWBfa
+2zeH3UGaOyMB79oKOSw0qzXkFA+0qXzROObVWTzjGOPZJtkfaA8=
+=BqjQ
+-----END PGP SIGNATURE-----
diff --git a/app/test/cc/arduino/contributions/no-blank-lines.txt.asc b/app/test/cc/arduino/contributions/no-blank-lines.txt.asc
new file mode 100644
index 00000000000..9b5e995b022
--- /dev/null
+++ b/app/test/cc/arduino/contributions/no-blank-lines.txt.asc
@@ -0,0 +1,20 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+hello
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAEBCgAdFiEEMmVnwcayiN8yywYalfpvQ+IRiMQFAl6C/AsACgkQlfpvQ+IR
+iMQYUA//SEHkb60xiXyP8TfTfxiJORr7ltvLtL7N9IwpnrkeKdvvrxC71O5U4kdf
+Ek5XUALXUPArWJHByKBZ3saQapRPMUNbsLDG9QqtIffORqgPnfPAgMiGYceIaT1o
+QChspIQRL6xt6k8c6o2M5R7CBMjiDqaldpJHsq3kcjcgHBmJFPdDdhjA2D9s4mCo
+nymy3PqLcZ4RARrED61VlF4hcVHv5BHPnDZDXug3oGiR2EQTpdQgvBg/rL2xiVHl
+MgZ6/5owF6omeNM30JNPQfNdDnV2jDhxILfjY2Rqyj9zClrnZB6qox6h1RoSKIFy
+UkBUAs3WapZMqR2UKq3K7dL8cqUKoOFdSpd/R2T4ZBC/6exiwVnbDGa161ur8f4/
+55RxAe/VE+75bC7HI3jNAUn7xGMUGBWgTKh+xTnyh50BHifmCIzyLOgRWds/rvMH
+PT0fmtbTWhjrJOfuhi15uSJ0Lvq/XuM/z/aIgeZHaIBnIxvRYUOdP6Rz1xKHmc2q
++puI2FxpbENUyt8TNlrnHJeq72UubVIRJ08CE2iiuWjP+1jFxPYA1tWBYrqtbK7y
+2ZPq5c+vy8LMij/SfZkeOa388Ss3lXr5CTJ+O2VMLellyCdUd0G47T26umCr6zaF
+jX1huU0rfA+vGLVfr1Z2nexX0r7kwebkz/PWIO1+Kc41gBY+cek=
+=Rejh
+-----END PGP SIGNATURE-----
diff --git a/app/test/cc/arduino/contributions/one-blank-line.txt.asc b/app/test/cc/arduino/contributions/one-blank-line.txt.asc
new file mode 100644
index 00000000000..7a48cf72987
--- /dev/null
+++ b/app/test/cc/arduino/contributions/one-blank-line.txt.asc
@@ -0,0 +1,21 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+hello
+
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAEBCgAdFiEEMmVnwcayiN8yywYalfpvQ+IRiMQFAl6C/E8ACgkQlfpvQ+IR
+iMS/iA/+KxhaOTokmXTKgSkZwaCdPwTIdcfIVWMsK6mxXcSeWtmQpCxMyH3sudaA
+MvVg8qMHTTiYbFD5TMBF79Eu3nJuLemnm7WbpQMAQ7/Ay8qDrH6MIIqeR6TLa6CO
+VQw2a9H3AoqVxBtH0y6vVk+mOnr8NnZA3XoT0B2TuLApqY4uFt9quHBkrycHdXve
+6++EyiYeDgY6oG1bAR0S/peFhCL45CId9xJe5RIHT7aFoDKw78LPcfoTLdKCtzzE
+CdX1IpzP/tOVlv0LTNPWQQeRQ07BKpquQnQbzuAgVFbLf27kI2e+AxjIdmozEjO9
+rk9Za0tpKYCYT5lF9zfmaCWzkhTfrjB8OPXWYHjozzv4aUhfvgQ+TF9dynWU4ct8
+ACRkb+bp+ENZVy0/1VdDDs3wrFnMHuZyrBWXNI8sBu6f1JSG4ddgy7NfPveOJEdG
+wg/4mxzSnBQ7jogRaJLioC+mjxulmN1FCoDMwphOLkmaIX1qIL3OxkEBfSzpfkfe
+wAYhjSqLHHXwx0W0resDahC2L81gBnTbf0yP1jEWjnSs96AqwvLSAj0gyC4BtVoN
+LSgYDFeNitoBMDx8osi0KjLydOzbQW9L5u//Rm/Q/8/p7+4u/X3X8EWHULHIsrRW
+T9M6jun8zTr9OLAk9W1T76Yu2Wyu+ps6f6iAeSlvqTTpr3+j2W4=
+=0JE+
+-----END PGP SIGNATURE-----
diff --git a/app/test/cc/arduino/contributions/public.gpg.key b/app/test/cc/arduino/contributions/test.public.gpg.key
similarity index 62%
rename from app/test/cc/arduino/contributions/public.gpg.key
rename to app/test/cc/arduino/contributions/test.public.gpg.key
index 5de39fed1f7..fde092a92ac 100644
Binary files a/app/test/cc/arduino/contributions/public.gpg.key and b/app/test/cc/arduino/contributions/test.public.gpg.key differ
diff --git a/app/test/cc/arduino/contributions/text.txt.asc b/app/test/cc/arduino/contributions/text.txt.asc
new file mode 100644
index 00000000000..23b47f73ba3
--- /dev/null
+++ b/app/test/cc/arduino/contributions/text.txt.asc
@@ -0,0 +1,21 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+helllo!!!!
+
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAEBCgAdFiEEMmVnwcayiN8yywYalfpvQ+IRiMQFAl6CPtwACgkQlfpvQ+IR
+iMS8AA//dCIbYCPwTdOFf3YRFO2Z7YSgAgEpctcH1UW9IaZpGlfZDqiskgNT5zWx
+ooW0EqVagyyY+1Fips4a52/urQmbQ+0RIU6r4zDUvyMoN/f41jFKd1eY365oqORo
+G5avMFhZ7PwbYXKhDeVQrDCRSYSvWYyRXH97VpFvo/SSuvB7KWvlTFFK9rt1xysx
+rjikCqMWuPeReSclLCMCGQGjcJExNsu7E9l55NcJMOP8a3yVlY9b1LwH5nuVXOfw
+UvSzHK2K16O3COb0otWN8VZHB0x2y3Y1boIF2J/Wt9zBaB/d4cmacwL4KLHiw7KR
+q5YzDfEpmwMM9S5QLsLPYBWr9B1XSQ+PrylPSha1NnDStr/RkFtwVsags3igJu5E
+Ye+aJra6D/VREFc/IQYsEmcDHYdKp3CNJn/BdNwTDI4BIdj4lBDd6lVrcL+Id8pl
+ivdk1bV/575eTH9cDf65aF/lmd/z6dGNLcke4N76n8g/xjAuSkN7FDm91KGv9oCJ
+e0eO0+GDJbfMHz61vQ1DPfUWqyQLC3z9OeX+bUvqSb1Xy9i2OI0FlY3GMtwDONto
+qShjcbeXXYt74bINsAEV78pSoYPHc2rYco1/qrAL1bYY9Hngy3FvgD7mVNd7nC/J
+buFQ4Ek07urJpA9lw8o/z49O7iGisc8UXuPeHY2z7LmUuCvKpKI=
+=DKVh
+-----END PGP SIGNATURE-----
diff --git a/app/test/cc/arduino/contributions/two-blank-lines.txt.asc b/app/test/cc/arduino/contributions/two-blank-lines.txt.asc
new file mode 100644
index 00000000000..ec8f2f5b18c
--- /dev/null
+++ b/app/test/cc/arduino/contributions/two-blank-lines.txt.asc
@@ -0,0 +1,22 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+hello
+
+
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAEBCgAdFiEEMmVnwcayiN8yywYalfpvQ+IRiMQFAl6C/H8ACgkQlfpvQ+IR
+iMQD9RAAk34n7WegdXdreKLU5nAkx9/lcPwZYRcwTeV6pgroxQ3XuC3oEqUwDQtD
+RLT+Gc+1j7XMGxTIJtHhpwpJk2IeT5x/lfqpMwpHoJUEOA/QZ7YtUTUj5aqc3bJV
+QEWXrNyceqaYkUguR7gk0ahtniTuaj9WaixRpxiu9+AMht74g0iZm99h2fpgiIb1
+7M2SdMbmyW1mK7BFN9Cghz/8NedV6TeKpQnWWpN+hdO1fsjGIVRNRfTMod7gL6QK
+KhPa3eQW+yxlEshKZwQJwIe3vcgquK6bAl/p20EU7+2ytnFd3zBhcCFJtC6fV+zF
+s89BOAoAJPRaBKbQp01IsBB5mNZUxKFOa+e94tFLmaaI7KhHB7oQ7Qnx+hTgq7bG
+9PRza0bHMkBPumACM8FNjOlzJNY5eTnfDcAq2kgaAdoX5LFLrIiYUNtz9gmIIHNj
+xekG9LB3WUv013jPSOUnbmGkch0VvVgFvcsOtGWTHp+VlZLOWZjXkO+Cco2qWcVr
+F7ckPTXs+nzoOTVsYZLfN8g92y2sT548+Q9bCtT1QStGCSTyNF1o0JW9Ih1hsSkn
+qJVgo8wGO45mTT4GJ6VWUkFkAjoaZM14F70CJp0eLw5PSxDIkSsmkL7CYwGeA+6N
+UtKIJ1ZoFMElW5bMuSayLEjtC9RkGILjplkiFY+h7gJd/vitTvI=
+=g/9c
+-----END PGP SIGNATURE-----
diff --git a/app/test/cc/arduino/contributions/wrong-text.txt.asc b/app/test/cc/arduino/contributions/wrong-text.txt.asc
new file mode 100644
index 00000000000..de08872c6be
--- /dev/null
+++ b/app/test/cc/arduino/contributions/wrong-text.txt.asc
@@ -0,0 +1,21 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+!!helllo!!!!
+
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAEBCgAdFiEEMmVnwcayiN8yywYalfpvQ+IRiMQFAl6CPtwACgkQlfpvQ+IR
+iMS8AA//dCIbYCPwTdOFf3YRFO2Z7YSgAgEpctcH1UW9IaZpGlfZDqiskgNT5zWx
+ooW0EqVagyyY+1Fips4a52/urQmbQ+0RIU6r4zDUvyMoN/f41jFKd1eY365oqORo
+G5avMFhZ7PwbYXKhDeVQrDCRSYSvWYyRXH97VpFvo/SSuvB7KWvlTFFK9rt1xysx
+rjikCqMWuPeReSclLCMCGQGjcJExNsu7E9l55NcJMOP8a3yVlY9b1LwH5nuVXOfw
+UvSzHK2K16O3COb0otWN8VZHB0x2y3Y1boIF2J/Wt9zBaB/d4cmacwL4KLHiw7KR
+q5YzDfEpmwMM9S5QLsLPYBWr9B1XSQ+PrylPSha1NnDStr/RkFtwVsags3igJu5E
+Ye+aJra6D/VREFc/IQYsEmcDHYdKp3CNJn/BdNwTDI4BIdj4lBDd6lVrcL+Id8pl
+ivdk1bV/575eTH9cDf65aF/lmd/z6dGNLcke4N76n8g/xjAuSkN7FDm91KGv9oCJ
+e0eO0+GDJbfMHz61vQ1DPfUWqyQLC3z9OeX+bUvqSb1Xy9i2OI0FlY3GMtwDONto
+qShjcbeXXYt74bINsAEV78pSoYPHc2rYco1/qrAL1bYY9Hngy3FvgD7mVNd7nC/J
+buFQ4Ek07urJpA9lw8o/z49O7iGisc8UXuPeHY2z7LmUuCvKpKI=
+=DKVh
+-----END PGP SIGNATURE-----
diff --git a/arduino-core/src/cc/arduino/contributions/GPGDetachedSignatureVerifier.java b/arduino-core/src/cc/arduino/contributions/GPGDetachedSignatureVerifier.java
deleted file mode 100644
index ead276f4731..00000000000
--- a/arduino-core/src/cc/arduino/contributions/GPGDetachedSignatureVerifier.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * This file is part of Arduino.
- *
- * Copyright 2015 Arduino LLC (http://www.arduino.cc/)
- *
- * Arduino is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * As a special exception, you may use this file as part of a free software
- * library without restriction.  Specifically, if other files instantiate
- * templates or use macros or inline functions from this file, or you compile
- * this file and link it with other files to produce an executable, this
- * file does not by itself cause the resulting executable to be covered by
- * the GNU General Public License.  This exception does not however
- * invalidate any other reasons why the executable file might be covered by
- * the GNU General Public License.
- */
-
-package cc.arduino.contributions;
-
-import org.apache.commons.compress.utils.IOUtils;
-import org.bouncycastle.openpgp.*;
-import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
-import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
-
-import java.io.*;
-import java.util.Iterator;
-
-public class GPGDetachedSignatureVerifier extends SignatureVerifier {
-
-  private String keyId;
-
-  public GPGDetachedSignatureVerifier() {
-    this("7F294291");
-  }
-
-  public GPGDetachedSignatureVerifier(String keyId) {
-    this.keyId = keyId;
-  }
-
-  @Override
-  protected boolean verify(File signedFile, File signature, File publicKey) throws IOException {
-    FileInputStream signatureInputStream = null;
-    FileInputStream signedFileInputStream = null;
-    try {
-      signatureInputStream = new FileInputStream(signature);
-      PGPObjectFactory pgpObjectFactory = new PGPObjectFactory(signatureInputStream, new BcKeyFingerprintCalculator());
-
-      Object nextObject;
-      try {
-        nextObject = pgpObjectFactory.nextObject();
-        if (!(nextObject instanceof PGPSignatureList)) {
-          return false;
-        }
-      } catch (IOException e) {
-        return false;
-      }
-      PGPSignatureList pgpSignatureList = (PGPSignatureList) nextObject;
-      assert pgpSignatureList.size() == 1;
-      PGPSignature pgpSignature = pgpSignatureList.get(0);
-
-      PGPPublicKey pgpPublicKey = readPublicKey(publicKey, keyId);
-
-      pgpSignature.init(new BcPGPContentVerifierBuilderProvider(), pgpPublicKey);
-      signedFileInputStream = new FileInputStream(signedFile);
-      pgpSignature.update(IOUtils.toByteArray(signedFileInputStream));
-
-      return pgpSignature.verify();
-    } catch (PGPException e) {
-      throw new IOException(e);
-    } finally {
-      IOUtils.closeQuietly(signatureInputStream);
-      IOUtils.closeQuietly(signedFileInputStream);
-    }
-  }
-
-  private PGPPublicKey readPublicKey(File file, String id) throws IOException, PGPException {
-    InputStream keyIn = null;
-    try {
-      keyIn = new BufferedInputStream(new FileInputStream(file));
-      return readPublicKey(keyIn, id);
-    } finally {
-      IOUtils.closeQuietly(keyIn);
-    }
-  }
-
-  private PGPPublicKey readPublicKey(InputStream input, String id) throws IOException, PGPException {
-    PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(input), new BcKeyFingerprintCalculator());
-
-    Iterator<PGPPublicKeyRing> keyRingIter = pgpPub.getKeyRings();
-    while (keyRingIter.hasNext()) {
-      PGPPublicKeyRing keyRing = keyRingIter.next();
-
-      Iterator<PGPPublicKey> keyIter = keyRing.getPublicKeys();
-      while (keyIter.hasNext()) {
-        PGPPublicKey key = keyIter.next();
-
-        if (Long.toHexString(key.getKeyID()).toUpperCase().endsWith(id)) {
-          return key;
-        }
-      }
-    }
-
-    throw new IllegalArgumentException("Can't find encryption key in key ring.");
-  }
-
-}
diff --git a/arduino-core/src/cc/arduino/contributions/SignatureVerifier.java b/arduino-core/src/cc/arduino/contributions/SignatureVerifier.java
index a4ea7a7ba53..940e38666c1 100644
--- a/arduino-core/src/cc/arduino/contributions/SignatureVerifier.java
+++ b/arduino-core/src/cc/arduino/contributions/SignatureVerifier.java
@@ -29,12 +29,38 @@
 
 package cc.arduino.contributions;
 
-import processing.app.BaseNoGui;
-
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.compress.utils.IOUtils;
+import org.bouncycastle.bcpg.ArmoredInputStream;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPObjectFactory;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
+import org.bouncycastle.openpgp.PGPSignature;
+import org.bouncycastle.openpgp.PGPSignatureList;
+import org.bouncycastle.openpgp.PGPUtil;
+import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
+import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
+import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
+
+import processing.app.BaseNoGui;
+
+public class SignatureVerifier {
+
+  private File keyRingFile;
+
+  public SignatureVerifier() {
+    keyRingFile = new File(BaseNoGui.getContentFile("lib"), "public.gpg.key");
+  }
 
-public abstract class SignatureVerifier {
+  public void setKeyRingFile(File keyRingFile) {
+    this.keyRingFile = keyRingFile;
+  }
 
   public boolean isSigned(File indexFile) {
     File signature = new File(indexFile.getParent(), indexFile.getName() + ".sig");
@@ -43,7 +69,7 @@ public boolean isSigned(File indexFile) {
     }
 
     try {
-      return verify(indexFile, signature, new File(BaseNoGui.getContentFile("lib"), "public.gpg.key"));
+      return verify(indexFile, signature);
     } catch (Exception e) {
       BaseNoGui.showWarning(e.getMessage(), e.getMessage(), e);
       return false;
@@ -52,13 +78,122 @@ public boolean isSigned(File indexFile) {
 
   public boolean isSigned(File indexFile, File signature) {
     try {
-      return verify(indexFile, signature, new File(BaseNoGui.getContentFile("lib"), "public.gpg.key"));
+      return verify(indexFile, signature);
     } catch (Exception e) {
       BaseNoGui.showWarning(e.getMessage(), e.getMessage(), e);
       return false;
     }
   }
 
-  protected abstract boolean verify(File signedFile, File signature, File publicKey) throws IOException;
+  protected boolean verify(File signedFile, File signatureFile) throws IOException {
+    try {
+      // Read signature from signatureFile
+      PGPSignature signature;
+      try (FileInputStream in = new FileInputStream(signatureFile)) {
+        PGPObjectFactory objFactory = new PGPObjectFactory(in, new BcKeyFingerprintCalculator());
+        Object obj = objFactory.nextObject();
+        if (!(obj instanceof PGPSignatureList)) {
+          return false;
+        }
+        PGPSignatureList signatureList = (PGPSignatureList) obj;
+        if (signatureList.size() != 1) {
+          return false;
+        }
+        signature = signatureList.get(0);
+      } catch (Exception e) {
+        return false;
+      }
+
+      // Extract public key from keyring
+      PGPPublicKey pgpPublicKey = readPublicKey(signature.getKeyID());
 
+      // Check signature
+      signature.init(new BcPGPContentVerifierBuilderProvider(), pgpPublicKey);
+      try (FileInputStream in = new FileInputStream(signedFile)) {
+        signature.update(IOUtils.toByteArray(in));
+        return signature.verify();
+      }
+    } catch (PGPException e) {
+      throw new IOException(e);
+    }
+  }
+
+  private PGPPublicKey readPublicKey(long id) throws IOException, PGPException {
+    try (InputStream in = PGPUtil.getDecoderStream(new FileInputStream(keyRingFile))) {
+      PGPPublicKeyRingCollection pubRing = new PGPPublicKeyRingCollection(in, new BcKeyFingerprintCalculator());
+
+      PGPPublicKey publicKey = pubRing.getPublicKey(id);
+      if (publicKey == null) {
+        throw new IllegalArgumentException("Can't find public key in key ring.");
+      }
+      return publicKey;
+    }
+  }
+
+  public String[] extractTextFromCleartextSignature(File inFile) throws FileNotFoundException, IOException {
+    try (ArmoredInputStream in = new ArmoredInputStream(new FileInputStream(inFile))) {
+      return extractTextFromCleartextSignature(in);
+    }
+  }
+
+  public boolean verifyCleartextSignature(File inFile) {
+    try (ArmoredInputStream in = new ArmoredInputStream(new FileInputStream(inFile))) {
+      String[] clearTextLines = extractTextFromCleartextSignature(in);
+      int clearTextSize = clearTextLines.length;
+
+      JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(in);
+      PGPSignatureList p3 = (PGPSignatureList) pgpFact.nextObject();
+      PGPSignature sig = p3.get(0);
+      PGPPublicKey publicKey = readPublicKey(sig.getKeyID());
+
+      sig.init(new BcPGPContentVerifierBuilderProvider(), publicKey);
+      for (int i = 0; i < clearTextSize; i++) {
+        sig.update(clearTextLines[i].getBytes());
+        if (i + 1 < clearTextSize) {
+          // https://tools.ietf.org/html/rfc4880#section-7
+          // Convert all line endings to '\r\n'
+          sig.update((byte) '\r');
+          sig.update((byte) '\n');
+        }
+      }
+      return sig.verify();
+    } catch (Exception e) {
+      e.printStackTrace();
+      return false;
+    }
+  }
+
+  private String[] extractTextFromCleartextSignature(ArmoredInputStream in) throws FileNotFoundException, IOException {
+    // https://tools.ietf.org/html/rfc4880#section-7
+    // ArmoredInputStream does unescape dash-escaped string in armored text and skips
+    // all headers. To calculate the signature we still need to:
+    // 1. handle different line endings \n or \n\r or \r\n
+    // 2. remove trailing whitespaces from each line (' ' and '\t')
+    // 3. remove the latest line ending
+
+    String clearText = "";
+    for (;;) {
+      int c = in.read();
+      // in.isClearText() refers to the PREVIOUS byte read
+      if (c == -1 || !in.isClearText()) {
+        break;
+      }
+      // 1. convert all line endings to '\r\n'
+      if (c == '\r') {
+        continue;
+      }
+      clearText += (char) c;
+    }
+
+    // 3. remove the latest line ending
+    if (clearText.endsWith("\n")) {
+      clearText = clearText.substring(0, clearText.length() - 1);
+    }
+    String[] lines = clearText.split("\n", -1);
+    for (int i = 0; i < lines.length; i++) {
+      // 2. remove trailing whitespaces from each line (' ' and '\t')
+      lines[i] = lines[i].replaceAll("[ \\t]+$", "");
+    }
+    return lines;
+  }
 }
diff --git a/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java b/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java
index 3f00f909b0d..858734fd88b 100644
--- a/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java
+++ b/arduino-core/src/cc/arduino/contributions/libraries/LibraryInstaller.java
@@ -31,7 +31,7 @@
 
 import cc.arduino.Constants;
 import cc.arduino.contributions.DownloadableContributionsDownloader;
-import cc.arduino.contributions.GPGDetachedSignatureVerifier;
+import cc.arduino.contributions.SignatureVerifier;
 import cc.arduino.contributions.GZippedJsonDownloader;
 import cc.arduino.contributions.ProgressListener;
 import cc.arduino.utils.ArchiveExtractor;
@@ -60,9 +60,9 @@ public class LibraryInstaller {
   private static Logger log = LogManager.getLogger(LibraryInstaller.class);
 
   private final Platform platform;
-  private final GPGDetachedSignatureVerifier signatureVerifier;
+  private final SignatureVerifier signatureVerifier;
 
-  public LibraryInstaller(Platform platform, GPGDetachedSignatureVerifier signatureVerifier) {
+  public LibraryInstaller(Platform platform, SignatureVerifier signatureVerifier) {
     this.platform = platform;
     this.signatureVerifier = signatureVerifier;
   }
diff --git a/arduino-core/src/cc/arduino/contributions/packages/ContributionInstaller.java b/arduino-core/src/cc/arduino/contributions/packages/ContributionInstaller.java
index 2b6ff4cdea8..a58ee552691 100644
--- a/arduino-core/src/cc/arduino/contributions/packages/ContributionInstaller.java
+++ b/arduino-core/src/cc/arduino/contributions/packages/ContributionInstaller.java
@@ -32,8 +32,8 @@
 import cc.arduino.Constants;
 import cc.arduino.contributions.DownloadableContribution;
 import cc.arduino.contributions.DownloadableContributionsDownloader;
-import cc.arduino.contributions.ProgressListener;
 import cc.arduino.contributions.SignatureVerifier;
+import cc.arduino.contributions.ProgressListener;
 import cc.arduino.filters.FileExecutablePredicate;
 import cc.arduino.utils.ArchiveExtractor;
 import cc.arduino.utils.MultiStepProgress;
diff --git a/arduino-core/src/processing/app/BaseNoGui.java b/arduino-core/src/processing/app/BaseNoGui.java
index c47a82d69b8..dc8bd7091a0 100644
--- a/arduino-core/src/processing/app/BaseNoGui.java
+++ b/arduino-core/src/processing/app/BaseNoGui.java
@@ -1,7 +1,7 @@
 package processing.app;
 
 import cc.arduino.Constants;
-import cc.arduino.contributions.GPGDetachedSignatureVerifier;
+import cc.arduino.contributions.SignatureVerifier;
 import cc.arduino.contributions.VersionComparator;
 import cc.arduino.contributions.libraries.LibrariesIndexer;
 import cc.arduino.contributions.packages.ContributedPlatform;
@@ -477,7 +477,7 @@ static public void initLogger() {
 
   static public void initPackages() throws Exception {
     indexer = new ContributionsIndexer(getSettingsFolder(), getHardwareFolder(), getPlatform(),
-        new GPGDetachedSignatureVerifier());
+        new SignatureVerifier());
 
     try {
       indexer.parseIndex();
diff --git a/arduino-core/src/processing/app/PreferencesData.java b/arduino-core/src/processing/app/PreferencesData.java
index 01f4568ad5b..162c4090224 100644
--- a/arduino-core/src/processing/app/PreferencesData.java
+++ b/arduino-core/src/processing/app/PreferencesData.java
@@ -282,4 +282,16 @@ public static boolean areInsecurePackagesAllowed() {
     }
     return getBoolean(Constants.PREF_CONTRIBUTIONS_TRUST_ALL, false);
   }
+
+  public static void setLong(String k, long v) {
+    set(k, String.valueOf(v));
+  }
+
+  public static Long getLong(String k) {
+    try {
+      return Long.parseLong(get(k));
+    } catch (NumberFormatException e) {
+      return null;
+    }
+  }
 }
diff --git a/build/shared/lib/public.gpg.key b/build/shared/lib/public.gpg.key
index 5de39fed1f7..fde092a92ac 100644
Binary files a/build/shared/lib/public.gpg.key and b/build/shared/lib/public.gpg.key differ