You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AesCipher.java 4.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2020 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.api.config.internal;
  21. import java.io.File;
  22. import java.io.IOException;
  23. import java.nio.charset.StandardCharsets;
  24. import java.security.Key;
  25. import java.security.SecureRandom;
  26. import javax.annotation.Nullable;
  27. import javax.crypto.KeyGenerator;
  28. import javax.crypto.SecretKey;
  29. import javax.crypto.spec.SecretKeySpec;
  30. import org.apache.commons.codec.binary.Base64;
  31. import org.apache.commons.io.FileUtils;
  32. import org.apache.commons.lang.StringUtils;
  33. import org.sonar.api.CoreProperties;
  34. import static java.nio.charset.StandardCharsets.UTF_8;
  35. final class AesCipher implements Cipher {
  36. static final int KEY_SIZE_IN_BITS = 256;
  37. private static final String CRYPTO_KEY = "AES";
  38. private String pathToSecretKey;
  39. AesCipher(@Nullable String pathToSecretKey) {
  40. this.pathToSecretKey = pathToSecretKey;
  41. }
  42. @Override
  43. public String encrypt(String clearText) {
  44. try {
  45. javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(CRYPTO_KEY);
  46. cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, loadSecretFile());
  47. return Base64.encodeBase64String(cipher.doFinal(clearText.getBytes(StandardCharsets.UTF_8.name())));
  48. } catch (RuntimeException e) {
  49. throw e;
  50. } catch (Exception e) {
  51. throw new IllegalStateException(e);
  52. }
  53. }
  54. @Override
  55. public String decrypt(String encryptedText) {
  56. try {
  57. javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(CRYPTO_KEY);
  58. cipher.init(javax.crypto.Cipher.DECRYPT_MODE, loadSecretFile());
  59. byte[] cipherData = cipher.doFinal(Base64.decodeBase64(StringUtils.trim(encryptedText)));
  60. return new String(cipherData, StandardCharsets.UTF_8);
  61. } catch (RuntimeException e) {
  62. throw e;
  63. } catch (Exception e) {
  64. throw new IllegalStateException(e);
  65. }
  66. }
  67. /**
  68. * This method checks the existence of the file, but not the validity of the contained key.
  69. */
  70. boolean hasSecretKey() {
  71. String path = getPathToSecretKey();
  72. if (StringUtils.isNotBlank(path)) {
  73. File file = new File(path);
  74. return file.exists() && file.isFile();
  75. }
  76. return false;
  77. }
  78. private Key loadSecretFile() throws IOException {
  79. String path = getPathToSecretKey();
  80. return loadSecretFileFromFile(path);
  81. }
  82. Key loadSecretFileFromFile(@Nullable String path) throws IOException {
  83. if (StringUtils.isBlank(path)) {
  84. throw new IllegalStateException("Secret key not found. Please set the property " + CoreProperties.ENCRYPTION_SECRET_KEY_PATH);
  85. }
  86. File file = new File(path);
  87. if (!file.exists() || !file.isFile()) {
  88. throw new IllegalStateException("The property " + CoreProperties.ENCRYPTION_SECRET_KEY_PATH + " does not link to a valid file: " + path);
  89. }
  90. String s = FileUtils.readFileToString(file, UTF_8);
  91. if (StringUtils.isBlank(s)) {
  92. throw new IllegalStateException("No secret key in the file: " + path);
  93. }
  94. return new SecretKeySpec(Base64.decodeBase64(StringUtils.trim(s)), CRYPTO_KEY);
  95. }
  96. String generateRandomSecretKey() {
  97. try {
  98. KeyGenerator keyGen = KeyGenerator.getInstance(CRYPTO_KEY);
  99. keyGen.init(KEY_SIZE_IN_BITS, new SecureRandom());
  100. SecretKey secretKey = keyGen.generateKey();
  101. return Base64.encodeBase64String(secretKey.getEncoded());
  102. } catch (Exception e) {
  103. throw new IllegalStateException("Fail to generate secret key", e);
  104. }
  105. }
  106. String getPathToSecretKey() {
  107. if (StringUtils.isBlank(pathToSecretKey)) {
  108. pathToSecretKey = new File(FileUtils.getUserDirectoryPath(), ".sonar/sonar-secret.txt").getPath();
  109. }
  110. return pathToSecretKey;
  111. }
  112. public void setPathToSecretKey(@Nullable String pathToSecretKey) {
  113. this.pathToSecretKey = pathToSecretKey;
  114. }
  115. }