aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/main/java/org/connectbot/util/Encryptor.java
blob: 11a71d607826bc83653cdcf766ba919922c37f3e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/*
 * ConnectBot: simple, powerful, open-source SSH client for Android
 * Copyright 2007 Kenny Root, Jeffrey Sharkey
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.connectbot.util;

/**
 * This class is from:
 *
 * Encryptor.java
 * Copyright 2008 Zach Scrivena
 * zachscrivena@gmail.com
 * http://zs.freeshell.org/
 */

import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;


/**
 * Perform AES-128 encryption.
 */
public final class Encryptor
{
	/** name of the character set to use for converting between characters and bytes */
	private static final String CHARSET_NAME = "UTF-8";

	/** random number generator algorithm */
	private static final String RNG_ALGORITHM = "SHA1PRNG";

	/** message digest algorithm (must be sufficiently long to provide the key and initialization vector) */
	private static final String DIGEST_ALGORITHM = "SHA-256";

	/** key algorithm (must be compatible with CIPHER_ALGORITHM) */
	private static final String KEY_ALGORITHM = "AES";

	/** cipher algorithm (must be compatible with KEY_ALGORITHM) */
	private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";


	/**
	* Private constructor that should never be called.
	*/
	private Encryptor() {
	}


	/**
	* Encrypt the specified cleartext using the given password.
	* With the correct salt, number of iterations, and password, the decrypt() method reverses
	* the effect of this method.
	* This method generates and uses a random salt, and the user-specified number of iterations
	* and password to create a 16-byte secret key and 16-byte initialization vector.
	* The secret key and initialization vector are then used in the AES-128 cipher to encrypt
	* the given cleartext.
	*
	* @param salt
	*	  salt that was used in the encryption (to be populated)
	* @param iterations
	*	  number of iterations to use in salting
	* @param password
	*	  password to be used for encryption
	* @param cleartext
	*	  cleartext to be encrypted
	* @return
	*	  ciphertext
	* @throws Exception
	*	  on any error encountered in encryption
	*/
	public static byte[] encrypt(
			final byte[] salt,
			final int iterations,
			final String password,
			final byte[] cleartext)
			throws Exception
	{
		/* generate salt randomly */
		SecureRandom.getInstance(RNG_ALGORITHM).nextBytes(salt);

		/* compute key and initialization vector */
		final MessageDigest shaDigest = MessageDigest.getInstance(DIGEST_ALGORITHM);
		byte[] pw = password.getBytes(CHARSET_NAME);

		for (int i = 0; i < iterations; i++)
		{
			/* add salt */
			final byte[] salted = new byte[pw.length + salt.length];
			System.arraycopy(pw, 0, salted, 0, pw.length);
			System.arraycopy(salt, 0, salted, pw.length, salt.length);
			Arrays.fill(pw, (byte) 0x00);

			/* compute SHA-256 digest */
			shaDigest.reset();
			pw = shaDigest.digest(salted);
			Arrays.fill(salted, (byte) 0x00);
		}

		/* extract the 16-byte key and initialization vector from the SHA-256 digest */
		final byte[] key = new byte[16];
		final byte[] iv = new byte[16];
		System.arraycopy(pw, 0, key, 0, 16);
		System.arraycopy(pw, 16, iv, 0, 16);
		Arrays.fill(pw, (byte) 0x00);

		/* perform AES-128 encryption */
		final Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);

		cipher.init(
				Cipher.ENCRYPT_MODE,
				new SecretKeySpec(key, KEY_ALGORITHM),
				new IvParameterSpec(iv));

		Arrays.fill(key, (byte) 0x00);
		Arrays.fill(iv, (byte) 0x00);

		return cipher.doFinal(cleartext);
	}


	/**
	* Decrypt the specified ciphertext using the given password.
	* With the correct salt, number of iterations, and password, this method reverses the effect
	* of the encrypt() method.
	* This method uses the user-specified salt, number of iterations, and password
	* to recreate the 16-byte secret key and 16-byte initialization vector.
	* The secret key and initialization vector are then used in the AES-128 cipher to decrypt
	* the given ciphertext.
	*
	* @param salt
	*	  salt to be used in decryption
	* @param iterations
	*	  number of iterations to use in salting
	* @param password
	*	  password to be used for decryption
	* @param ciphertext
	*	  ciphertext to be decrypted
	* @return
	*	  cleartext
	* @throws Exception
	*	  on any error encountered in decryption
	*/
	public static byte[] decrypt(
			final byte[] salt,
			final int iterations,
			final String password,
			final byte[] ciphertext)
			throws Exception
	{
		/* compute key and initialization vector */
		final MessageDigest shaDigest = MessageDigest.getInstance(DIGEST_ALGORITHM);
		byte[] pw = password.getBytes(CHARSET_NAME);

		for (int i = 0; i < iterations; i++)
		{
			/* add salt */
			final byte[] salted = new byte[pw.length + salt.length];
			System.arraycopy(pw, 0, salted, 0, pw.length);
			System.arraycopy(salt, 0, salted, pw.length, salt.length);
			Arrays.fill(pw, (byte) 0x00);

			/* compute SHA-256 digest */
			shaDigest.reset();
			pw = shaDigest.digest(salted);
			Arrays.fill(salted, (byte) 0x00);
		}

		/* extract the 16-byte key and initialization vector from the SHA-256 digest */
		final byte[] key = new byte[16];
		final byte[] iv = new byte[16];
		System.arraycopy(pw, 0, key, 0, 16);
		System.arraycopy(pw, 16, iv, 0, 16);
		Arrays.fill(pw, (byte) 0x00);

		/* perform AES-128 decryption */
		final Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);

		cipher.init(
				Cipher.DECRYPT_MODE,
				new SecretKeySpec(key, KEY_ALGORITHM),
				new IvParameterSpec(iv));

		Arrays.fill(key, (byte) 0x00);
		Arrays.fill(iv, (byte) 0x00);

		return cipher.doFinal(ciphertext);
	}
}