mirror of
https://github.com/etesync/android
synced 2025-02-02 19:01:06 +00:00
Crypto: Add basic asymmetric encryption methods
This commit is contained in:
parent
89731519e9
commit
e836b4c716
@ -7,11 +7,18 @@ import com.etesync.syncadapter.utils.Base64;
|
|||||||
|
|
||||||
import org.apache.commons.codec.Charsets;
|
import org.apache.commons.codec.Charsets;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
|
||||||
|
import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
|
||||||
|
import org.spongycastle.crypto.AsymmetricBlockCipher;
|
||||||
|
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
||||||
import org.spongycastle.crypto.BufferedBlockCipher;
|
import org.spongycastle.crypto.BufferedBlockCipher;
|
||||||
import org.spongycastle.crypto.CipherParameters;
|
import org.spongycastle.crypto.CipherParameters;
|
||||||
import org.spongycastle.crypto.InvalidCipherTextException;
|
import org.spongycastle.crypto.InvalidCipherTextException;
|
||||||
import org.spongycastle.crypto.digests.SHA256Digest;
|
import org.spongycastle.crypto.digests.SHA256Digest;
|
||||||
|
import org.spongycastle.crypto.encodings.OAEPEncoding;
|
||||||
import org.spongycastle.crypto.engines.AESEngine;
|
import org.spongycastle.crypto.engines.AESEngine;
|
||||||
|
import org.spongycastle.crypto.engines.RSAEngine;
|
||||||
|
import org.spongycastle.crypto.generators.RSAKeyPairGenerator;
|
||||||
import org.spongycastle.crypto.generators.SCrypt;
|
import org.spongycastle.crypto.generators.SCrypt;
|
||||||
import org.spongycastle.crypto.macs.HMac;
|
import org.spongycastle.crypto.macs.HMac;
|
||||||
import org.spongycastle.crypto.modes.CBCBlockCipher;
|
import org.spongycastle.crypto.modes.CBCBlockCipher;
|
||||||
@ -20,12 +27,21 @@ import org.spongycastle.crypto.paddings.PKCS7Padding;
|
|||||||
import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
|
import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
|
||||||
import org.spongycastle.crypto.params.KeyParameter;
|
import org.spongycastle.crypto.params.KeyParameter;
|
||||||
import org.spongycastle.crypto.params.ParametersWithIV;
|
import org.spongycastle.crypto.params.ParametersWithIV;
|
||||||
|
import org.spongycastle.crypto.params.RSAKeyGenerationParameters;
|
||||||
|
import org.spongycastle.crypto.util.PrivateKeyFactory;
|
||||||
|
import org.spongycastle.crypto.util.PrivateKeyInfoFactory;
|
||||||
|
import org.spongycastle.crypto.util.PublicKeyFactory;
|
||||||
|
import org.spongycastle.crypto.util.SubjectPublicKeyInfoFactory;
|
||||||
import org.spongycastle.util.encoders.Hex;
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import lombok.AccessLevel;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
public class Crypto {
|
public class Crypto {
|
||||||
public static String deriveKey(String salt, String password) {
|
public static String deriveKey(String salt, String password) {
|
||||||
@ -34,6 +50,73 @@ public class Crypto {
|
|||||||
return Base64.encodeToString(SCrypt.generate(password.getBytes(Charsets.UTF_8), salt.getBytes(Charsets.UTF_8), 16384, 8, 1, keySize), Base64.NO_WRAP);
|
return Base64.encodeToString(SCrypt.generate(password.getBytes(Charsets.UTF_8), salt.getBytes(Charsets.UTF_8), 16384, 8, 1, keySize), Base64.NO_WRAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static AsymmetricKeyPair generateKeyPair() {
|
||||||
|
RSAKeyPairGenerator keyPairGenerator = new RSAKeyPairGenerator();
|
||||||
|
keyPairGenerator.init(new RSAKeyGenerationParameters(BigInteger.valueOf(65537), new SecureRandom(), 2048, 160));
|
||||||
|
AsymmetricCipherKeyPair keyPair = keyPairGenerator.generateKeyPair();
|
||||||
|
try {
|
||||||
|
PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.createPrivateKeyInfo(keyPair.getPrivate());
|
||||||
|
SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(keyPair.getPublic());
|
||||||
|
return new AsymmetricKeyPair(privateKeyInfo.getEncoded(), publicKeyInfo.getEncoded());
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public static class AsymmetricKeyPair {
|
||||||
|
@Getter(AccessLevel.PUBLIC)
|
||||||
|
private final byte[] privateKey;
|
||||||
|
@Getter(AccessLevel.PUBLIC)
|
||||||
|
private final byte[] publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AsymmetricCryptoManager {
|
||||||
|
private final AsymmetricKeyPair keyPair;
|
||||||
|
|
||||||
|
public AsymmetricCryptoManager(AsymmetricKeyPair keyPair) {
|
||||||
|
this.keyPair = keyPair;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] encrypt(byte[] pubkey, byte[] content) {
|
||||||
|
AsymmetricBlockCipher cipher = new RSAEngine();
|
||||||
|
cipher = new OAEPEncoding(cipher);
|
||||||
|
try {
|
||||||
|
cipher.init(true, PublicKeyFactory.createKey(pubkey));
|
||||||
|
return cipher.processBlock(content, 0, content.length);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (InvalidCipherTextException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
App.log.severe("Invalid ciphertext: " + Base64.encodeToString(content, Base64.NO_WRAP));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] decrypt(byte[] cipherText) {
|
||||||
|
AsymmetricBlockCipher cipher = new RSAEngine();
|
||||||
|
cipher = new OAEPEncoding(cipher);
|
||||||
|
try {
|
||||||
|
cipher.init(false, PrivateKeyFactory.createKey(keyPair.getPrivateKey()));
|
||||||
|
return cipher.processBlock(cipherText, 0, cipherText.length);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (InvalidCipherTextException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
App.log.severe("Invalid ciphertext: " + Base64.encodeToString(cipherText, Base64.NO_WRAP));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] getKeyFingerprint(byte[] pubkey) {
|
||||||
|
return sha256(pubkey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class CryptoManager {
|
public static class CryptoManager {
|
||||||
final static int HMAC_SIZE = 256 / 8; // hmac256 in bytes
|
final static int HMAC_SIZE = 256 / 8; // hmac256 in bytes
|
||||||
|
|
||||||
@ -146,15 +229,15 @@ public class Crypto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static String sha256(String base) {
|
static String sha256(String base) {
|
||||||
return sha256(base.getBytes(Charsets.UTF_8));
|
return toHex(sha256(base.getBytes(Charsets.UTF_8)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String sha256(byte[] base) {
|
private static byte[] sha256(byte[] base) {
|
||||||
SHA256Digest digest = new SHA256Digest();
|
SHA256Digest digest = new SHA256Digest();
|
||||||
digest.update(base, 0, base.length);
|
digest.update(base, 0, base.length);
|
||||||
byte[] ret = new byte[digest.getDigestSize()];
|
byte[] ret = new byte[digest.getDigestSize()];
|
||||||
digest.doFinal(ret, 0);
|
digest.doFinal(ret, 0);
|
||||||
return toHex(ret);
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static String toHex(byte[] bytes) {
|
static String toHex(byte[] bytes) {
|
||||||
|
@ -14,9 +14,11 @@ import org.apache.commons.codec.Charsets;
|
|||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
public class EncryptionTest {
|
public class EncryptionTest {
|
||||||
@ -67,4 +69,19 @@ public class EncryptionTest {
|
|||||||
public void testCryptoVersionOutOfRange() throws Exceptions.IntegrityException, Exceptions.VersionTooNewException {
|
public void testCryptoVersionOutOfRange() throws Exceptions.IntegrityException, Exceptions.VersionTooNewException {
|
||||||
new Crypto.CryptoManager(999, Helpers.keyBase64, "TestSaltShouldBeJournalId");
|
new Crypto.CryptoManager(999, Helpers.keyBase64, "TestSaltShouldBeJournalId");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAsymCrypto() throws Exceptions.IntegrityException, Exceptions.GenericCryptoException {
|
||||||
|
Crypto.AsymmetricKeyPair keyPair = Crypto.generateKeyPair();
|
||||||
|
Crypto.AsymmetricCryptoManager cryptoManager = new Crypto.AsymmetricCryptoManager(keyPair);
|
||||||
|
|
||||||
|
byte[] clearText = "This Is Some Test Cleartext.".getBytes(Charsets.UTF_8);
|
||||||
|
byte[] cipher = cryptoManager.encrypt(keyPair.getPublicKey(), clearText);
|
||||||
|
byte[] clearText2 = cryptoManager.decrypt(cipher);
|
||||||
|
assertArrayEquals(clearText, clearText2);
|
||||||
|
|
||||||
|
// Mostly for coverage. Make sure it's the expected sha256 value.
|
||||||
|
assertEquals("ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb",
|
||||||
|
Hex.toHexString(Crypto.AsymmetricCryptoManager.getKeyFingerprint("a".getBytes(Charsets.UTF_8))).toLowerCase());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user