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.

WalkEncryption.java 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. /*
  2. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.transport;
  44. import java.io.IOException;
  45. import java.io.InputStream;
  46. import java.io.OutputStream;
  47. import java.net.HttpURLConnection;
  48. import java.security.AlgorithmParameters;
  49. import java.security.GeneralSecurityException;
  50. import java.security.spec.AlgorithmParameterSpec;
  51. import java.security.spec.KeySpec;
  52. import java.text.MessageFormat;
  53. import java.util.Properties;
  54. import java.util.regex.Matcher;
  55. import java.util.regex.Pattern;
  56. import javax.crypto.Cipher;
  57. import javax.crypto.CipherInputStream;
  58. import javax.crypto.CipherOutputStream;
  59. import javax.crypto.SecretKey;
  60. import javax.crypto.SecretKeyFactory;
  61. import javax.crypto.spec.IvParameterSpec;
  62. import javax.crypto.spec.PBEKeySpec;
  63. import javax.crypto.spec.PBEParameterSpec;
  64. import javax.crypto.spec.SecretKeySpec;
  65. import javax.xml.bind.DatatypeConverter;
  66. import org.eclipse.jgit.internal.JGitText;
  67. import org.eclipse.jgit.util.Base64;
  68. abstract class WalkEncryption {
  69. static final WalkEncryption NONE = new NoEncryption();
  70. static final String JETS3T_CRYPTO_VER = "jets3t-crypto-ver"; //$NON-NLS-1$
  71. static final String JETS3T_CRYPTO_ALG = "jets3t-crypto-alg"; //$NON-NLS-1$
  72. // Note: encrypt -> request state machine, step 1.
  73. abstract OutputStream encrypt(OutputStream output) throws IOException;
  74. // Note: encrypt -> request state machine, step 2.
  75. abstract void request(HttpURLConnection conn, String prefix) throws IOException;
  76. // Note: validate -> decrypt state machine, step 1.
  77. abstract void validate(HttpURLConnection conn, String prefix) throws IOException;
  78. // Note: validate -> decrypt state machine, step 2.
  79. abstract InputStream decrypt(InputStream input) throws IOException;
  80. // TODO mixed ciphers
  81. // consider permitting mixed ciphers to facilitate algorithm migration
  82. // i.e. user keeps the password, but changes the algorithm
  83. // then existing remote entries will still be readable
  84. protected void validateImpl(final HttpURLConnection u, final String prefix,
  85. final String version, final String name) throws IOException {
  86. String v;
  87. v = u.getHeaderField(prefix + JETS3T_CRYPTO_VER);
  88. if (v == null)
  89. v = ""; //$NON-NLS-1$
  90. if (!version.equals(v))
  91. throw new IOException(MessageFormat.format(JGitText.get().unsupportedEncryptionVersion, v));
  92. v = u.getHeaderField(prefix + JETS3T_CRYPTO_ALG);
  93. if (v == null)
  94. v = ""; //$NON-NLS-1$
  95. // Standard names are not case-sensitive.
  96. // http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html
  97. if (!name.equalsIgnoreCase(v))
  98. throw new IOException(MessageFormat.format(JGitText.get().unsupportedEncryptionAlgorithm, v));
  99. }
  100. IOException error(final Throwable why) {
  101. final IOException e;
  102. e = new IOException(MessageFormat.format(JGitText.get().encryptionError, why.getMessage()));
  103. e.initCause(why);
  104. return e;
  105. }
  106. private static class NoEncryption extends WalkEncryption {
  107. @Override
  108. void request(HttpURLConnection u, String prefix) {
  109. // Don't store any request properties.
  110. }
  111. @Override
  112. void validate(final HttpURLConnection u, final String prefix)
  113. throws IOException {
  114. validateImpl(u, prefix, "", ""); //$NON-NLS-1$ //$NON-NLS-2$
  115. }
  116. @Override
  117. InputStream decrypt(InputStream in) {
  118. return in;
  119. }
  120. @Override
  121. OutputStream encrypt(OutputStream os) {
  122. return os;
  123. }
  124. }
  125. // PBEParameterSpec factory for Java (version <= 7).
  126. // Does not support AlgorithmParameterSpec.
  127. static PBEParameterSpec java7PBEParameterSpec(byte[] salt,
  128. int iterationCount) {
  129. return new PBEParameterSpec(salt, iterationCount);
  130. }
  131. // PBEParameterSpec factory for Java (version >= 8).
  132. // Adds support for AlgorithmParameterSpec.
  133. static PBEParameterSpec java8PBEParameterSpec(byte[] salt,
  134. int iterationCount, AlgorithmParameterSpec paramSpec) {
  135. try {
  136. @SuppressWarnings("boxing")
  137. PBEParameterSpec instance = PBEParameterSpec.class
  138. .getConstructor(byte[].class, int.class,
  139. AlgorithmParameterSpec.class)
  140. .newInstance(salt, iterationCount, paramSpec);
  141. return instance;
  142. } catch (Exception e) {
  143. throw new RuntimeException(e);
  144. }
  145. }
  146. // Current runtime version.
  147. // https://docs.oracle.com/javase/7/docs/technotes/guides/versioning/spec/versioning2.html
  148. static double javaVersion() {
  149. return Double.parseDouble(System.getProperty("java.specification.version")); //$NON-NLS-1$
  150. }
  151. /**
  152. * JetS3t compatibility reference: <a href=
  153. * "https://bitbucket.org/jmurty/jets3t/src/156c00eb160598c2e9937fd6873f00d3190e28ca/src/org/jets3t/service/security/EncryptionUtil.java">
  154. * EncryptionUtil.java</a>
  155. * <p>
  156. * Note: EncryptionUtil is inadequate:
  157. * <li>EncryptionUtil.isCipherAvailableForUse checks encryption only which
  158. * "always works", but in JetS3t both encryption and decryption use non-IV
  159. * aware algorithm parameters for all PBE specs, which breaks in case of AES
  160. * <li>that means that only non-IV algorithms will work round trip in
  161. * JetS3t, such as PBEWithMD5AndDES and PBEWithSHAAndTwofish-CBC
  162. * <li>any AES based algorithms such as "PBE...With...And...AES" will not
  163. * work, since they need proper IV setup
  164. */
  165. static class JetS3tV2 extends WalkEncryption {
  166. static final String VERSION = "2"; //$NON-NLS-1$
  167. static final String ALGORITHM = "PBEWithMD5AndDES"; //$NON-NLS-1$
  168. static final int ITERATIONS = 5000;
  169. static final int KEY_SIZE = 32;
  170. static final byte[] SALT = { //
  171. (byte) 0xA4, (byte) 0x0B, (byte) 0xC8, (byte) 0x34, //
  172. (byte) 0xD6, (byte) 0x95, (byte) 0xF3, (byte) 0x13 //
  173. };
  174. // Size 16, see com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE
  175. static final byte[] ZERO_AES_IV = new byte[16];
  176. private static final String cryptoVer = VERSION;
  177. private final String cryptoAlg;
  178. private final SecretKey secretKey;
  179. private final AlgorithmParameterSpec paramSpec;
  180. JetS3tV2(final String algo, final String key)
  181. throws GeneralSecurityException {
  182. cryptoAlg = algo;
  183. // Verify if cipher is present.
  184. Cipher cipher = Cipher.getInstance(cryptoAlg);
  185. // Standard names are not case-sensitive.
  186. // http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html
  187. String cryptoName = cryptoAlg.toUpperCase();
  188. if (!cryptoName.startsWith("PBE")) //$NON-NLS-1$
  189. throw new GeneralSecurityException(JGitText.get().encryptionOnlyPBE);
  190. PBEKeySpec keySpec = new PBEKeySpec(key.toCharArray(), SALT, ITERATIONS, KEY_SIZE);
  191. secretKey = SecretKeyFactory.getInstance(algo).generateSecret(keySpec);
  192. // Detect algorithms which require initialization vector.
  193. boolean useIV = cryptoName.contains("AES"); //$NON-NLS-1$
  194. // PBEParameterSpec algorithm parameters are supported from Java 8.
  195. boolean isJava8 = javaVersion() >= 1.8;
  196. if (useIV && isJava8) {
  197. // Support IV where possible:
  198. // * since JCE provider uses random IV for PBE/AES
  199. // * and there is no place to store dynamic IV in JetS3t V2
  200. // * we use static IV, and tolerate increased security risk
  201. // TODO back port this change to JetS3t V2
  202. // See:
  203. // https://bitbucket.org/jmurty/jets3t/raw/156c00eb160598c2e9937fd6873f00d3190e28ca/src/org/jets3t/service/security/EncryptionUtil.java
  204. // http://cr.openjdk.java.net/~mullan/webrevs/ascarpin/webrev.00/raw_files/new/src/share/classes/com/sun/crypto/provider/PBES2Core.java
  205. IvParameterSpec paramIV = new IvParameterSpec(ZERO_AES_IV);
  206. paramSpec = java8PBEParameterSpec(SALT, ITERATIONS, paramIV);
  207. } else {
  208. // Strict legacy JetS3t V2 compatibility, with no IV support.
  209. paramSpec = java7PBEParameterSpec(SALT, ITERATIONS);
  210. }
  211. // Verify if cipher + key are allowed by policy.
  212. cipher.init(Cipher.ENCRYPT_MODE, secretKey, paramSpec);
  213. cipher.doFinal();
  214. }
  215. @Override
  216. void request(final HttpURLConnection u, final String prefix) {
  217. u.setRequestProperty(prefix + JETS3T_CRYPTO_VER, cryptoVer);
  218. u.setRequestProperty(prefix + JETS3T_CRYPTO_ALG, cryptoAlg);
  219. }
  220. @Override
  221. void validate(final HttpURLConnection u, final String prefix)
  222. throws IOException {
  223. validateImpl(u, prefix, cryptoVer, cryptoAlg);
  224. }
  225. @Override
  226. OutputStream encrypt(final OutputStream os) throws IOException {
  227. try {
  228. final Cipher cipher = Cipher.getInstance(cryptoAlg);
  229. cipher.init(Cipher.ENCRYPT_MODE, secretKey, paramSpec);
  230. return new CipherOutputStream(os, cipher);
  231. } catch (GeneralSecurityException e) {
  232. throw error(e);
  233. }
  234. }
  235. @Override
  236. InputStream decrypt(final InputStream in) throws IOException {
  237. try {
  238. final Cipher cipher = Cipher.getInstance(cryptoAlg);
  239. cipher.init(Cipher.DECRYPT_MODE, secretKey, paramSpec);
  240. return new CipherInputStream(in, cipher);
  241. } catch (GeneralSecurityException e) {
  242. throw error(e);
  243. }
  244. }
  245. }
  246. /** Encryption property names. */
  247. interface Keys {
  248. // Remote S3 meta: V1 algorithm name or V2 profile name.
  249. String JGIT_PROFILE = "jgit-crypto-profile"; //$NON-NLS-1$
  250. // Remote S3 meta: JGit encryption implementation version.
  251. String JGIT_VERSION = "jgit-crypto-version"; //$NON-NLS-1$
  252. // Remote S3 meta: base-64 encoded cipher algorithm parameters.
  253. String JGIT_CONTEXT = "jgit-crypto-context"; //$NON-NLS-1$
  254. // Amazon S3 connection configuration file profile property suffixes:
  255. String X_ALGO = ".algo"; //$NON-NLS-1$
  256. String X_KEY_ALGO = ".key.algo"; //$NON-NLS-1$
  257. String X_KEY_SIZE = ".key.size"; //$NON-NLS-1$
  258. String X_KEY_ITER = ".key.iter"; //$NON-NLS-1$
  259. String X_KEY_SALT = ".key.salt"; //$NON-NLS-1$
  260. }
  261. /** Encryption constants and defaults. */
  262. interface Vals {
  263. // Compatibility defaults.
  264. String DEFAULT_VERS = "0"; //$NON-NLS-1$
  265. String DEFAULT_ALGO = JetS3tV2.ALGORITHM;
  266. String DEFAULT_KEY_ALGO = JetS3tV2.ALGORITHM;
  267. String DEFAULT_KEY_SIZE = Integer.toString(JetS3tV2.KEY_SIZE);
  268. String DEFAULT_KEY_ITER = Integer.toString(JetS3tV2.ITERATIONS);
  269. String DEFAULT_KEY_SALT = DatatypeConverter.printHexBinary(JetS3tV2.SALT);
  270. String EMPTY = ""; //$NON-NLS-1$
  271. // Match white space.
  272. String REGEX_WS = "\\s+"; //$NON-NLS-1$
  273. // Match PBE ciphers, i.e: PBEWithMD5AndDES
  274. String REGEX_PBE = "(PBE).*(WITH).+(AND).+"; //$NON-NLS-1$
  275. // Match transformation ciphers, i.e: AES/CBC/PKCS5Padding
  276. String REGEX_TRANS = "(.+)/(.+)/(.+)"; //$NON-NLS-1$
  277. }
  278. static GeneralSecurityException securityError(String message) {
  279. return new GeneralSecurityException(
  280. MessageFormat.format(JGitText.get().encryptionError, message));
  281. }
  282. /**
  283. * Base implementation of JGit symmetric encryption. Supports V2 properties
  284. * format.
  285. */
  286. static abstract class SymmetricEncryption extends WalkEncryption
  287. implements Keys, Vals {
  288. /** Encryption profile, root name of group of related properties. */
  289. final String profile;
  290. /** Encryption version, reflects actual implementation class. */
  291. final String version;
  292. /** Full cipher algorithm name. */
  293. final String cipherAlgo;
  294. /** Cipher algorithm name for parameters lookup. */
  295. final String paramsAlgo;
  296. /** Generated secret key. */
  297. final SecretKey secretKey;
  298. SymmetricEncryption(Properties props) throws GeneralSecurityException {
  299. profile = props.getProperty(AmazonS3.Keys.CRYPTO_ALG);
  300. version = props.getProperty(AmazonS3.Keys.CRYPTO_VER);
  301. String pass = props.getProperty(AmazonS3.Keys.PASSWORD);
  302. cipherAlgo = props.getProperty(profile + X_ALGO, DEFAULT_ALGO);
  303. String keyAlgo = props.getProperty(profile + X_KEY_ALGO, DEFAULT_KEY_ALGO);
  304. String keySize = props.getProperty(profile + X_KEY_SIZE, DEFAULT_KEY_SIZE);
  305. String keyIter = props.getProperty(profile + X_KEY_ITER, DEFAULT_KEY_ITER);
  306. String keySalt = props.getProperty(profile + X_KEY_SALT, DEFAULT_KEY_SALT);
  307. // Verify if cipher is present.
  308. Cipher cipher = Cipher.getInstance(cipherAlgo);
  309. // Verify if key factory is present.
  310. SecretKeyFactory factory = SecretKeyFactory.getInstance(keyAlgo);
  311. final int size;
  312. try {
  313. size = Integer.parseInt(keySize);
  314. } catch (Exception e) {
  315. throw securityError(X_KEY_SIZE + EMPTY + keySize);
  316. }
  317. final int iter;
  318. try {
  319. iter = Integer.parseInt(keyIter);
  320. } catch (Exception e) {
  321. throw securityError(X_KEY_ITER + EMPTY + keyIter);
  322. }
  323. final byte[] salt;
  324. try {
  325. salt = DatatypeConverter
  326. .parseHexBinary(keySalt.replaceAll(REGEX_WS, EMPTY));
  327. } catch (Exception e) {
  328. throw securityError(X_KEY_SALT + EMPTY + keySalt);
  329. }
  330. KeySpec keySpec = new PBEKeySpec(pass.toCharArray(), salt, iter, size);
  331. SecretKey keyBase = factory.generateSecret(keySpec);
  332. String name = cipherAlgo.toUpperCase();
  333. Matcher matcherPBE = Pattern.compile(REGEX_PBE).matcher(name);
  334. Matcher matcherTrans = Pattern.compile(REGEX_TRANS).matcher(name);
  335. if (matcherPBE.matches()) {
  336. paramsAlgo = cipherAlgo;
  337. secretKey = keyBase;
  338. } else if (matcherTrans.find()) {
  339. paramsAlgo = matcherTrans.group(1);
  340. secretKey = new SecretKeySpec(keyBase.getEncoded(), paramsAlgo);
  341. } else {
  342. throw new GeneralSecurityException(MessageFormat.format(
  343. JGitText.get().unsupportedEncryptionAlgorithm,
  344. cipherAlgo));
  345. }
  346. // Verify if cipher + key are allowed by policy.
  347. cipher.init(Cipher.ENCRYPT_MODE, secretKey);
  348. cipher.doFinal();
  349. }
  350. // Shared state encrypt -> request.
  351. volatile String context;
  352. @Override
  353. OutputStream encrypt(OutputStream output) throws IOException {
  354. try {
  355. Cipher cipher = Cipher.getInstance(cipherAlgo);
  356. cipher.init(Cipher.ENCRYPT_MODE, secretKey);
  357. AlgorithmParameters params = cipher.getParameters();
  358. if (params == null) {
  359. context = EMPTY;
  360. } else {
  361. context = Base64.encodeBytes(params.getEncoded());
  362. }
  363. return new CipherOutputStream(output, cipher);
  364. } catch (Exception e) {
  365. throw error(e);
  366. }
  367. }
  368. @Override
  369. void request(HttpURLConnection conn, String prefix) throws IOException {
  370. conn.setRequestProperty(prefix + JGIT_PROFILE, profile);
  371. conn.setRequestProperty(prefix + JGIT_VERSION, version);
  372. conn.setRequestProperty(prefix + JGIT_CONTEXT, context);
  373. // No cleanup:
  374. // single encrypt can be followed by several request
  375. // from the AmazonS3.putImpl() multiple retry attempts
  376. // context = null; // Cleanup encrypt -> request transition.
  377. // TODO re-factor AmazonS3.putImpl to be more transaction-like
  378. }
  379. // Shared state validate -> decrypt.
  380. volatile Cipher decryptCipher;
  381. @Override
  382. void validate(HttpURLConnection conn, String prefix)
  383. throws IOException {
  384. String prof = conn.getHeaderField(prefix + JGIT_PROFILE);
  385. String vers = conn.getHeaderField(prefix + JGIT_VERSION);
  386. String cont = conn.getHeaderField(prefix + JGIT_CONTEXT);
  387. if (prof == null) {
  388. throw new IOException(MessageFormat
  389. .format(JGitText.get().encryptionError, JGIT_PROFILE));
  390. }
  391. if (vers == null) {
  392. throw new IOException(MessageFormat
  393. .format(JGitText.get().encryptionError, JGIT_VERSION));
  394. }
  395. if (cont == null) {
  396. throw new IOException(MessageFormat
  397. .format(JGitText.get().encryptionError, JGIT_CONTEXT));
  398. }
  399. if (!profile.equals(prof)) {
  400. throw new IOException(MessageFormat.format(
  401. JGitText.get().unsupportedEncryptionAlgorithm, prof));
  402. }
  403. if (!version.equals(vers)) {
  404. throw new IOException(MessageFormat.format(
  405. JGitText.get().unsupportedEncryptionVersion, vers));
  406. }
  407. try {
  408. decryptCipher = Cipher.getInstance(cipherAlgo);
  409. if (cont.isEmpty()) {
  410. decryptCipher.init(Cipher.DECRYPT_MODE, secretKey);
  411. } else {
  412. AlgorithmParameters params = AlgorithmParameters
  413. .getInstance(paramsAlgo);
  414. params.init(Base64.decode(cont));
  415. decryptCipher.init(Cipher.DECRYPT_MODE, secretKey, params);
  416. }
  417. } catch (Exception e) {
  418. throw error(e);
  419. }
  420. }
  421. @Override
  422. InputStream decrypt(InputStream input) throws IOException {
  423. try {
  424. return new CipherInputStream(input, decryptCipher);
  425. } finally {
  426. decryptCipher = null; // Cleanup validate -> decrypt transition.
  427. }
  428. }
  429. }
  430. /**
  431. * Provides JetS3t-like encryption with AES support. Uses V1 connection file
  432. * format. For reference, see: 'jgit-s3-connection-v-1.properties'.
  433. */
  434. static class JGitV1 extends SymmetricEncryption {
  435. static final String VERSION = "1"; //$NON-NLS-1$
  436. // Re-map connection properties V1 -> V2.
  437. static Properties wrap(String algo, String pass) {
  438. Properties props = new Properties();
  439. props.put(AmazonS3.Keys.CRYPTO_ALG, algo);
  440. props.put(AmazonS3.Keys.CRYPTO_VER, VERSION);
  441. props.put(AmazonS3.Keys.PASSWORD, pass);
  442. props.put(algo + Keys.X_ALGO, algo);
  443. props.put(algo + Keys.X_KEY_ALGO, algo);
  444. props.put(algo + Keys.X_KEY_ITER, DEFAULT_KEY_ITER);
  445. props.put(algo + Keys.X_KEY_SIZE, DEFAULT_KEY_SIZE);
  446. props.put(algo + Keys.X_KEY_SALT, DEFAULT_KEY_SALT);
  447. return props;
  448. }
  449. JGitV1(String algo, String pass)
  450. throws GeneralSecurityException {
  451. super(wrap(algo, pass));
  452. String name = cipherAlgo.toUpperCase();
  453. Matcher matcherPBE = Pattern.compile(REGEX_PBE).matcher(name);
  454. if (!matcherPBE.matches())
  455. throw new GeneralSecurityException(
  456. JGitText.get().encryptionOnlyPBE);
  457. }
  458. }
  459. /**
  460. * Supports both PBE and non-PBE algorithms. Uses V2 connection file format.
  461. * For reference, see: 'jgit-s3-connection-v-2.properties'.
  462. */
  463. static class JGitV2 extends SymmetricEncryption {
  464. static final String VERSION = "2"; //$NON-NLS-1$
  465. JGitV2(Properties props)
  466. throws GeneralSecurityException {
  467. super(props);
  468. }
  469. }
  470. /**
  471. * Encryption factory.
  472. *
  473. * @param props
  474. * @return instance
  475. * @throws GeneralSecurityException
  476. */
  477. static WalkEncryption instance(Properties props)
  478. throws GeneralSecurityException {
  479. String algo = props.getProperty(AmazonS3.Keys.CRYPTO_ALG, Vals.DEFAULT_ALGO);
  480. String vers = props.getProperty(AmazonS3.Keys.CRYPTO_VER, Vals.DEFAULT_VERS);
  481. String pass = props.getProperty(AmazonS3.Keys.PASSWORD);
  482. if (pass == null) // Disable encryption.
  483. return WalkEncryption.NONE;
  484. switch (vers) {
  485. case Vals.DEFAULT_VERS:
  486. return new JetS3tV2(algo, pass);
  487. case JGitV1.VERSION:
  488. return new JGitV1(algo, pass);
  489. case JGitV2.VERSION:
  490. return new JGitV2(props);
  491. default:
  492. throw new GeneralSecurityException(MessageFormat.format(
  493. JGitText.get().unsupportedEncryptionVersion, vers));
  494. }
  495. }
  496. }