mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-05-19 23:10:48 -04:00
Add Hmac to encrypted object, refactor crypto code (WIP)
This commit is contained in:
parent
4604e9c0bd
commit
4883c90030
33 changed files with 783 additions and 443 deletions
|
@ -1,136 +0,0 @@
|
|||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.common.crypto;
|
||||
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.Utils;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.util.encoders.Base64;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.*;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
||||
public class CryptoUtil {
|
||||
private static final Logger log = LoggerFactory.getLogger(CryptoUtil.class);
|
||||
public static final String STORAGE_SIGN_KEY_ALGO = "DSA";
|
||||
public static final String MSG_SIGN_KEY_ALGO = "DSA";
|
||||
public static final String MSG_ENCR_KEY_ALGO = "RSA";
|
||||
|
||||
public static final String SYM_ENCR_KEY_ALGO = "AES";
|
||||
public static final String SYM_CIPHER = "AES";
|
||||
public static final String ASYM_CIPHER = "RSA"; //RSA/ECB/PKCS1Padding
|
||||
public static final String MSG_SIGN_ALGO = "SHA1withDSA";
|
||||
|
||||
|
||||
public static KeyPair generateStorageSignatureKeyPair() throws NoSuchAlgorithmException {
|
||||
long ts = System.currentTimeMillis();
|
||||
final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(STORAGE_SIGN_KEY_ALGO);
|
||||
keyPairGenerator.initialize(1024);
|
||||
KeyPair keyPair = keyPairGenerator.genKeyPair();
|
||||
log.trace("Generate storageSignatureKeyPair needed {} ms", System.currentTimeMillis() - ts);
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
public static KeyPair generateMsgSignatureKeyPair() throws NoSuchAlgorithmException {
|
||||
long ts = System.currentTimeMillis();
|
||||
final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(MSG_SIGN_KEY_ALGO);
|
||||
keyPairGenerator.initialize(1024);
|
||||
KeyPair keyPair = keyPairGenerator.genKeyPair();
|
||||
log.trace("Generate msgSignatureKeyPair needed {} ms", System.currentTimeMillis() - ts);
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
public static KeyPair generateMsgEncryptionKeyPair() throws NoSuchAlgorithmException {
|
||||
long ts = System.currentTimeMillis();
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(MSG_ENCR_KEY_ALGO);
|
||||
keyPairGenerator.initialize(2048);
|
||||
KeyPair keyPair = keyPairGenerator.genKeyPair();
|
||||
log.trace("Generate msgEncryptionKeyPair needed {} ms", System.currentTimeMillis() - ts);
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
static {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
|
||||
public static String signMessage(PrivateKey privateKey, String message)
|
||||
throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
|
||||
Signature sig = Signature.getInstance(MSG_SIGN_ALGO);
|
||||
sig.initSign(privateKey);
|
||||
sig.update(message.getBytes());
|
||||
return Base64.toBase64String(sig.sign());
|
||||
}
|
||||
|
||||
public static boolean verifyMessage(PublicKey publicKey, String message, String signature)
|
||||
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
|
||||
Signature sig = Signature.getInstance(MSG_SIGN_ALGO);
|
||||
sig.initVerify(publicKey);
|
||||
sig.update(message.getBytes());
|
||||
return sig.verify(Base64.decode(signature));
|
||||
}
|
||||
|
||||
public static byte[] signStorageData(PrivateKey privateKey, byte[] data)
|
||||
throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
|
||||
Signature sig = Signature.getInstance(MSG_SIGN_ALGO);
|
||||
sig.initSign(privateKey);
|
||||
sig.update(data);
|
||||
return sig.sign();
|
||||
}
|
||||
|
||||
public static boolean verifyStorageData(PublicKey publicKey, byte[] data, byte[] signature)
|
||||
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
|
||||
Signature sig = Signature.getInstance(MSG_SIGN_ALGO);
|
||||
sig.initVerify(publicKey);
|
||||
sig.update(data);
|
||||
return sig.verify(signature);
|
||||
}
|
||||
|
||||
public static byte[] getHash(Integer data) {
|
||||
return Sha256Hash.hash(ByteBuffer.allocate(4).putInt(data).array());
|
||||
}
|
||||
|
||||
public static byte[] getHash(Object data) {
|
||||
return Sha256Hash.hash(Utilities.objectToByteArray(data));
|
||||
}
|
||||
|
||||
public static byte[] getHash(String message) {
|
||||
return Sha256Hash.hash(Utils.formatMessageForSigning(message));
|
||||
}
|
||||
|
||||
public static String getHashAsHex(String text) {
|
||||
return Utils.HEX.encode(Sha256Hash.hash(Utils.formatMessageForSigning(text)));
|
||||
}
|
||||
|
||||
public static String pubKeyToString(PublicKey publicKey) {
|
||||
final X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
|
||||
return java.util.Base64.getEncoder().encodeToString(x509EncodedKeySpec.getEncoded());
|
||||
}
|
||||
|
||||
// TODO just temp for arbitrator
|
||||
public static PublicKey decodeDSAPubKeyHex(String pubKeyHex) throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(Utils.HEX.decode(pubKeyHex));
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
|
||||
return keyFactory.generatePublic(pubKeySpec);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.common.crypto;
|
||||
|
||||
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;
|
||||
|
||||
public final Serializable payload;
|
||||
public final PublicKey sigPublicKey;
|
||||
|
||||
public DecryptedPayloadWithPubKey(Serializable payload, PublicKey sigPublicKey) {
|
||||
this.payload = payload;
|
||||
this.sigPublicKey = sigPublicKey;
|
||||
}
|
||||
|
||||
}
|
276
common/src/main/java/io/bitsquare/common/crypto/Encryption.java
Normal file
276
common/src/main/java/io/bitsquare/common/crypto/Encryption.java
Normal file
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.common.crypto;
|
||||
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import javax.crypto.*;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.security.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
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 HMAC = "HmacSHA256";
|
||||
|
||||
public static KeyPair generateEncryptionKeyPair() {
|
||||
long ts = System.currentTimeMillis();
|
||||
try {
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ENCR_KEY_ALGO);
|
||||
keyPairGenerator.initialize(2048);
|
||||
KeyPair keyPair = keyPairGenerator.genKeyPair();
|
||||
log.trace("Generate msgEncryptionKeyPair needed {} ms", System.currentTimeMillis() - ts);
|
||||
return keyPair;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Could not create key.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Symmetric
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static byte[] encrypt(byte[] payload, SecretKey secretKey) throws CryptoException {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(SYM_CIPHER);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
|
||||
return cipher.doFinal(payload);
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
|
||||
| BadPaddingException | IllegalBlockSizeException e) {
|
||||
e.printStackTrace();
|
||||
throw new CryptoException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] decrypt(byte[] encryptedPayload, SecretKey secretKey) throws CryptoException {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(SYM_CIPHER);
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey);
|
||||
return cipher.doFinal(encryptedPayload);
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
|
||||
| BadPaddingException | IllegalBlockSizeException e) {
|
||||
e.printStackTrace();
|
||||
throw new CryptoException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Hmac
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private static byte[] getPayloadWithHmac(byte[] payload, SecretKey secretKey) {
|
||||
byte[] payloadWithHmac;
|
||||
try {
|
||||
|
||||
ByteArrayOutputStream outputStream = null;
|
||||
try {
|
||||
byte[] hmac = getHmac(payload, secretKey);
|
||||
outputStream = new ByteArrayOutputStream();
|
||||
outputStream.write(payload);
|
||||
outputStream.write(hmac);
|
||||
outputStream.flush();
|
||||
payloadWithHmac = outputStream.toByteArray().clone();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Could not create hmac");
|
||||
} finally {
|
||||
if (outputStream != null) {
|
||||
try {
|
||||
outputStream.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Could not create hmac");
|
||||
}
|
||||
return payloadWithHmac;
|
||||
}
|
||||
|
||||
|
||||
private static boolean verifyHmac(byte[] message, byte[] hmac, SecretKey secretKey) throws CryptoException {
|
||||
try {
|
||||
byte[] hmacTest = getHmac(message, secretKey);
|
||||
return Arrays.equals(hmacTest, hmac);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException 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);
|
||||
mac.init(secretKey);
|
||||
return mac.doFinal(payload);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Symmetric with Hmac
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static byte[] encryptPayloadWithHmac(Serializable object, SecretKey secretKey) throws CryptoException {
|
||||
return encryptPayloadWithHmac(Utilities.serialize(object), secretKey);
|
||||
}
|
||||
|
||||
public static byte[] encryptPayloadWithHmac(byte[] payload, SecretKey secretKey) throws CryptoException {
|
||||
return encrypt(getPayloadWithHmac(payload, secretKey), secretKey);
|
||||
}
|
||||
|
||||
public static byte[] decryptPayloadWithHmac(byte[] encryptedPayloadWithHmac, SecretKey secretKey) throws CryptoException {
|
||||
byte[] payloadWithHmac = decrypt(encryptedPayloadWithHmac, secretKey);
|
||||
String payloadWithHmacAsHex = Hex.toHexString(payloadWithHmac);
|
||||
// first part is raw message
|
||||
int length = payloadWithHmacAsHex.length();
|
||||
int sep = length - 64;
|
||||
String payloadAsHex = payloadWithHmacAsHex.substring(0, sep);
|
||||
// last 64 bytes is hmac
|
||||
String hmacAsHex = payloadWithHmacAsHex.substring(sep, length);
|
||||
if (verifyHmac(Hex.decode(payloadAsHex), Hex.decode(hmacAsHex), secretKey)) {
|
||||
return Hex.decode(payloadAsHex);
|
||||
} else {
|
||||
throw new CryptoException("Hmac does not match.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Asymmetric
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static byte[] encrypt(byte[] payload, PublicKey publicKey) throws CryptoException {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(ASYM_CIPHER);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||
return cipher.doFinal(payload);
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
|
||||
| BadPaddingException | IllegalBlockSizeException e) {
|
||||
e.printStackTrace();
|
||||
throw new CryptoException("Couldn't encrypt payload");
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] decrypt(byte[] encryptedPayload, PrivateKey privateKey) throws CryptoException {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(ASYM_CIPHER);
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||
return cipher.doFinal(encryptedPayload);
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
|
||||
| BadPaddingException | IllegalBlockSizeException e) {
|
||||
e.printStackTrace();
|
||||
throw new CryptoException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Hybrid with signature of asymmetric key
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @param payload The data to encrypt.
|
||||
* @param sigPrivateKey The private key for signing.
|
||||
* @param sigPublicKey The public key used 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)
|
||||
throws CryptoException {
|
||||
// Create a symmetric key
|
||||
SecretKey secretKey = generateSecretKey();
|
||||
|
||||
// Encrypt secretKey with receivers publicKey
|
||||
byte[] encryptedSecretKey = encrypt(secretKey.getEncoded(), encryptionPublicKey);
|
||||
|
||||
// Encrypt with sym key and add 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sealedAndSigned The sealedAndSigned object.
|
||||
* @param privateKey The private key for decryption
|
||||
* @return A DecryptedPayloadWithPubKey object.
|
||||
* @throws CryptoException
|
||||
*/
|
||||
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) {
|
||||
throw new CryptoException("Signature verification failed.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private static SecretKey getSecretKeyFromBytes(byte[] encodedKey) {
|
||||
return new SecretKeySpec(encodedKey, SYM_KEY_ALGO);
|
||||
}
|
||||
|
||||
private static SecretKey generateSecretKey() {
|
||||
try {
|
||||
KeyGenerator keyPairGenerator = KeyGenerator.getInstance(SYM_CIPHER);
|
||||
keyPairGenerator.init(256);
|
||||
return keyPairGenerator.generateKey();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("Couldn't generate key");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
83
common/src/main/java/io/bitsquare/common/crypto/Hash.java
Normal file
83
common/src/main/java/io/bitsquare/common/crypto/Hash.java
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.common.crypto;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import io.bitsquare.common.util.Utilities;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class Hash {
|
||||
private static final Logger log = LoggerFactory.getLogger(Hash.class);
|
||||
|
||||
/**
|
||||
* @param data Data as byte array
|
||||
* @return Hash of data
|
||||
*/
|
||||
public static byte[] getHash(byte[] data) {
|
||||
MessageDigest digest;
|
||||
try {
|
||||
digest = MessageDigest.getInstance("SHA-256");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
log.error("Could not create MessageDigest for hash. " + e.getMessage());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
digest.update(data, 0, data.length);
|
||||
return digest.digest();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param data Any serializable object. Will be converted into a byte array using Java serialisation.
|
||||
* @return Hash of data
|
||||
*/
|
||||
public static byte[] getHash(Serializable data) {
|
||||
return getHash(Utilities.serialize(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message UTF-8 encoded message
|
||||
* @return Hash of data
|
||||
*/
|
||||
public static byte[] getHash(String message) {
|
||||
return getHash(message.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message UTF-8 encoded message
|
||||
* @return Hex string of hash of data
|
||||
*/
|
||||
public static String getHashAsHex(String message) {
|
||||
return Hex.toHexString(message.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param data data as Integer
|
||||
* @return Hash of data
|
||||
*/
|
||||
public static byte[] getHash(Integer data) {
|
||||
return getHash(ByteBuffer.allocate(4).putInt(data).array());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -22,77 +22,42 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import javax.inject.Inject;
|
||||
import java.security.KeyPair;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class KeyRing {
|
||||
private static final Logger log = LoggerFactory.getLogger(KeyRing.class);
|
||||
|
||||
// Used for signing storage data
|
||||
private KeyPair storageSignatureKeyPair;
|
||||
// Used for signing messages sent over the wire
|
||||
private KeyPair msgSignatureKeyPair;
|
||||
private final KeyPair signatureKeyPair;
|
||||
// Used for encrypting messages sent over the wire (hybrid encryption scheme is used, so it is used only to encrypt a symmetric session key)
|
||||
private KeyPair msgEncryptionKeyPair;
|
||||
private final KeyPair encryptionKeyPair;
|
||||
|
||||
private PubKeyRing pubKeyRing;
|
||||
private final KeyStorage keyStorage;
|
||||
private final PubKeyRing pubKeyRing;
|
||||
|
||||
@Inject
|
||||
public KeyRing(KeyStorage keyStorage) throws CryptoException {
|
||||
this.keyStorage = keyStorage;
|
||||
|
||||
init(keyStorage);
|
||||
}
|
||||
|
||||
// consider extra thread for loading (takes about 264 ms at first startup, then load only takes nearly no time)
|
||||
private void init(KeyStorage keyStorage) throws CryptoException {
|
||||
public KeyRing(KeyStorage keyStorage) {
|
||||
if (keyStorage.allKeyFilesExist()) {
|
||||
storageSignatureKeyPair = keyStorage.loadKeyPair(KeyStorage.Key.STORAGE_SIGNATURE);
|
||||
msgSignatureKeyPair = keyStorage.loadKeyPair(KeyStorage.Key.MSG_SIGNATURE);
|
||||
msgEncryptionKeyPair = keyStorage.loadKeyPair(KeyStorage.Key.MSG_ENCRYPTION);
|
||||
signatureKeyPair = keyStorage.loadKeyPair(KeyStorage.Key.MSG_SIGNATURE);
|
||||
encryptionKeyPair = keyStorage.loadKeyPair(KeyStorage.Key.MSG_ENCRYPTION);
|
||||
} else {
|
||||
// First time we create key pairs
|
||||
try {
|
||||
this.storageSignatureKeyPair = CryptoUtil.generateStorageSignatureKeyPair();
|
||||
this.msgSignatureKeyPair = CryptoUtil.generateMsgSignatureKeyPair();
|
||||
this.msgEncryptionKeyPair = CryptoUtil.generateMsgEncryptionKeyPair();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
throw new CryptoException("Error at KeyRing constructor ", e);
|
||||
}
|
||||
signatureKeyPair = Sig.generateKeyPair();
|
||||
encryptionKeyPair = Encryption.generateEncryptionKeyPair();
|
||||
keyStorage.saveKeyRing(this);
|
||||
}
|
||||
|
||||
pubKeyRing = new PubKeyRing(storageSignatureKeyPair.getPublic(), msgSignatureKeyPair.getPublic(), msgEncryptionKeyPair.getPublic());
|
||||
}
|
||||
|
||||
// For unit testing
|
||||
KeyRing() throws NoSuchAlgorithmException {
|
||||
keyStorage = null;
|
||||
this.storageSignatureKeyPair = CryptoUtil.generateStorageSignatureKeyPair();
|
||||
this.msgSignatureKeyPair = CryptoUtil.generateMsgSignatureKeyPair();
|
||||
this.msgEncryptionKeyPair = CryptoUtil.generateMsgEncryptionKeyPair();
|
||||
pubKeyRing = new PubKeyRing(storageSignatureKeyPair.getPublic(), msgSignatureKeyPair.getPublic(), msgEncryptionKeyPair.getPublic());
|
||||
}
|
||||
|
||||
KeyRing(KeyPair storageSignatureKeyPair, KeyPair msgSignatureKeyPair, KeyPair msgEncryptionKeyPair) {
|
||||
this.keyStorage = null;
|
||||
this.storageSignatureKeyPair = storageSignatureKeyPair;
|
||||
this.msgSignatureKeyPair = msgSignatureKeyPair;
|
||||
this.msgEncryptionKeyPair = msgEncryptionKeyPair;
|
||||
pubKeyRing = new PubKeyRing(storageSignatureKeyPair.getPublic(), msgSignatureKeyPair.getPublic(), msgEncryptionKeyPair.getPublic());
|
||||
pubKeyRing = new PubKeyRing(signatureKeyPair.getPublic(), signatureKeyPair.getPublic(), encryptionKeyPair.getPublic());
|
||||
}
|
||||
|
||||
//TODO
|
||||
public KeyPair getStorageSignatureKeyPair() {
|
||||
return storageSignatureKeyPair;
|
||||
return signatureKeyPair;
|
||||
}
|
||||
|
||||
public KeyPair getMsgSignatureKeyPair() {
|
||||
return msgSignatureKeyPair;
|
||||
public KeyPair getSignatureKeyPair() {
|
||||
return signatureKeyPair;
|
||||
}
|
||||
|
||||
public KeyPair getMsgEncryptionKeyPair() {
|
||||
return msgEncryptionKeyPair;
|
||||
public KeyPair getEncryptionKeyPair() {
|
||||
return encryptionKeyPair;
|
||||
}
|
||||
|
||||
public PubKeyRing getPubKeyRing() {
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
package io.bitsquare.common.crypto;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -39,14 +38,10 @@ public class KeyStorage {
|
|||
|
||||
public static final String DIR_KEY = "key.storage.dir";
|
||||
|
||||
static {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
|
||||
public enum Key {
|
||||
STORAGE_SIGNATURE("storageSignature", CryptoUtil.STORAGE_SIGN_KEY_ALGO),
|
||||
MSG_SIGNATURE("msgSignature", CryptoUtil.MSG_SIGN_KEY_ALGO),
|
||||
MSG_ENCRYPTION("msgEncryption", CryptoUtil.MSG_ENCR_KEY_ALGO);
|
||||
STORAGE_SIGNATURE("storageSignature", Sig.KEY_ALGO),
|
||||
MSG_SIGNATURE("msgSignature", Sig.KEY_ALGO),
|
||||
MSG_ENCRYPTION("msgEncryption", Encryption.ENCR_KEY_ALGO);
|
||||
|
||||
private final String fileName;
|
||||
private final String algorithm;
|
||||
|
@ -81,7 +76,7 @@ public class KeyStorage {
|
|||
this.storageDir = storageDir;
|
||||
}
|
||||
|
||||
public boolean allKeyFilesExist() throws CryptoException {
|
||||
public boolean allKeyFilesExist() {
|
||||
return fileExists(KeyStorage.Key.STORAGE_SIGNATURE) && fileExists(KeyStorage.Key.MSG_SIGNATURE) && fileExists(KeyStorage.Key.MSG_ENCRYPTION);
|
||||
}
|
||||
|
||||
|
@ -89,7 +84,7 @@ public class KeyStorage {
|
|||
return new File(storageDir + "/" + key.getFileName() + "Pub.key").exists();
|
||||
}
|
||||
|
||||
public KeyPair loadKeyPair(Key key) throws CryptoException {
|
||||
public KeyPair loadKeyPair(Key key) {
|
||||
// long now = System.currentTimeMillis();
|
||||
try {
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm());
|
||||
|
@ -106,7 +101,7 @@ public class KeyStorage {
|
|||
} catch (InvalidKeySpecException | IOException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
throw new CryptoException("Could not load key " + key.toString(), e);
|
||||
throw new RuntimeException("Could not load key " + key.toString(), e);
|
||||
}
|
||||
|
||||
File filePrivateKey = new File(storageDir + "/" + key.getFileName() + "Priv.key");
|
||||
|
@ -119,7 +114,7 @@ public class KeyStorage {
|
|||
} catch (InvalidKeySpecException | IOException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
throw new CryptoException("Could not load key " + key.toString(), e);
|
||||
throw new RuntimeException("Could not load key " + key.toString(), e);
|
||||
}
|
||||
//log.info("load completed in {} msec", System.currentTimeMillis() - now);
|
||||
return new KeyPair(publicKey, privateKey);
|
||||
|
@ -127,17 +122,17 @@ public class KeyStorage {
|
|||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
throw new CryptoException("Could not load key " + key.toString(), e);
|
||||
throw new RuntimeException("Could not load key " + key.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveKeyRing(KeyRing keyRing) throws CryptoException {
|
||||
public void saveKeyRing(KeyRing keyRing) {
|
||||
saveKeyPair(keyRing.getStorageSignatureKeyPair(), Key.STORAGE_SIGNATURE.getFileName());
|
||||
saveKeyPair(keyRing.getMsgSignatureKeyPair(), Key.MSG_SIGNATURE.getFileName());
|
||||
saveKeyPair(keyRing.getMsgEncryptionKeyPair(), Key.MSG_ENCRYPTION.getFileName());
|
||||
saveKeyPair(keyRing.getSignatureKeyPair(), Key.MSG_SIGNATURE.getFileName());
|
||||
saveKeyPair(keyRing.getEncryptionKeyPair(), Key.MSG_ENCRYPTION.getFileName());
|
||||
}
|
||||
|
||||
public void saveKeyPair(KeyPair keyPair, String name) throws CryptoException {
|
||||
public void saveKeyPair(KeyPair keyPair, String name) {
|
||||
if (!storageDir.exists())
|
||||
storageDir.mkdir();
|
||||
|
||||
|
@ -147,7 +142,7 @@ public class KeyStorage {
|
|||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
throw new CryptoException("Could not save key " + name, e);
|
||||
throw new RuntimeException("Could not save key " + name, e);
|
||||
}
|
||||
|
||||
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyPair.getPrivate().getEncoded());
|
||||
|
@ -156,7 +151,7 @@ public class KeyStorage {
|
|||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
throw new CryptoException("Could not save key " + name, e);
|
||||
throw new RuntimeException("Could not save key " + name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,22 +45,22 @@ public class PubKeyRing implements Serializable {
|
|||
|
||||
transient private PublicKey storageSignaturePubKey;
|
||||
transient private PublicKey msgSignaturePubKey;
|
||||
transient private PublicKey msgEncryptionPubKey;
|
||||
transient private PublicKey encryptionPubKey;
|
||||
|
||||
public PubKeyRing(PublicKey storageSignaturePubKey, PublicKey msgSignaturePubKey, PublicKey msgEncryptionPubKey) {
|
||||
public PubKeyRing(PublicKey storageSignaturePubKey, PublicKey msgSignaturePubKey, PublicKey encryptionPubKey) {
|
||||
this.storageSignaturePubKey = storageSignaturePubKey;
|
||||
this.msgSignaturePubKey = msgSignaturePubKey;
|
||||
this.msgEncryptionPubKey = msgEncryptionPubKey;
|
||||
this.encryptionPubKey = encryptionPubKey;
|
||||
|
||||
this.storageSignaturePubKeyBytes = new X509EncodedKeySpec(storageSignaturePubKey.getEncoded()).getEncoded();
|
||||
this.msgSignaturePubKeyBytes = new X509EncodedKeySpec(msgSignaturePubKey.getEncoded()).getEncoded();
|
||||
this.msgEncryptionPubKeyBytes = new X509EncodedKeySpec(msgEncryptionPubKey.getEncoded()).getEncoded();
|
||||
this.msgEncryptionPubKeyBytes = new X509EncodedKeySpec(encryptionPubKey.getEncoded()).getEncoded();
|
||||
}
|
||||
|
||||
public PublicKey getStorageSignaturePubKey() {
|
||||
if (storageSignaturePubKey == null) {
|
||||
try {
|
||||
storageSignaturePubKey = KeyFactory.getInstance(CryptoUtil.STORAGE_SIGN_KEY_ALGO).generatePublic(new X509EncodedKeySpec(storageSignaturePubKeyBytes));
|
||||
storageSignaturePubKey = KeyFactory.getInstance(Sig.KEY_ALGO).generatePublic(new X509EncodedKeySpec(storageSignaturePubKeyBytes));
|
||||
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
|
@ -73,7 +73,7 @@ public class PubKeyRing implements Serializable {
|
|||
public PublicKey getMsgSignaturePubKey() {
|
||||
if (msgSignaturePubKey == null) {
|
||||
try {
|
||||
msgSignaturePubKey = KeyFactory.getInstance(CryptoUtil.MSG_SIGN_KEY_ALGO).generatePublic(new X509EncodedKeySpec(msgSignaturePubKeyBytes));
|
||||
msgSignaturePubKey = KeyFactory.getInstance(Sig.KEY_ALGO).generatePublic(new X509EncodedKeySpec(msgSignaturePubKeyBytes));
|
||||
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
|
@ -82,16 +82,16 @@ public class PubKeyRing implements Serializable {
|
|||
return msgSignaturePubKey;
|
||||
}
|
||||
|
||||
public PublicKey getMsgEncryptionPubKey() {
|
||||
if (msgEncryptionPubKey == null) {
|
||||
public PublicKey getEncryptionPubKey() {
|
||||
if (encryptionPubKey == null) {
|
||||
try {
|
||||
msgEncryptionPubKey = KeyFactory.getInstance(CryptoUtil.MSG_ENCR_KEY_ALGO).generatePublic(new X509EncodedKeySpec(msgEncryptionPubKeyBytes));
|
||||
encryptionPubKey = KeyFactory.getInstance(Encryption.ENCR_KEY_ALGO).generatePublic(new X509EncodedKeySpec(msgEncryptionPubKeyBytes));
|
||||
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
return msgEncryptionPubKey;
|
||||
return encryptionPubKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -118,9 +118,9 @@ public class PubKeyRing implements Serializable {
|
|||
@Override
|
||||
public String toString() {
|
||||
return "PubKeyRing{" +
|
||||
"\nstorageSignaturePubKey=\n" + CryptoUtil.pubKeyToString(getStorageSignaturePubKey()) +
|
||||
"\n\nmsgSignaturePubKey=\n" + CryptoUtil.pubKeyToString(getMsgSignaturePubKey()) +
|
||||
"\n\nmsgEncryptionPubKey=\n" + CryptoUtil.pubKeyToString(getMsgEncryptionPubKey()) +
|
||||
"\nstorageSignaturePubKey=\n" + Util.pubKeyToString(getStorageSignaturePubKey()) +
|
||||
"\n\nmsgSignaturePubKey=\n" + Util.pubKeyToString(getMsgSignaturePubKey()) +
|
||||
"\n\nmsgEncryptionPubKey=\n" + Util.pubKeyToString(getEncryptionPubKey()) +
|
||||
'}';
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.common.crypto;
|
||||
|
||||
import io.bitsquare.app.Version;
|
||||
|
||||
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;
|
||||
|
||||
public final byte[] encryptedSecretKey;
|
||||
public final byte[] encryptedPayloadWithHmac;
|
||||
public final byte[] signature;
|
||||
public final PublicKey sigPublicKey;
|
||||
|
||||
public SealedAndSigned(byte[] encryptedSecretKey, byte[] encryptedPayloadWithHmac, byte[] signature, PublicKey sigPublicKey) {
|
||||
this.encryptedSecretKey = encryptedSecretKey;
|
||||
this.encryptedPayloadWithHmac = encryptedPayloadWithHmac;
|
||||
this.signature = signature;
|
||||
this.sigPublicKey = sigPublicKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof SealedAndSigned)) return false;
|
||||
|
||||
SealedAndSigned that = (SealedAndSigned) o;
|
||||
|
||||
if (!Arrays.equals(encryptedSecretKey, that.encryptedSecretKey)) return false;
|
||||
if (!Arrays.equals(encryptedPayloadWithHmac, that.encryptedPayloadWithHmac)) return false;
|
||||
if (!Arrays.equals(signature, that.signature)) return false;
|
||||
return !(sigPublicKey != null ? !sigPublicKey.equals(that.sigPublicKey) : that.sigPublicKey != null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = encryptedSecretKey != null ? Arrays.hashCode(encryptedSecretKey) : 0;
|
||||
result = 31 * result + (encryptedPayloadWithHmac != null ? Arrays.hashCode(encryptedPayloadWithHmac) : 0);
|
||||
result = 31 * result + (signature != null ? Arrays.hashCode(signature) : 0);
|
||||
result = 31 * result + (sigPublicKey != null ? sigPublicKey.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
121
common/src/main/java/io/bitsquare/common/crypto/Sig.java
Normal file
121
common/src/main/java/io/bitsquare/common/crypto/Sig.java
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.common.crypto;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import org.bouncycastle.util.encoders.Base64;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.*;
|
||||
|
||||
/**
|
||||
* StorageSignatureKeyPair/STORAGE_SIGN_KEY_ALGO: That is used for signing the data to be stored to the P2P network (by flooding).
|
||||
* The algo is selected because it originated from the TomP2P version which used DSA.
|
||||
* Changing to EC keys might be considered.
|
||||
* <p>
|
||||
* MsgSignatureKeyPair/MSG_SIGN_KEY_ALGO/MSG_SIGN_ALGO: That is used when sending a message to a peer which is encrypted and signed.
|
||||
* Changing to EC keys might be considered.
|
||||
*/
|
||||
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
|
||||
|
||||
|
||||
/**
|
||||
* @return keyPair
|
||||
*/
|
||||
public static KeyPair generateKeyPair() {
|
||||
long ts = System.currentTimeMillis();
|
||||
try {
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGO);
|
||||
keyPairGenerator.initialize(1024);
|
||||
KeyPair keyPair = keyPairGenerator.genKeyPair();
|
||||
log.trace("Generate msgSignatureKeyPair needed {} ms", System.currentTimeMillis() - ts);
|
||||
return keyPair;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Could not create key.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param privateKey
|
||||
* @param data
|
||||
* @return
|
||||
* @throws SignatureException
|
||||
* @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();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param privateKey
|
||||
* @param message UTF-8 encoded message to sign
|
||||
* @return Base64 encoded signature
|
||||
* @throws SignatureException
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws InvalidKeyException
|
||||
*/
|
||||
public static String sign(PrivateKey privateKey, String message)
|
||||
throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
|
||||
byte[] sigAsBytes = sign(privateKey, message.getBytes(Charsets.UTF_8));
|
||||
return Base64.toBase64String(sigAsBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param publicKey
|
||||
* @param data
|
||||
* @param signature
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param publicKey
|
||||
* @param message UTF-8 encoded message
|
||||
* @param signature Base64 encoded signature
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws InvalidKeyException
|
||||
* @throws SignatureException
|
||||
*/
|
||||
public static boolean verify(PublicKey publicKey, String message, String signature)
|
||||
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
|
||||
return verify(publicKey, message.getBytes(Charsets.UTF_8), Base64.decode(signature));
|
||||
}
|
||||
}
|
||||
|
34
common/src/main/java/io/bitsquare/common/crypto/Util.java
Normal file
34
common/src/main/java/io/bitsquare/common/crypto/Util.java
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.common.crypto;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,9 @@
|
|||
|
||||
package io.bitsquare.common.util;
|
||||
|
||||
public class Tuple2<A, B> {
|
||||
import java.io.Serializable;
|
||||
|
||||
public class Tuple2<A, B> implements Serializable {
|
||||
final public A first;
|
||||
final public B second;
|
||||
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
|
||||
package io.bitsquare.common.util;
|
||||
|
||||
public class Tuple3<A, B, C> {
|
||||
import java.io.Serializable;
|
||||
|
||||
public class Tuple3<A, B, C> implements Serializable {
|
||||
final public A first;
|
||||
final public B second;
|
||||
final public C third;
|
||||
|
|
|
@ -208,13 +208,15 @@ public class Utilities {
|
|||
return result;
|
||||
}*/
|
||||
|
||||
public static <T> T byteArrayToObject(byte[] data) {
|
||||
public static <T extends Serializable> T deserialize(byte[] data) {
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(data);
|
||||
ObjectInput in = null;
|
||||
Object result = null;
|
||||
try {
|
||||
in = new ObjectInputStream(bis);
|
||||
result = in.readObject();
|
||||
if (!(result instanceof Serializable))
|
||||
throw new RuntimeException("Object not of type Serializable");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
|
@ -234,14 +236,15 @@ public class Utilities {
|
|||
return (T) result;
|
||||
}
|
||||
|
||||
public static byte[] objectToByteArray(Object object) {
|
||||
public static byte[] serialize(Serializable object) {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ObjectOutput out = null;
|
||||
byte[] result = null;
|
||||
try {
|
||||
out = new ObjectOutputStream(bos);
|
||||
out.writeObject(object);
|
||||
result = bos.toByteArray();
|
||||
out.flush();
|
||||
result = bos.toByteArray().clone();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
|
@ -249,13 +252,11 @@ public class Utilities {
|
|||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
// ignore close exception
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
try {
|
||||
bos.close();
|
||||
} catch (IOException ex) {
|
||||
// ignore close exception
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue