/* ******************************************************************************
* Copyright (c) 2006-2016 XMind Ltd. and others.
*
* This file is a part of XMind 3. XMind releases 3 and
* above are dual-licensed under the Eclipse Public License (EPL),
* which is available at http://www.eclipse.org/legal/epl-v10.html
* and the GNU Lesser General Public License (LGPL),
* which is available at http://www.gnu.org/licenses/lgpl.html
* See http://www.xmind.net/license.html for details.
*
* Contributors:
* XMind Ltd. - initial API and implementation
*******************************************************************************/
/**
*
*/
package org.xmind.core.internal.security;
import java.security.MessageDigest;
/**
* Generator for PBE derived keys and ivs as defined by PKCS 12 V1.0.
* <p>
* The document this implementation is based on can be found at <a
* href=http://www.rsasecurity.com/rsalabs/pkcs/pkcs-12/index.html> RSA's PKCS12
* Page</a>
* <p>
* NOTE: This algorithm in this class is copied from Bouncycastle's
* PKCS12ParametersGenerator in order to decrypt legacy xmind files.
*
* @author Frank Shaka
*/
public class PKCS12KeyGenerator {
public static final int KEY_MATERIAL = 1;
public static final int IV_MATERIAL = 2;
public static final int MAC_MATERIAL = 3;
protected byte[] password;
protected byte[] salt;
protected int iterationCount;
private MessageDigest digest;
private int u;
private int v;
/**
* Construct a PKCS 12 Parameters generator. This constructor will accept
* any digest which also implements ExtendedDigest.
*
* @param digest
* the digest to be used as the source of derived keys.
* @exception IllegalArgumentException
* if an unknown digest is passed in.
*/
public PKCS12KeyGenerator(MessageDigest digest) {
this.digest = digest;
u = digest.getDigestLength();
v = 64; //((ExtendedDigest)digest).getByteLength();
}
/**
* initialise the PBE generator.
*
* @param password
* the password converted into bytes (see below).
* @param salt
* the salt to be mixed with the password.
* @param iterationCount
* the number of iterations the "mixing" function is to be
* applied for.
*/
public void init(byte[] password, byte[] salt, int iterationCount) {
this.password = password;
this.salt = salt;
this.iterationCount = iterationCount;
}
/**
* return the password byte array.
*
* @return the password byte array.
*/
public byte[] getPassword() {
return password;
}
/**
* return the salt byte array.
*
* @return the salt byte array.
*/
public byte[] getSalt() {
return salt;
}
/**
* return the iteration count.
*
* @return the iteration count.
*/
public int getIterationCount() {
return iterationCount;
}
/**
* converts a password to a byte array according to the scheme in PKCS12
* (unicode, big endian, 2 zero pad bytes at the end).
*
* @param password
* a character array representing the password.
* @return a byte array representing the password.
*/
public static byte[] PKCS12PasswordToBytes(char[] password) {
if (password != null && password.length > 0) {
// +1 for extra 2 pad bytes.
byte[] bytes = new byte[(password.length + 1) * 2];
for (int i = 0; i != password.length; i++) {
bytes[i * 2] = (byte) (password[i] >>> 8);
bytes[i * 2 + 1] = (byte) password[i];
}
return bytes;
} else {
return new byte[0];
}
}
/**
* add a + b + 1, returning the result in a. The a value is treated as a
* BigInteger of length (b.length * 8) bits. The result is modulo 2^b.length
* in case of overflow.
*/
private void adjust(byte[] a, int aOff, byte[] b) {
int x = (b[b.length - 1] & 0xff) + (a[aOff + b.length - 1] & 0xff) + 1;
a[aOff + b.length - 1] = (byte) x;
x >>>= 8;
for (int i = b.length - 2; i >= 0; i--) {
x += (b[i] & 0xff) + (a[aOff + i] & 0xff);
a[aOff + i] = (byte) x;
x >>>= 8;
}
}
/**
* generation of a derived key ala PKCS12 V1.0.
*/
private byte[] generateDerivedKey(int idByte, int n) {
byte[] D = new byte[v];
byte[] dKey = new byte[n];
for (int i = 0; i != D.length; i++) {
D[i] = (byte) idByte;
}
byte[] S;
if ((salt != null) && (salt.length != 0)) {
S = new byte[v * ((salt.length + v - 1) / v)];
for (int i = 0; i != S.length; i++) {
S[i] = salt[i % salt.length];
}
} else {
S = new byte[0];
}
byte[] P;
if ((password != null) && (password.length != 0)) {
P = new byte[v * ((password.length + v - 1) / v)];
for (int i = 0; i != P.length; i++) {
P[i] = password[i % password.length];
}
} else {
P = new byte[0];
}
byte[] I = new byte[S.length + P.length];
System.arraycopy(S, 0, I, 0, S.length);
System.arraycopy(P, 0, I, S.length, P.length);
byte[] B = new byte[v];
int c = (n + u - 1) / u;
byte[] A;
for (int i = 1; i <= c; i++) {
digest.update(D, 0, D.length);
digest.update(I, 0, I.length);
A = digest.digest();
for (int j = 1; j < iterationCount; j++) {
digest.update(A, 0, A.length);
A = digest.digest();
}
for (int j = 0; j != B.length; j++) {
B[j] = A[j % A.length];
}
for (int j = 0; j != I.length / v; j++) {
adjust(I, j * v, B);
}
if (i == c) {
System.arraycopy(A, 0, dKey, (i - 1) * u,
dKey.length - ((i - 1) * u));
} else {
System.arraycopy(A, 0, dKey, (i - 1) * u, A.length);
}
}
return dKey;
}
/**
* Generate a key parameter derived from the password, salt, and iteration
* count we are currently initialised with.
*
* @param keySize
* the size of the key we want (in bits)
* @return a KeyParameter object.
*/
public byte[] generateDerivedKey(int keySize) {
keySize = keySize / 8;
byte[] dKey = generateDerivedKey(KEY_MATERIAL, keySize);
byte[] key = new byte[keySize];
System.arraycopy(dKey, 0, key, 0, keySize);
return key;
}
}