View Javadoc
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  
44  package org.eclipse.jgit.transport;
45  
46  import java.io.IOException;
47  import java.io.InputStream;
48  import java.io.OutputStream;
49  import java.net.HttpURLConnection;
50  import java.security.AlgorithmParameters;
51  import java.security.GeneralSecurityException;
52  import java.security.spec.AlgorithmParameterSpec;
53  import java.security.spec.KeySpec;
54  import java.text.MessageFormat;
55  import java.util.Locale;
56  import java.util.Properties;
57  import java.util.regex.Matcher;
58  import java.util.regex.Pattern;
59  
60  import javax.crypto.Cipher;
61  import javax.crypto.CipherInputStream;
62  import javax.crypto.CipherOutputStream;
63  import javax.crypto.SecretKey;
64  import javax.crypto.SecretKeyFactory;
65  import javax.crypto.spec.IvParameterSpec;
66  import javax.crypto.spec.PBEKeySpec;
67  import javax.crypto.spec.PBEParameterSpec;
68  import javax.crypto.spec.SecretKeySpec;
69  import javax.xml.bind.DatatypeConverter;
70  
71  import org.eclipse.jgit.internal.JGitText;
72  import org.eclipse.jgit.util.Base64;
73  
74  abstract class WalkEncryption {
75  	static final WalkEncryption NONE = new NoEncryption();
76  
77  	static final String JETS3T_CRYPTO_VER = "jets3t-crypto-ver"; //$NON-NLS-1$
78  
79  	static final String JETS3T_CRYPTO_ALG = "jets3t-crypto-alg"; //$NON-NLS-1$
80  
81  	// Note: encrypt -> request state machine, step 1.
82  	abstract OutputStream encrypt(OutputStream output) throws IOException;
83  
84  	// Note: encrypt -> request state machine, step 2.
85  	abstract void request(HttpURLConnection conn, String prefix) throws IOException;
86  
87  	// Note: validate -> decrypt state machine, step 1.
88  	abstract void validate(HttpURLConnection conn, String prefix) throws IOException;
89  
90  	// Note: validate -> decrypt state machine, step 2.
91  	abstract InputStream decrypt(InputStream input) throws IOException;
92  
93  
94  	// TODO mixed ciphers
95  	// consider permitting mixed ciphers to facilitate algorithm migration
96  	// i.e. user keeps the password, but changes the algorithm
97  	// then existing remote entries will still be readable
98  	/**
99  	 * Validate
100 	 *
101 	 * @param u
102 	 *            a {@link java.net.HttpURLConnection} object.
103 	 * @param prefix
104 	 *            a {@link java.lang.String} object.
105 	 * @param version
106 	 *            a {@link java.lang.String} object.
107 	 * @param name
108 	 *            a {@link java.lang.String} object.
109 	 * @throws java.io.IOException
110 	 *             if any.
111 	 */
112 	protected void validateImpl(final HttpURLConnection u, final String prefix,
113 			final String version, final String name) throws IOException {
114 		String v;
115 
116 		v = u.getHeaderField(prefix + JETS3T_CRYPTO_VER);
117 		if (v == null)
118 			v = ""; //$NON-NLS-1$
119 		if (!version.equals(v))
120 			throw new IOException(MessageFormat.format(JGitText.get().unsupportedEncryptionVersion, v));
121 
122 		v = u.getHeaderField(prefix + JETS3T_CRYPTO_ALG);
123 		if (v == null)
124 			v = ""; //$NON-NLS-1$
125 		// Standard names are not case-sensitive.
126 		// http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html
127 		if (!name.equalsIgnoreCase(v))
128 			throw new IOException(MessageFormat.format(JGitText.get().unsupportedEncryptionAlgorithm, v));
129 	}
130 
131 	IOException error(Throwable why) {
132 		return new IOException(MessageFormat
133 				.format(JGitText.get().encryptionError,
134 				why.getMessage()), why);
135 	}
136 
137 	private static class NoEncryption extends WalkEncryption {
138 		@Override
139 		void request(HttpURLConnection u, String prefix) {
140 			// Don't store any request properties.
141 		}
142 
143 		@Override
144 		void validate(HttpURLConnection u, String prefix)
145 				throws IOException {
146 			validateImpl(u, prefix, "", ""); //$NON-NLS-1$ //$NON-NLS-2$
147 		}
148 
149 		@Override
150 		InputStream decrypt(InputStream in) {
151 			return in;
152 		}
153 
154 		@Override
155 		OutputStream encrypt(OutputStream os) {
156 			return os;
157 		}
158 	}
159 
160 	/**
161 	 * JetS3t compatibility reference: <a href=
162 	 * "https://bitbucket.org/jmurty/jets3t/src/156c00eb160598c2e9937fd6873f00d3190e28ca/src/org/jets3t/service/security/EncryptionUtil.java">
163 	 * EncryptionUtil.java</a>
164 	 * <p>
165 	 * Note: EncryptionUtil is inadequate:
166 	 * <li>EncryptionUtil.isCipherAvailableForUse checks encryption only which
167 	 * "always works", but in JetS3t both encryption and decryption use non-IV
168 	 * aware algorithm parameters for all PBE specs, which breaks in case of AES
169 	 * <li>that means that only non-IV algorithms will work round trip in
170 	 * JetS3t, such as PBEWithMD5AndDES and PBEWithSHAAndTwofish-CBC
171 	 * <li>any AES based algorithms such as "PBE...With...And...AES" will not
172 	 * work, since they need proper IV setup
173 	 */
174 	static class JetS3tV2 extends WalkEncryption {
175 
176 		static final String VERSION = "2"; //$NON-NLS-1$
177 
178 		static final String ALGORITHM = "PBEWithMD5AndDES"; //$NON-NLS-1$
179 
180 		static final int ITERATIONS = 5000;
181 
182 		static final int KEY_SIZE = 32;
183 
184 		static final byte[] SALT = { //
185 				(byte) 0xA4, (byte) 0x0B, (byte) 0xC8, (byte) 0x34, //
186 				(byte) 0xD6, (byte) 0x95, (byte) 0xF3, (byte) 0x13 //
187 		};
188 
189 		// Size 16, see com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE
190 		static final byte[] ZERO_AES_IV = new byte[16];
191 
192 		private static final String CRYPTO_VER = VERSION;
193 
194 		private final String cryptoAlg;
195 
196 		private final SecretKey secretKey;
197 
198 		private final AlgorithmParameterSpec paramSpec;
199 
200 		JetS3tV2(final String algo, final String key)
201 				throws GeneralSecurityException {
202 			cryptoAlg = algo;
203 
204 			// Verify if cipher is present.
205 			Cipher cipher = InsecureCipherFactory.create(cryptoAlg);
206 
207 			// Standard names are not case-sensitive.
208 			// http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html
209 			String cryptoName = cryptoAlg.toUpperCase(Locale.ROOT);
210 
211 			if (!cryptoName.startsWith("PBE")) //$NON-NLS-1$
212 				throw new GeneralSecurityException(JGitText.get().encryptionOnlyPBE);
213 
214 			PBEKeySpec keySpec = new PBEKeySpec(key.toCharArray(), SALT, ITERATIONS, KEY_SIZE);
215 			secretKey = SecretKeyFactory.getInstance(algo).generateSecret(keySpec);
216 
217 			// Detect algorithms which require initialization vector.
218 			boolean useIV = cryptoName.contains("AES"); //$NON-NLS-1$
219 
220 			// PBEParameterSpec algorithm parameters are supported from Java 8.
221 			if (useIV) {
222 				// Support IV where possible:
223 				// * since JCE provider uses random IV for PBE/AES
224 				// * and there is no place to store dynamic IV in JetS3t V2
225 				// * we use static IV, and tolerate increased security risk
226 				// TODO back port this change to JetS3t V2
227 				// See:
228 				// https://bitbucket.org/jmurty/jets3t/raw/156c00eb160598c2e9937fd6873f00d3190e28ca/src/org/jets3t/service/security/EncryptionUtil.java
229 				// http://cr.openjdk.java.net/~mullan/webrevs/ascarpin/webrev.00/raw_files/new/src/share/classes/com/sun/crypto/provider/PBES2Core.java
230 				IvParameterSpec paramIV = new IvParameterSpec(ZERO_AES_IV);
231 				paramSpec = new PBEParameterSpec(SALT, ITERATIONS, paramIV);
232 			} else {
233 				// Strict legacy JetS3t V2 compatibility, with no IV support.
234 				paramSpec = new PBEParameterSpec(SALT, ITERATIONS);
235 			}
236 
237 			// Verify if cipher + key are allowed by policy.
238 			cipher.init(Cipher.ENCRYPT_MODE, secretKey, paramSpec);
239 			cipher.doFinal();
240 		}
241 
242 		@Override
243 		void request(HttpURLConnection u, String prefix) {
244 			u.setRequestProperty(prefix + JETS3T_CRYPTO_VER, CRYPTO_VER);
245 			u.setRequestProperty(prefix + JETS3T_CRYPTO_ALG, cryptoAlg);
246 		}
247 
248 		@Override
249 		void validate(HttpURLConnection u, String prefix)
250 				throws IOException {
251 			validateImpl(u, prefix, CRYPTO_VER, cryptoAlg);
252 		}
253 
254 		@Override
255 		OutputStream encrypt(OutputStream os) throws IOException {
256 			try {
257 				final Cipher cipher = InsecureCipherFactory.create(cryptoAlg);
258 				cipher.init(Cipher.ENCRYPT_MODE, secretKey, paramSpec);
259 				return new CipherOutputStream(os, cipher);
260 			} catch (GeneralSecurityException e) {
261 				throw error(e);
262 			}
263 		}
264 
265 		@Override
266 		InputStream decrypt(InputStream in) throws IOException {
267 			try {
268 				final Cipher cipher = InsecureCipherFactory.create(cryptoAlg);
269 				cipher.init(Cipher.DECRYPT_MODE, secretKey, paramSpec);
270 				return new CipherInputStream(in, cipher);
271 			} catch (GeneralSecurityException e) {
272 				throw error(e);
273 			}
274 		}
275 	}
276 
277 	/** Encryption property names. */
278 	interface Keys {
279 		// Remote S3 meta: V1 algorithm name or V2 profile name.
280 		String JGIT_PROFILE = "jgit-crypto-profile"; //$NON-NLS-1$
281 
282 		// Remote S3 meta: JGit encryption implementation version.
283 		String JGIT_VERSION = "jgit-crypto-version"; //$NON-NLS-1$
284 
285 		// Remote S3 meta: base-64 encoded cipher algorithm parameters.
286 		String JGIT_CONTEXT = "jgit-crypto-context"; //$NON-NLS-1$
287 
288 		// Amazon S3 connection configuration file profile property suffixes:
289 		String X_ALGO = ".algo"; //$NON-NLS-1$
290 		String X_KEY_ALGO = ".key.algo"; //$NON-NLS-1$
291 		String X_KEY_SIZE = ".key.size"; //$NON-NLS-1$
292 		String X_KEY_ITER = ".key.iter"; //$NON-NLS-1$
293 		String X_KEY_SALT = ".key.salt"; //$NON-NLS-1$
294 	}
295 
296 	/** Encryption constants and defaults. */
297 	interface Vals {
298 		// Compatibility defaults.
299 		String DEFAULT_VERS = "0"; //$NON-NLS-1$
300 		String DEFAULT_ALGO = JetS3tV2.ALGORITHM;
301 		String DEFAULT_KEY_ALGO = JetS3tV2.ALGORITHM;
302 		String DEFAULT_KEY_SIZE = Integer.toString(JetS3tV2.KEY_SIZE);
303 		String DEFAULT_KEY_ITER = Integer.toString(JetS3tV2.ITERATIONS);
304 		String DEFAULT_KEY_SALT = DatatypeConverter.printHexBinary(JetS3tV2.SALT);
305 
306 		String EMPTY = ""; //$NON-NLS-1$
307 
308 		// Match white space.
309 		String REGEX_WS = "\\s+"; //$NON-NLS-1$
310 
311 		// Match PBE ciphers, i.e: PBEWithMD5AndDES
312 		String REGEX_PBE = "(PBE).*(WITH).+(AND).+"; //$NON-NLS-1$
313 
314 		// Match transformation ciphers, i.e: AES/CBC/PKCS5Padding
315 		String REGEX_TRANS = "(.+)/(.+)/(.+)"; //$NON-NLS-1$
316 	}
317 
318 	static GeneralSecurityException securityError(String message) {
319 		return new GeneralSecurityException(
320 				MessageFormat.format(JGitText.get().encryptionError, message));
321 	}
322 
323 	/**
324 	 * Base implementation of JGit symmetric encryption. Supports V2 properties
325 	 * format.
326 	 */
327 	static abstract class SymmetricEncryption extends WalkEncryption
328 			implements Keys, Vals {
329 
330 		/** Encryption profile, root name of group of related properties. */
331 		final String profile;
332 
333 		/** Encryption version, reflects actual implementation class. */
334 		final String version;
335 
336 		/** Full cipher algorithm name. */
337 		final String cipherAlgo;
338 
339 		/** Cipher algorithm name for parameters lookup. */
340 		final String paramsAlgo;
341 
342 		/** Generated secret key. */
343 		final SecretKey secretKey;
344 
345 		SymmetricEncryption(Properties props) throws GeneralSecurityException {
346 
347 			profile = props.getProperty(AmazonS3.Keys.CRYPTO_ALG);
348 			version = props.getProperty(AmazonS3.Keys.CRYPTO_VER);
349 			String pass = props.getProperty(AmazonS3.Keys.PASSWORD);
350 
351 			cipherAlgo = props.getProperty(profile + X_ALGO, DEFAULT_ALGO);
352 
353 			String keyAlgo = props.getProperty(profile + X_KEY_ALGO, DEFAULT_KEY_ALGO);
354 			String keySize = props.getProperty(profile + X_KEY_SIZE, DEFAULT_KEY_SIZE);
355 			String keyIter = props.getProperty(profile + X_KEY_ITER, DEFAULT_KEY_ITER);
356 			String keySalt = props.getProperty(profile + X_KEY_SALT, DEFAULT_KEY_SALT);
357 
358 			// Verify if cipher is present.
359 			Cipher cipher = InsecureCipherFactory.create(cipherAlgo);
360 
361 			// Verify if key factory is present.
362 			SecretKeyFactory factory = SecretKeyFactory.getInstance(keyAlgo);
363 
364 			final int size;
365 			try {
366 				size = Integer.parseInt(keySize);
367 			} catch (Exception e) {
368 				throw securityError(X_KEY_SIZE + EMPTY + keySize);
369 			}
370 
371 			final int iter;
372 			try {
373 				iter = Integer.parseInt(keyIter);
374 			} catch (Exception e) {
375 				throw securityError(X_KEY_ITER + EMPTY + keyIter);
376 			}
377 
378 			final byte[] salt;
379 			try {
380 				salt = DatatypeConverter
381 						.parseHexBinary(keySalt.replaceAll(REGEX_WS, EMPTY));
382 			} catch (Exception e) {
383 				throw securityError(X_KEY_SALT + EMPTY + keySalt);
384 			}
385 
386 			KeySpec keySpec = new PBEKeySpec(pass.toCharArray(), salt, iter, size);
387 
388 			SecretKey keyBase = factory.generateSecret(keySpec);
389 
390 			String name = cipherAlgo.toUpperCase(Locale.ROOT);
391 			Matcher matcherPBE = Pattern.compile(REGEX_PBE).matcher(name);
392 			Matcher matcherTrans = Pattern.compile(REGEX_TRANS).matcher(name);
393 			if (matcherPBE.matches()) {
394 				paramsAlgo = cipherAlgo;
395 				secretKey = keyBase;
396 			} else if (matcherTrans.find()) {
397 				paramsAlgo = matcherTrans.group(1);
398 				secretKey = new SecretKeySpec(keyBase.getEncoded(), paramsAlgo);
399 			} else {
400 				throw new GeneralSecurityException(MessageFormat.format(
401 						JGitText.get().unsupportedEncryptionAlgorithm,
402 						cipherAlgo));
403 			}
404 
405 			// Verify if cipher + key are allowed by policy.
406 			cipher.init(Cipher.ENCRYPT_MODE, secretKey);
407 			cipher.doFinal();
408 
409 		}
410 
411 		// Shared state encrypt -> request.
412 		volatile String context;
413 
414 		@Override
415 		OutputStream encrypt(OutputStream output) throws IOException {
416 			try {
417 				Cipher cipher = InsecureCipherFactory.create(cipherAlgo);
418 				cipher.init(Cipher.ENCRYPT_MODE, secretKey);
419 				AlgorithmParameters params = cipher.getParameters();
420 				if (params == null) {
421 					context = EMPTY;
422 				} else {
423 					context = Base64.encodeBytes(params.getEncoded());
424 				}
425 				return new CipherOutputStream(output, cipher);
426 			} catch (Exception e) {
427 				throw error(e);
428 			}
429 		}
430 
431 		@Override
432 		void request(HttpURLConnection conn, String prefix) throws IOException {
433 			conn.setRequestProperty(prefix + JGIT_PROFILE, profile);
434 			conn.setRequestProperty(prefix + JGIT_VERSION, version);
435 			conn.setRequestProperty(prefix + JGIT_CONTEXT, context);
436 			// No cleanup:
437 			// single encrypt can be followed by several request
438 			// from the AmazonS3.putImpl() multiple retry attempts
439 			// context = null; // Cleanup encrypt -> request transition.
440 			// TODO re-factor AmazonS3.putImpl to be more transaction-like
441 		}
442 
443 		// Shared state validate -> decrypt.
444 		volatile Cipher decryptCipher;
445 
446 		@Override
447 		void validate(HttpURLConnection conn, String prefix)
448 				throws IOException {
449 			String prof = conn.getHeaderField(prefix + JGIT_PROFILE);
450 			String vers = conn.getHeaderField(prefix + JGIT_VERSION);
451 			String cont = conn.getHeaderField(prefix + JGIT_CONTEXT);
452 
453 			if (prof == null) {
454 				throw new IOException(MessageFormat
455 						.format(JGitText.get().encryptionError, JGIT_PROFILE));
456 			}
457 			if (vers == null) {
458 				throw new IOException(MessageFormat
459 						.format(JGitText.get().encryptionError, JGIT_VERSION));
460 			}
461 			if (cont == null) {
462 				throw new IOException(MessageFormat
463 						.format(JGitText.get().encryptionError, JGIT_CONTEXT));
464 			}
465 			if (!profile.equals(prof)) {
466 				throw new IOException(MessageFormat.format(
467 						JGitText.get().unsupportedEncryptionAlgorithm, prof));
468 			}
469 			if (!version.equals(vers)) {
470 				throw new IOException(MessageFormat.format(
471 						JGitText.get().unsupportedEncryptionVersion, vers));
472 			}
473 			try {
474 				decryptCipher = InsecureCipherFactory.create(cipherAlgo);
475 				if (cont.isEmpty()) {
476 					decryptCipher.init(Cipher.DECRYPT_MODE, secretKey);
477 				} else {
478 					AlgorithmParameters params = AlgorithmParameters
479 							.getInstance(paramsAlgo);
480 					params.init(Base64.decode(cont));
481 					decryptCipher.init(Cipher.DECRYPT_MODE, secretKey, params);
482 				}
483 			} catch (Exception e) {
484 				throw error(e);
485 			}
486 		}
487 
488 		@Override
489 		InputStream decrypt(InputStream input) throws IOException {
490 			try {
491 				return new CipherInputStream(input, decryptCipher);
492 			} finally {
493 				decryptCipher = null; // Cleanup validate -> decrypt transition.
494 			}
495 		}
496 	}
497 
498 	/**
499 	 * Provides JetS3t-like encryption with AES support. Uses V1 connection file
500 	 * format. For reference, see: 'jgit-s3-connection-v-1.properties'.
501 	 */
502 	static class JGitV1 extends SymmetricEncryption {
503 
504 		static final String VERSION = "1"; //$NON-NLS-1$
505 
506 		// Re-map connection properties V1 -> V2.
507 		static Properties wrap(String algo, String pass) {
508 			Properties props = new Properties();
509 			props.put(AmazonS3.Keys.CRYPTO_ALG, algo);
510 			props.put(AmazonS3.Keys.CRYPTO_VER, VERSION);
511 			props.put(AmazonS3.Keys.PASSWORD, pass);
512 			props.put(algo + Keys.X_ALGO, algo);
513 			props.put(algo + Keys.X_KEY_ALGO, algo);
514 			props.put(algo + Keys.X_KEY_ITER, DEFAULT_KEY_ITER);
515 			props.put(algo + Keys.X_KEY_SIZE, DEFAULT_KEY_SIZE);
516 			props.put(algo + Keys.X_KEY_SALT, DEFAULT_KEY_SALT);
517 			return props;
518 		}
519 
520 		JGitV1(String algo, String pass)
521 				throws GeneralSecurityException {
522 			super(wrap(algo, pass));
523 			String name = cipherAlgo.toUpperCase(Locale.ROOT);
524 			Matcher matcherPBE = Pattern.compile(REGEX_PBE).matcher(name);
525 			if (!matcherPBE.matches())
526 				throw new GeneralSecurityException(
527 						JGitText.get().encryptionOnlyPBE);
528 		}
529 
530 	}
531 
532 	/**
533 	 * Supports both PBE and non-PBE algorithms. Uses V2 connection file format.
534 	 * For reference, see: 'jgit-s3-connection-v-2.properties'.
535 	 */
536 	static class JGitV2 extends SymmetricEncryption {
537 
538 		static final String VERSION = "2"; //$NON-NLS-1$
539 
540 		JGitV2(Properties props)
541 				throws GeneralSecurityException {
542 			super(props);
543 		}
544 	}
545 
546 	/**
547 	 * Encryption factory.
548 	 *
549 	 * @param props
550 	 * @return instance
551 	 * @throws GeneralSecurityException
552 	 */
553 	static WalkEncryption instance(Properties props)
554 			throws GeneralSecurityException {
555 
556 		String algo = props.getProperty(AmazonS3.Keys.CRYPTO_ALG, Vals.DEFAULT_ALGO);
557 		String vers = props.getProperty(AmazonS3.Keys.CRYPTO_VER, Vals.DEFAULT_VERS);
558 		String pass = props.getProperty(AmazonS3.Keys.PASSWORD);
559 
560 		if (pass == null) // Disable encryption.
561 			return WalkEncryption.NONE;
562 
563 		switch (vers) {
564 		case Vals.DEFAULT_VERS:
565 			return new JetS3tV2(algo, pass);
566 		case JGitV1.VERSION:
567 			return new JGitV1(algo, pass);
568 		case JGitV2.VERSION:
569 			return new JGitV2(props);
570 		default:
571 			throw new GeneralSecurityException(MessageFormat.format(
572 					JGitText.get().unsupportedEncryptionVersion, vers));
573 		}
574 	}
575 }