Update bouncycastle, cleanup, renamings

This commit is contained in:
Manfred Karrer 2015-11-02 01:57:00 +01:00
parent 07a9c7fd52
commit 3711519e3a
26 changed files with 267 additions and 210 deletions

View file

@ -22,11 +22,6 @@ import io.bitsquare.app.Version;
import java.io.Serializable;
import java.security.PublicKey;
/**
* Packs the encrypted symmetric secretKey and the encrypted and signed message into one object.
* SecretKey is encrypted with asymmetric pubKey of peer. Signed message is encrypted with secretKey.
* Using that hybrid encryption model we are not restricted by data size and performance as symmetric encryption is very fast.
*/
public final class DecryptedPayloadWithPubKey implements Serializable {
// That object is sent over the wire, so we need to take care of version compatibility.
private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
@ -39,4 +34,30 @@ public final class DecryptedPayloadWithPubKey implements Serializable {
this.sigPublicKey = sigPublicKey;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof DecryptedPayloadWithPubKey)) return false;
DecryptedPayloadWithPubKey that = (DecryptedPayloadWithPubKey) o;
if (payload != null ? !payload.equals(that.payload) : that.payload != null) return false;
return !(sigPublicKey != null ? !sigPublicKey.equals(that.sigPublicKey) : that.sigPublicKey != null);
}
@Override
public int hashCode() {
int result = payload != null ? payload.hashCode() : 0;
result = 31 * result + (sigPublicKey != null ? sigPublicKey.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "DecryptedPayloadWithPubKey{" +
"payload=" + payload +
", sigPublicKey.hashCode()=" + sigPublicKey.hashCode() +
'}';
}
}

View file

@ -30,24 +30,28 @@ import java.io.Serializable;
import java.security.*;
import java.util.Arrays;
// TODO: which counter modes and paddings should we use?
// https://security.stackexchange.com/questions/52665/which-is-the-best-cipher-mode-and-padding-mode-for-aes-encryption
public class Encryption {
private static final Logger log = LoggerFactory.getLogger(Encryption.class);
public static final String ENCR_KEY_ALGO = "RSA";
public static final String SYM_KEY_ALGO = "AES";
public static final String SYM_CIPHER = "AES"; // java.security.NoSuchAlgorithmException: AES/ECB/PKCS5Padding KeyGenerator not available
public static final String ASYM_CIPHER = "RSA"; // TODO test with RSA/ECB/PKCS1Padding
public static final String ASYM_KEY_ALGO = "RSA"; // RSA/NONE/OAEPWithSHA256AndMGF1Padding
public static final String ASYM_CIPHER = "RSA";
public static final String SYM_KEY_ALGO = "AES"; // AES/CTR/NoPadding
public static final String SYM_CIPHER = "AES";
public static final String HMAC = "HmacSHA256";
public static KeyPair generateKeyPair() {
long ts = System.currentTimeMillis();
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ENCR_KEY_ALGO);
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ASYM_KEY_ALGO, "BC");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.genKeyPair();
log.trace("Generate msgEncryptionKeyPair needed {} ms", System.currentTimeMillis() - ts);
return keyPair;
} catch (NoSuchAlgorithmException e) {
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
e.printStackTrace();
throw new RuntimeException("Could not create key.");
}
@ -60,11 +64,11 @@ public class Encryption {
public static byte[] encrypt(byte[] payload, SecretKey secretKey) throws CryptoException {
try {
Cipher cipher = Cipher.getInstance(SYM_CIPHER);
Cipher cipher = Cipher.getInstance(SYM_CIPHER, "BC");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(payload);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| BadPaddingException | IllegalBlockSizeException e) {
| BadPaddingException | IllegalBlockSizeException | NoSuchProviderException e) {
e.printStackTrace();
throw new CryptoException(e);
}
@ -72,11 +76,11 @@ public class Encryption {
public static byte[] decrypt(byte[] encryptedPayload, SecretKey secretKey) throws CryptoException {
try {
Cipher cipher = Cipher.getInstance(SYM_CIPHER);
Cipher cipher = Cipher.getInstance(SYM_CIPHER, "BC");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return cipher.doFinal(encryptedPayload);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| BadPaddingException | IllegalBlockSizeException e) {
| BadPaddingException | IllegalBlockSizeException | NoSuchProviderException e) {
e.printStackTrace();
throw new CryptoException(e);
}
@ -99,7 +103,7 @@ public class Encryption {
outputStream.write(hmac);
outputStream.flush();
payloadWithHmac = outputStream.toByteArray().clone();
} catch (IOException e) {
} catch (IOException | NoSuchProviderException e) {
e.printStackTrace();
throw new RuntimeException("Could not create hmac");
} finally {
@ -122,14 +126,14 @@ public class Encryption {
try {
byte[] hmacTest = getHmac(message, secretKey);
return Arrays.equals(hmacTest, hmac);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
} catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchProviderException e) {
e.printStackTrace();
throw new RuntimeException("Could not create cipher");
}
}
private static byte[] getHmac(byte[] payload, SecretKey secretKey) throws NoSuchAlgorithmException, InvalidKeyException {
Mac mac = Mac.getInstance(HMAC);
private static byte[] getHmac(byte[] payload, SecretKey secretKey) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException {
Mac mac = Mac.getInstance(HMAC, "BC");
mac.init(secretKey);
return mac.doFinal(payload);
}
@ -170,11 +174,11 @@ public class Encryption {
public static byte[] encrypt(byte[] payload, PublicKey publicKey) throws CryptoException {
try {
Cipher cipher = Cipher.getInstance(ASYM_CIPHER);
Cipher cipher = Cipher.getInstance(ASYM_CIPHER, "BC");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(payload);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| BadPaddingException | IllegalBlockSizeException e) {
| BadPaddingException | IllegalBlockSizeException | NoSuchProviderException e) {
e.printStackTrace();
throw new CryptoException("Couldn't encrypt payload");
}
@ -182,11 +186,11 @@ public class Encryption {
public static byte[] decrypt(byte[] encryptedPayload, PrivateKey privateKey) throws CryptoException {
try {
Cipher cipher = Cipher.getInstance(ASYM_CIPHER);
Cipher cipher = Cipher.getInstance(ASYM_CIPHER, "BC");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(encryptedPayload);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| BadPaddingException | IllegalBlockSizeException e) {
| BadPaddingException | IllegalBlockSizeException | NoSuchProviderException e) {
// errors when trying to decrypt foreign messages are normal
throw new CryptoException(e);
}
@ -199,14 +203,13 @@ public class Encryption {
/**
* @param payload The data to encrypt.
* @param sigPrivateKey The private key for signing.
* @param sigPublicKey The public key used for signing.
* @param signatureKeyPair The key pair for signing.
* @param encryptionPublicKey The public key used for encryption.
* @return A SealedAndSigned object.
* @throws CryptoException
*/
public static SealedAndSigned encryptHybridWithSignature(Serializable payload, PrivateKey sigPrivateKey,
PublicKey sigPublicKey, PublicKey encryptionPublicKey)
public static SealedAndSigned encryptHybridWithSignature(Serializable payload, KeyPair signatureKeyPair,
PublicKey encryptionPublicKey)
throws CryptoException {
// Create a symmetric key
SecretKey secretKey = generateSecretKey();
@ -214,22 +217,15 @@ public class Encryption {
// Encrypt secretKey with receivers publicKey
byte[] encryptedSecretKey = encrypt(secretKey.getEncoded(), encryptionPublicKey);
// Encrypt with sym key and add hmac
// Encrypt with sym key payload with appended hmac
byte[] encryptedPayloadWithHmac = encryptPayloadWithHmac(payload, secretKey);
// sign hash of encryptedPayloadWithHmac
byte[] hash = Hash.getHash(encryptedPayloadWithHmac);
try {
byte[] signature = Sig.sign(sigPrivateKey, hash);
// Pack all together
return new SealedAndSigned(encryptedSecretKey, encryptedPayloadWithHmac, signature, sigPublicKey);
} catch (SignatureException | NoSuchAlgorithmException | InvalidKeyException e) {
e.printStackTrace();
throw new CryptoException(e);
}
byte[] signature = Sig.sign(signatureKeyPair.getPrivate(), hash);
// Pack all together
return new SealedAndSigned(encryptedSecretKey, encryptedPayloadWithHmac, signature, signatureKeyPair.getPublic());
}
/**
@ -240,18 +236,14 @@ public class Encryption {
*/
public static DecryptedPayloadWithPubKey decryptHybridWithSignature(SealedAndSigned sealedAndSigned, PrivateKey privateKey) throws CryptoException {
SecretKey secretKey = getSecretKeyFromBytes(decrypt(sealedAndSigned.encryptedSecretKey, privateKey));
try {
boolean isValid = Sig.verify(sealedAndSigned.sigPublicKey,
Hash.getHash(sealedAndSigned.encryptedPayloadWithHmac),
sealedAndSigned.signature);
if (!isValid)
throw new CryptoException("Signature verification failed.");
Serializable decryptedPayload = Utilities.deserialize(decryptPayloadWithHmac(sealedAndSigned.encryptedPayloadWithHmac, secretKey));
return new DecryptedPayloadWithPubKey(decryptedPayload, sealedAndSigned.sigPublicKey);
} catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException e) {
boolean isValid = Sig.verify(sealedAndSigned.sigPublicKey,
Hash.getHash(sealedAndSigned.encryptedPayloadWithHmac),
sealedAndSigned.signature);
if (!isValid)
throw new CryptoException("Signature verification failed.");
}
Serializable decryptedPayload = Utilities.deserialize(decryptPayloadWithHmac(sealedAndSigned.encryptedPayloadWithHmac, secretKey));
return new DecryptedPayloadWithPubKey(decryptedPayload, sealedAndSigned.sigPublicKey);
}
@ -265,10 +257,11 @@ public class Encryption {
private static SecretKey generateSecretKey() {
try {
KeyGenerator keyPairGenerator = KeyGenerator.getInstance(SYM_CIPHER);
KeyGenerator keyPairGenerator = KeyGenerator.getInstance(SYM_CIPHER, "BC");
keyPairGenerator.init(256);
return keyPairGenerator.generateKey();
} catch (NoSuchAlgorithmException e) {
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
e.printStackTrace();
throw new RuntimeException("Couldn't generate key");
}
}

View file

@ -27,6 +27,7 @@ import java.io.Serializable;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
public class Hash {
private static final Logger log = LoggerFactory.getLogger(Hash.class);
@ -38,8 +39,8 @@ public class Hash {
public static byte[] getHash(byte[] data) {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
digest = MessageDigest.getInstance("SHA-256", "BC");
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
log.error("Could not create MessageDigest for hash. " + e.getMessage());
throw new RuntimeException(e);
}

View file

@ -21,7 +21,6 @@ import javax.inject.Inject;
import java.security.KeyPair;
public class KeyRing {
private final KeyPair signatureKeyPair;
private final KeyPair encryptionKeyPair;
private final PubKeyRing pubKeyRing;
@ -52,4 +51,36 @@ public class KeyRing {
public PubKeyRing getPubKeyRing() {
return pubKeyRing;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof KeyRing)) return false;
KeyRing keyRing = (KeyRing) o;
if (signatureKeyPair != null ? !signatureKeyPair.equals(keyRing.signatureKeyPair) : keyRing.signatureKeyPair != null)
return false;
if (encryptionKeyPair != null ? !encryptionKeyPair.equals(keyRing.encryptionKeyPair) : keyRing.encryptionKeyPair != null)
return false;
return !(pubKeyRing != null ? !pubKeyRing.equals(keyRing.pubKeyRing) : keyRing.pubKeyRing != null);
}
@Override
public int hashCode() {
int result = signatureKeyPair != null ? signatureKeyPair.hashCode() : 0;
result = 31 * result + (encryptionKeyPair != null ? encryptionKeyPair.hashCode() : 0);
result = 31 * result + (pubKeyRing != null ? pubKeyRing.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "KeyRing{" +
"signatureKeyPair.hashCode()=" + signatureKeyPair.hashCode() +
", encryptionKeyPair.hashCode()=" + encryptionKeyPair.hashCode() +
", pubKeyRing.hashCode()=" + pubKeyRing.hashCode() +
'}';
}
}

View file

@ -34,6 +34,7 @@ import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.spec.*;
// TODO: use a password protection for storage?
public class KeyStorage {
private static final Logger log = LoggerFactory.getLogger(KeyStorage.class);
@ -41,7 +42,7 @@ public class KeyStorage {
public enum KeyEntry {
MSG_SIGNATURE("sig", Sig.KEY_ALGO),
MSG_ENCRYPTION("enc", Encryption.ENCR_KEY_ALGO);
MSG_ENCRYPTION("enc", Encryption.ASYM_KEY_ALGO);
private final String fileName;
private final String algorithm;
@ -87,7 +88,7 @@ public class KeyStorage {
public KeyPair loadKeyPair(KeyEntry keyEntry) {
// long now = System.currentTimeMillis();
try {
KeyFactory keyFactory = KeyFactory.getInstance(keyEntry.getAlgorithm());
KeyFactory keyFactory = KeyFactory.getInstance(keyEntry.getAlgorithm(), "BC");
PublicKey publicKey;
PrivateKey privateKey;
@ -123,7 +124,7 @@ public class KeyStorage {
//log.info("load completed in {} msec", System.currentTimeMillis() - now);
return new KeyPair(publicKey, privateKey);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
} catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchProviderException e) {
e.printStackTrace();
log.error(e.getMessage());
throw new RuntimeException("Could not load key " + keyEntry.toString(), e);

View file

@ -26,13 +26,14 @@ import java.io.ObjectInputStream;
import java.io.Serializable;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
/**
* Same as KeyRing but with public keys only.
* Used to sent over the wire to other peer.
* Used to send public keys over the wire to other peer.
*/
public class PubKeyRing implements Serializable {
// That object is sent over the wire, so we need to take care of version compatibility.
@ -57,9 +58,9 @@ public class PubKeyRing implements Serializable {
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
try {
in.defaultReadObject();
signaturePubKey = KeyFactory.getInstance(Sig.KEY_ALGO).generatePublic(new X509EncodedKeySpec(signaturePubKeyBytes));
encryptionPubKey = KeyFactory.getInstance(Encryption.ENCR_KEY_ALGO).generatePublic(new X509EncodedKeySpec(encryptionPubKeyBytes));
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
signaturePubKey = KeyFactory.getInstance(Sig.KEY_ALGO, "BC").generatePublic(new X509EncodedKeySpec(signaturePubKeyBytes));
encryptionPubKey = KeyFactory.getInstance(Encryption.ASYM_KEY_ALGO, "BC").generatePublic(new X509EncodedKeySpec(encryptionPubKeyBytes));
} catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchProviderException e) {
e.printStackTrace();
log.error(e.getMessage());
} catch (Throwable t) {
@ -98,8 +99,8 @@ public class PubKeyRing implements Serializable {
@Override
public String toString() {
return "PubKeyRing{" +
"\n\nmsgSignaturePubKey=\n" + Util.pubKeyToString(getSignaturePubKey()) +
"\n\nmsgEncryptionPubKey=\n" + Util.pubKeyToString(getEncryptionPubKey()) +
"\n\nsignaturePubKey=\n" + Util.pubKeyToString(getSignaturePubKey()) +
"\n\nencryptionPubKey=\n" + Util.pubKeyToString(getEncryptionPubKey()) +
'}';
}

View file

@ -23,11 +23,6 @@ import java.io.Serializable;
import java.security.PublicKey;
import java.util.Arrays;
/**
* Packs the encrypted symmetric secretKey and the encrypted and signed message into one object.
* SecretKey is encrypted with asymmetric pubKey of peer. Signed message is encrypted with secretKey.
* Using that hybrid encryption model we are not restricted by data size and performance as symmetric encryption is very fast.
*/
public final class SealedAndSigned implements Serializable {
// That object is sent over the wire, so we need to take care of version compatibility.
private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;

View file

@ -36,7 +36,7 @@ public class Sig {
private static final Logger log = LoggerFactory.getLogger(Sig.class);
public static final String KEY_ALGO = "DSA";
public static final String ALGO = "SHA1withDSA"; //TODO better SHA512withECDSA
public static final String ALGO = "SHA256withDSA";
/**
@ -45,12 +45,12 @@ public class Sig {
public static KeyPair generateKeyPair() {
long ts = System.currentTimeMillis();
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGO);
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGO, "BC");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
log.trace("Generate msgSignatureKeyPair needed {} ms", System.currentTimeMillis() - ts);
return keyPair;
} catch (NoSuchAlgorithmException e) {
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
e.printStackTrace();
throw new RuntimeException("Could not create key.");
}
@ -65,12 +65,15 @@ public class Sig {
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
*/
public static byte[] sign(PrivateKey privateKey, byte[] data)
throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
Signature sig = Signature.getInstance(ALGO);
sig.initSign(privateKey);
sig.update(data);
return sig.sign();
public static byte[] sign(PrivateKey privateKey, byte[] data) throws CryptoException {
try {
Signature sig = Signature.getInstance(ALGO, "BC");
sig.initSign(privateKey);
sig.update(data);
return sig.sign();
} catch (SignatureException | NoSuchProviderException | InvalidKeyException | NoSuchAlgorithmException e) {
throw new CryptoException("Signing failed. " + e.getMessage());
}
}
/**
@ -81,8 +84,7 @@ public class Sig {
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
*/
public static String sign(PrivateKey privateKey, String message)
throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
public static String sign(PrivateKey privateKey, String message) throws CryptoException {
byte[] sigAsBytes = sign(privateKey, message.getBytes(Charsets.UTF_8));
return Base64.toBase64String(sigAsBytes);
}
@ -96,12 +98,16 @@ public class Sig {
* @throws InvalidKeyException
* @throws SignatureException
*/
public static boolean verify(PublicKey publicKey, byte[] data, byte[] signature)
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
Signature sig = Signature.getInstance(ALGO);
sig.initVerify(publicKey);
sig.update(data);
return sig.verify(signature);
public static boolean verify(PublicKey publicKey, byte[] data, byte[] signature) throws CryptoException {
byte[] sigAsBytes = new byte[0];
try {
Signature sig = Signature.getInstance(ALGO, "BC");
sig.initVerify(publicKey);
sig.update(data);
return sig.verify(signature);
} catch (SignatureException | NoSuchProviderException | InvalidKeyException | NoSuchAlgorithmException e) {
throw new CryptoException("Signature verification failed. " + e.getMessage());
}
}
/**
@ -113,8 +119,7 @@ public class Sig {
* @throws InvalidKeyException
* @throws SignatureException
*/
public static boolean verify(PublicKey publicKey, String message, String signature)
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
public static boolean verify(PublicKey publicKey, String message, String signature) throws CryptoException {
return verify(publicKey, message.getBytes(Charsets.UTF_8), Base64.decode(signature));
}
}

View file

@ -17,18 +17,14 @@
package io.bitsquare.common.crypto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class Util {
private static final Logger log = LoggerFactory.getLogger(Util.class);
public static String pubKeyToString(PublicKey publicKey) {
final X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
return java.util.Base64.getEncoder().encodeToString(x509EncodedKeySpec.getEncoded());
return Base64.getEncoder().encodeToString(x509EncodedKeySpec.getEncoded());
}
}