aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-plugin-api
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@gmail.com>2012-03-13 16:06:51 +0100
committerSimon Brandhof <simon.brandhof@gmail.com>2012-03-13 16:06:51 +0100
commit896acd53cbdf4cb16536055a7d48bfcb4b613c3d (patch)
tree274b33b1846da9a916ea310c67a283e6a8cccb3f /sonar-plugin-api
parent1d631531e60d1897f1c0dc7f20e39ec9bf59c60f (diff)
downloadsonarqube-896acd53cbdf4cb16536055a7d48bfcb4b613c3d.tar.gz
sonarqube-896acd53cbdf4cb16536055a7d48bfcb4b613c3d.zip
SONAR-2084 use RSA to encrypt settings
Diffstat (limited to 'sonar-plugin-api')
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java25
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/config/Base64Cipher.java32
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/config/Cipher.java25
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/config/Encryption.java89
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/config/RsaCipher.java137
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java23
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/config/RsaCipherTest.java95
-rw-r--r--sonar-plugin-api/src/test/resources/org/sonar/api/config/RsaCipherTest/rsa_private_key.txt1
-rw-r--r--sonar-plugin-api/src/test/resources/org/sonar/api/config/RsaCipherTest/rsa_public_key.txt1
9 files changed, 418 insertions, 10 deletions
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
index 75d3620545a..32a67d2b529 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
@@ -22,12 +22,23 @@ package org.sonar.api;
/**
* CoreProperties is used to group various properties of Sonar as well
* as default values of configuration in a single place
- *
+ *
* @since 1.11
*/
public interface CoreProperties {
/**
+ * @since 2.15
+ */
+ String ENCRYPTION_PATH_TO_PRIVATE_KEY = "sonar.encryption.privateKeyPath";
+
+ /**
+ * @since 2.15
+ */
+ String ENCRYPTION_PUBLIC_KEY = "sonar.encryption.publicKey";
+
+
+ /**
* @since 2.11
*/
String CATEGORY_GENERAL = "general";
@@ -85,7 +96,7 @@ public interface CoreProperties {
/**
* To determine value of this property use {@link org.sonar.api.resources.ProjectFileSystem#getSourceCharset()}.
- *
+ *
* @since 2.6
*/
String ENCODING_PROPERTY = "sonar.sourceEncoding";
@@ -148,8 +159,8 @@ public interface CoreProperties {
String SERVER_BASE_URL = "sonar.core.serverBaseURL";
/**
- * @since 2.10
* @see #SERVER_BASE_URL
+ * @since 2.10
*/
String SERVER_BASE_URL_DEFAULT_VALUE = "http://localhost:9000";
@@ -169,8 +180,8 @@ public interface CoreProperties {
String CPD_ENGINE = "sonar.cpd.engine";
/**
- * @since 2.11
* @see #CPD_ENGINE
+ * @since 2.11
*/
String CPD_ENGINE_DEFAULT_VALUE = "sonar";
@@ -180,8 +191,8 @@ public interface CoreProperties {
String CPD_CROSS_RPOJECT = "sonar.cpd.cross_project";
/**
- * @since 2.11
* @see #CPD_CROSS_RPOJECT
+ * @since 2.11
*/
boolean CPD_CROSS_RPOJECT_DEFAULT_VALUE = false;
@@ -189,7 +200,7 @@ public interface CoreProperties {
/**
* Indicates whether Java bytecode analysis should be skipped.
- *
+ *
* @since 2.0
*/
String DESIGN_SKIP_DESIGN_PROPERTY = "sonar.skipDesign";
@@ -197,7 +208,7 @@ public interface CoreProperties {
/**
* Indicates whether Package Design Analysis should be skipped.
- *
+ *
* @since 2.9
*/
String DESIGN_SKIP_PACKAGE_DESIGN_PROPERTY = "sonar.skipPackageDesign";
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/Base64Cipher.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/Base64Cipher.java
new file mode 100644
index 00000000000..a04b40e953b
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/Base64Cipher.java
@@ -0,0 +1,32 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.api.config;
+
+import org.apache.commons.codec.binary.Base64;
+
+final class Base64Cipher extends Cipher {
+ String encrypt(String clearText) {
+ return new String(Base64.encodeBase64(clearText.getBytes()));
+ }
+
+ String decrypt(String encryptedText) {
+ return new String(Base64.decodeBase64(encryptedText));
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/Cipher.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/Cipher.java
new file mode 100644
index 00000000000..7a39c7c7653
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/Cipher.java
@@ -0,0 +1,25 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.api.config;
+
+abstract class Cipher {
+ abstract String encrypt(String clearText);
+ abstract String decrypt(String encryptedText);
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/Encryption.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/Encryption.java
new file mode 100644
index 00000000000..edc7dfab7b8
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/Encryption.java
@@ -0,0 +1,89 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.api.config;
+
+import com.google.common.collect.ImmutableMap;
+
+import java.util.Locale;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @since 2.15
+ */
+public final class Encryption {
+
+ private static final String BASE64_ALGORITHM = "b64";
+ private final Base64Cipher base64Encryption;
+
+ private static final String RSA_ALGORITHM = "rsa";
+ private final RsaCipher rsaEncryption;
+
+ private final Map<String, Cipher> encryptions;
+ private static final Pattern ENCRYPTED_PATTERN = Pattern.compile("\\{(.*?)\\}(.*)");
+
+ Encryption(Settings settings) {
+ base64Encryption = new Base64Cipher();
+ rsaEncryption = new RsaCipher(settings);
+ encryptions = ImmutableMap.of(
+ BASE64_ALGORITHM, base64Encryption,
+ RSA_ALGORITHM, rsaEncryption
+ );
+ }
+
+ public boolean isEncrypted(String value) {
+ return value.startsWith("{") && value.indexOf("}") > 1;
+ }
+
+ public String encrypt(String clearText) {
+ return encrypt(RSA_ALGORITHM, clearText);
+ }
+
+ public String scramble(String clearText) {
+ return encrypt(BASE64_ALGORITHM, clearText);
+ }
+
+ /**
+ * @return an array of 2 strings: {public key, private key}
+ */
+ public String[] generateRandomKeys() {
+ return rsaEncryption.generateRandomKeys();
+ }
+
+ public String decrypt(String encryptedText) {
+ Matcher matcher = ENCRYPTED_PATTERN.matcher(encryptedText);
+ if (matcher.matches()) {
+ Cipher cipher = encryptions.get(matcher.group(0).toLowerCase(Locale.ENGLISH));
+ if (cipher != null) {
+ return cipher.decrypt(matcher.group(1));
+ }
+ }
+ return encryptedText;
+ }
+
+ private String encrypt(String algorithm, String clearText) {
+ Cipher cipher = encryptions.get(algorithm);
+ if (cipher == null) {
+ throw new IllegalArgumentException("Unknown cipher algorithm: " + algorithm);
+ }
+ return String.format("{%s}%s", algorithm, cipher.encrypt(clearText));
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/RsaCipher.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/RsaCipher.java
new file mode 100644
index 00000000000..3e96c096c12
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/RsaCipher.java
@@ -0,0 +1,137 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.api.config;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Throwables;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.CoreProperties;
+
+import java.io.File;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.*;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPrivateKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+
+final class RsaCipher extends Cipher {
+
+ private final Settings settings;
+
+ RsaCipher(Settings settings) {
+ this.settings = settings;
+ }
+
+ String encrypt(String clearText) {
+ String publicKey = settings.getClearString(CoreProperties.ENCRYPTION_PUBLIC_KEY);
+ if (StringUtils.isBlank(publicKey)) {
+ throw new IllegalStateException("RSA public key is missing. Please generate one.");
+ }
+ return encrypt(clearText, publicKey);
+ }
+
+ private String encrypt(String clearText, String publicKey) {
+ try {
+ javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("RSA");
+ cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, toPublicKey(publicKey));
+ return new String(Base64.encodeBase64(cipher.doFinal(clearText.getBytes())));
+ } catch (Exception e) {
+ throw Throwables.propagate(e);
+ }
+ }
+
+ String decrypt(String encryptedText) {
+ try {
+ PrivateKey privateKey = loadPrivateKey();
+ javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("RSA");
+ cipher.init(javax.crypto.Cipher.DECRYPT_MODE, privateKey);
+ byte[] cipherData = cipher.doFinal(Base64.decodeBase64(StringUtils.trim(encryptedText)));
+ return new String(cipherData);
+ } catch (Exception e) {
+ throw Throwables.propagate(e);
+ }
+ }
+
+ private PrivateKey loadPrivateKey() throws NoSuchAlgorithmException, IOException, InvalidKeySpecException, InvalidKeyException {
+ String path = settings.getClearString(CoreProperties.ENCRYPTION_PATH_TO_PRIVATE_KEY);
+ return loadPrivateKeyFromFile(path);
+ }
+
+ @VisibleForTesting
+ PrivateKey loadPrivateKeyFromFile(String path) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, InvalidKeyException {
+ if (StringUtils.isBlank(path)) {
+ throw new IllegalStateException("Impossible to decrypt text without the private key. Please set the property " + CoreProperties.ENCRYPTION_PATH_TO_PRIVATE_KEY);
+ }
+ File file = new File(path);
+ if (!file.exists() || !file.isFile()) {
+ throw new IllegalStateException("The property " + CoreProperties.ENCRYPTION_PATH_TO_PRIVATE_KEY + " does not link to a valid file: " + path);
+ }
+
+ String s = FileUtils.readFileToString(file);
+ if (StringUtils.isBlank(s)) {
+ throw new IllegalStateException("No private key in the file: " + path);
+ }
+ String[] fields = StringUtils.split(StringUtils.trim(s), ",");
+ if (fields.length != 2) {
+ throw new IllegalStateException("Badly formatted private key in the file: " + path);
+ }
+ BigInteger modulus = new BigInteger(fields[0]);
+ BigInteger exponent = new BigInteger(fields[1]);
+ KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+ return keyFactory.generatePrivate(new RSAPrivateKeySpec(modulus, exponent));
+ }
+
+ @VisibleForTesting
+ PublicKey toPublicKey(String text) throws InvalidKeySpecException, NoSuchAlgorithmException {
+ if (StringUtils.isBlank(text)) {
+ throw new IllegalArgumentException("The public key is blank");
+ }
+ String[] fields = StringUtils.split(StringUtils.trim(text), ",");
+ if (fields.length != 2) {
+ throw new IllegalStateException("Unknown format of public key: " + text);
+ }
+ BigInteger modulus = new BigInteger(fields[0]);
+ BigInteger exponent = new BigInteger(fields[1]);
+ KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+ return keyFactory.generatePublic(new RSAPublicKeySpec(modulus, exponent));
+ }
+
+ String[] generateRandomKeys() {
+ try {
+ KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
+ gen.initialize(1024, new SecureRandom());
+ KeyPair pair = gen.generateKeyPair();
+
+ KeyFactory fact = KeyFactory.getInstance("RSA");
+ RSAPublicKeySpec pub = fact.getKeySpec(pair.getPublic(), RSAPublicKeySpec.class);
+ RSAPrivateKeySpec priv = fact.getKeySpec(pair.getPrivate(), RSAPrivateKeySpec.class);
+
+ String publicKey = pub.getModulus() + "," + pub.getPublicExponent();
+ String privateKey = priv.getModulus() + "," + priv.getPrivateExponent();
+ return new String[]{publicKey, privateKey};
+
+ } catch (Exception e) {
+ throw new IllegalStateException("Fail to generate random RSA keys", e);
+ }
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java b/sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java
index 755345be3f5..8de950ac7f5 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/config/Settings.java
@@ -38,15 +38,18 @@ import java.util.*;
*/
public class Settings implements BatchComponent, ServerComponent {
- protected Map<String, String> properties = Maps.newHashMap();
- protected PropertyDefinitions definitions;
+ protected final Map<String, String> properties;
+ protected final PropertyDefinitions definitions;
+ private final Encryption encryption;
public Settings() {
this(new PropertyDefinitions());
}
public Settings(PropertyDefinitions definitions) {
+ this.properties = Maps.newHashMap();
this.definitions = definitions;
+ this.encryption = new Encryption(this);
}
public final String getDefaultValue(String key) {
@@ -65,6 +68,19 @@ public class Settings implements BatchComponent, ServerComponent {
String value = properties.get(key);
if (value == null) {
value = getDefaultValue(key);
+ } else if (encryption.isEncrypted(value)) {
+ value = encryption.decrypt(value);
+ }
+ return value;
+ }
+
+ /**
+ * Does not decrypt value.
+ */
+ protected String getClearString(String key) {
+ String value = properties.get(key);
+ if (value == null) {
+ value = getDefaultValue(key);
}
return value;
}
@@ -217,7 +233,8 @@ public class Settings implements BatchComponent, ServerComponent {
}
public final Settings setProperties(Map<String, String> props) {
- properties = Maps.newHashMap(props);
+ properties.clear();
+ properties.putAll(props);
return this;
}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/config/RsaCipherTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/config/RsaCipherTest.java
new file mode 100644
index 00000000000..2bd4a538c45
--- /dev/null
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/config/RsaCipherTest.java
@@ -0,0 +1,95 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.api.config;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+
+public class RsaCipherTest {
+ @Test
+ public void encrypt() throws IOException {
+ Settings settings = new Settings();
+ settings.setProperty("sonar.encryption.publicKey", loadPublicKey());
+ RsaCipher cipher = new RsaCipher(settings);
+ String encryptedText = cipher.encrypt("sonar");
+ System.out.println(encryptedText);
+ assertThat(StringUtils.isNotBlank(encryptedText), is(true));
+ assertThat(Base64.isArrayByteBase64(encryptedText.getBytes()), is(true));
+ }
+
+ @Test
+ public void decrypt() throws URISyntaxException, IOException {
+ File file = new File(getClass().getResource("/org/sonar/api/config/RsaCipherTest/rsa_private_key.txt").toURI());
+ Settings settings = new Settings();
+ settings.setProperty("sonar.encryption.privateKeyPath", file.getCanonicalPath());
+ RsaCipher cipher = new RsaCipher(settings);
+
+ // the following value has been encrypted with the public key /org/sonar/api/config/RsaCipherTest/rsa_public_key.txt
+ String clearText = cipher.decrypt("bnFlXnB5A8kLV4VR1FSGI4BmKd9I1E7euOQq/yB8a8RIpW34YYQX0toM5GTymY5EwkMO+KvfcpKXIvvhthr+5beW8v2nDux8n3VSH+tb+3wJZ+UYZQBQAQj2G8FVvYxbvRk3WVGn9bpw3x6195/gEneGvcG/A41/YsDHDce9zLw=");
+ assertThat(clearText, is("this is a secret"));
+ }
+
+ @Test
+ public void encryptThenDecrypt() throws URISyntaxException, IOException {
+ File file = new File(getClass().getResource("/org/sonar/api/config/RsaCipherTest/rsa_private_key.txt").toURI());
+ Settings settings = new Settings();
+ settings.setProperty("sonar.encryption.publicKey", loadPublicKey());
+ settings.setProperty("sonar.encryption.privateKeyPath", file.getCanonicalPath());
+ RsaCipher cipher = new RsaCipher(settings);
+
+ assertThat(cipher.decrypt(cipher.encrypt("foo")), is("foo"));
+ }
+
+ @Test
+ public void loadPrivateKeyFromFile() throws Exception {
+ File file = new File(getClass().getResource("/org/sonar/api/config/RsaCipherTest/rsa_private_key.txt").toURI());
+ RsaCipher cipher = new RsaCipher(new Settings());
+ PrivateKey privateKey = cipher.loadPrivateKeyFromFile(file.getPath());
+ assertThat(privateKey.getAlgorithm(), is("RSA"));
+ }
+
+ @Test
+ public void toPublicKey() throws Exception {
+ RsaCipher cipher = new RsaCipher(new Settings());
+ PublicKey publicKey = cipher.toPublicKey(loadPublicKey());
+ assertThat(publicKey.getAlgorithm(), is("RSA"));
+ }
+
+ private String loadPublicKey() throws IOException {
+ InputStream input = getClass().getResourceAsStream("/org/sonar/api/config/RsaCipherTest/rsa_public_key.txt");
+ try {
+ return IOUtils.toString(input);
+ } finally {
+ IOUtils.closeQuietly(input);
+ }
+ }
+}
diff --git a/sonar-plugin-api/src/test/resources/org/sonar/api/config/RsaCipherTest/rsa_private_key.txt b/sonar-plugin-api/src/test/resources/org/sonar/api/config/RsaCipherTest/rsa_private_key.txt
new file mode 100644
index 00000000000..10be545d264
--- /dev/null
+++ b/sonar-plugin-api/src/test/resources/org/sonar/api/config/RsaCipherTest/rsa_private_key.txt
@@ -0,0 +1 @@
+90219154459460484635307049294251309350624400174513872842964935995590426792849850754956692979878580134173903984923579664828287537160023584656524734039928278121145700539672753499137027823143447317638477535928797385199093031615075304372662494208460458746505946857452591645907526128623572362647338861106567346733,28487650981645105345729039749992166191644740180529930949117542540744133726768616377360480408404524731015987443184896433830608393640073974409602558039310242973125348929164889362700042142142217737063061860660679199646988663544428331146992861271726257946205621825278746735752228856292372558114153659087908459073 \ No newline at end of file
diff --git a/sonar-plugin-api/src/test/resources/org/sonar/api/config/RsaCipherTest/rsa_public_key.txt b/sonar-plugin-api/src/test/resources/org/sonar/api/config/RsaCipherTest/rsa_public_key.txt
new file mode 100644
index 00000000000..ef746000bcc
--- /dev/null
+++ b/sonar-plugin-api/src/test/resources/org/sonar/api/config/RsaCipherTest/rsa_public_key.txt
@@ -0,0 +1 @@
+90219154459460484635307049294251309350624400174513872842964935995590426792849850754956692979878580134173903984923579664828287537160023584656524734039928278121145700539672753499137027823143447317638477535928797385199093031615075304372662494208460458746505946857452591645907526128623572362647338861106567346733,65537 \ No newline at end of file