mirror of
https://github.com/etesync/android
synced 2025-01-23 14:10:54 +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.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.CipherParameters;
|
||||
import org.spongycastle.crypto.InvalidCipherTextException;
|
||||
import org.spongycastle.crypto.digests.SHA256Digest;
|
||||
import org.spongycastle.crypto.encodings.OAEPEncoding;
|
||||
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.macs.HMac;
|
||||
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.params.KeyParameter;
|
||||
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 java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
public class Crypto {
|
||||
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);
|
||||
}
|
||||
|
||||
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 {
|
||||
final static int HMAC_SIZE = 256 / 8; // hmac256 in bytes
|
||||
|
||||
@ -146,15 +229,15 @@ public class Crypto {
|
||||
}
|
||||
|
||||
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();
|
||||
digest.update(base, 0, base.length);
|
||||
byte[] ret = new byte[digest.getDigestSize()];
|
||||
digest.doFinal(ret, 0);
|
||||
return toHex(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static String toHex(byte[] bytes) {
|
||||
|
@ -14,9 +14,11 @@ import org.apache.commons.codec.Charsets;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class EncryptionTest {
|
||||
@ -67,4 +69,19 @@ public class EncryptionTest {
|
||||
public void testCryptoVersionOutOfRange() throws Exceptions.IntegrityException, Exceptions.VersionTooNewException {
|
||||
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