I don't believe it - encryption works!!!
This commit is contained in:
parent
f23f432f07
commit
46bb00c0aa
@ -16,11 +16,15 @@
|
||||
|
||||
package ch.dissem.bitmessage.demo;
|
||||
|
||||
import ch.dissem.bitmessage.BitmessageContext;
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||
import ch.dissem.bitmessage.entity.payload.ObjectType;
|
||||
import ch.dissem.bitmessage.entity.payload.Pubkey;
|
||||
import ch.dissem.bitmessage.entity.payload.*;
|
||||
import ch.dissem.bitmessage.inventory.JdbcAddressRepository;
|
||||
import ch.dissem.bitmessage.inventory.JdbcInventory;
|
||||
import ch.dissem.bitmessage.inventory.JdbcNodeRegistry;
|
||||
import ch.dissem.bitmessage.networking.NetworkNode;
|
||||
import ch.dissem.bitmessage.ports.NetworkHandler;
|
||||
import ch.dissem.bitmessage.utils.Base58;
|
||||
import ch.dissem.bitmessage.utils.Encode;
|
||||
import ch.dissem.bitmessage.utils.Security;
|
||||
@ -31,6 +35,7 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* Created by chris on 06.04.15.
|
||||
@ -39,43 +44,45 @@ public class Main {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(Main.class);
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
final BitmessageAddress address = new BitmessageAddress("BM-2D9Vc5rFxxR5vTi53T9gkLfemViHRMVLQZ");
|
||||
final BitmessageAddress address = new BitmessageAddress("BM-87hJ99tPAXxtetvnje7Z491YSvbEtBJVc5e");
|
||||
|
||||
// BitmessageContext ctx = new BitmessageContext.Builder()
|
||||
// .addressRepo(new JdbcAddressRepository())
|
||||
// .inventory(new JdbcInventory())
|
||||
// .nodeRegistry(new JdbcNodeRegistry())
|
||||
// .networkHandler(new NetworkNode())
|
||||
// .port(48444)
|
||||
// .streams(1)
|
||||
// .build();
|
||||
//
|
||||
// ctx.getNetworkHandler().start(new NetworkHandler.MessageListener() {
|
||||
// @Override
|
||||
// public void receive(ObjectPayload payload) {
|
||||
//// LOG.info("message received: " + payload);
|
||||
//// System.out.print('.');
|
||||
// if (payload instanceof V3Pubkey) {
|
||||
// V3Pubkey pubkey = (V3Pubkey) payload;
|
||||
// try {
|
||||
// address.setPubkey(pubkey);
|
||||
// System.out.println(address);
|
||||
// } catch (Exception ignore) {
|
||||
// System.err.println("Received pubkey we didn't request.");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// Scanner scanner = new Scanner(System.in);
|
||||
BitmessageContext ctx = new BitmessageContext.Builder()
|
||||
.addressRepo(new JdbcAddressRepository())
|
||||
.inventory(new JdbcInventory())
|
||||
.nodeRegistry(new JdbcNodeRegistry())
|
||||
.networkHandler(new NetworkNode())
|
||||
.port(48444)
|
||||
.streams(1)
|
||||
.build();
|
||||
|
||||
ctx.getNetworkHandler().start(new NetworkHandler.MessageListener() {
|
||||
@Override
|
||||
public void receive(ObjectPayload payload) {
|
||||
// LOG.info("message received: " + payload);
|
||||
// System.out.print('.');
|
||||
if (payload instanceof V4Pubkey) {
|
||||
V4Pubkey pubkey = (V4Pubkey) payload;
|
||||
if (Arrays.equals(address.getTag(), pubkey.getTag())) {
|
||||
System.out.println("Pubkey found!");
|
||||
try {
|
||||
address.setPubkey(pubkey);
|
||||
} catch (Exception ignore) {
|
||||
System.err.println("Received pubkey we didn't request.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
// System.out.println("Press Enter to request pubkey for address " + address);
|
||||
// scanner.nextLine();
|
||||
// ctx.send(1, address.getVersion(), new GetPubkey(address), 3000, 1000, 1000);
|
||||
//
|
||||
// System.out.println("Press Enter to exit");
|
||||
// scanner.nextLine();
|
||||
// LOG.info("Shutting down client");
|
||||
// ctx.getNetworkHandler().stop();
|
||||
|
||||
System.out.println("Press Enter to exit");
|
||||
scanner.nextLine();
|
||||
LOG.info("Shutting down client");
|
||||
ctx.getNetworkHandler().stop();
|
||||
|
||||
|
||||
List<ObjectMessage> objects = new JdbcInventory().getObjects(address.getStream(), address.getVersion(), ObjectType.PUBKEY);
|
||||
@ -84,12 +91,12 @@ public class Main {
|
||||
for (ObjectMessage o : objects) {
|
||||
// if (!o.isSignatureValid()) System.out.println("Invalid signature.");
|
||||
// System.out.println(o.getPayload().getSignature().length);
|
||||
Pubkey pubkey = (Pubkey) o.getPayload();
|
||||
if (Arrays.equals(address.getRipe(), pubkey.getRipe()))
|
||||
V4Pubkey pubkey = (V4Pubkey) o.getPayload();
|
||||
if (Arrays.equals(address.getTag(), pubkey.getTag())) {
|
||||
System.out.println("Pubkey found!");
|
||||
try {
|
||||
System.out.println("IV: "+o.getInventoryVector());
|
||||
address.setPubkey(pubkey);
|
||||
System.out.println(address);
|
||||
} catch (Exception ignore) {
|
||||
System.out.println("But setPubkey failed? " + address.getRipe().length + "/" + pubkey.getRipe().length);
|
||||
System.out.println("Failed address: " + generateAddress(address.getStream(), address.getVersion(), pubkey.getRipe()));
|
||||
@ -99,6 +106,7 @@ public class Main {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String generateAddress(long stream, long version, byte[] ripe) {
|
||||
try {
|
||||
|
@ -64,7 +64,8 @@ public class BitmessageAddress {
|
||||
this.tag = Arrays.copyOfRange(checksum, 32, 64);
|
||||
this.privateEncryptionKey = Arrays.copyOfRange(checksum, 0, 32);
|
||||
// but for the address and its checksum they need to be stripped
|
||||
os.write(Bytes.stripLeadingZeros(ripe));
|
||||
int offset = Bytes.numberOfLeadingZeros(ripe);
|
||||
os.write(ripe, offset, ripe.length - offset);
|
||||
checksum = Security.doubleSha512(os.toByteArray(), ripe);
|
||||
os.write(checksum, 0, 4);
|
||||
this.address = "BM-" + Base58.encode(os.toByteArray());
|
||||
@ -73,10 +74,14 @@ public class BitmessageAddress {
|
||||
}
|
||||
}
|
||||
|
||||
private BitmessageAddress(Pubkey publicKey) {
|
||||
this(publicKey.getVersion(), publicKey.getStream(), publicKey.getRipe());
|
||||
this.pubkey = publicKey;
|
||||
}
|
||||
|
||||
public BitmessageAddress(PrivateKey privateKey) {
|
||||
this(privateKey.getPubkey().getVersion(), privateKey.getPubkey().getStream(), privateKey.getPubkey().getRipe());
|
||||
this(privateKey.getPubkey());
|
||||
this.privateKey = privateKey;
|
||||
this.pubkey = privateKey.getPubkey();
|
||||
}
|
||||
|
||||
public BitmessageAddress(String address) {
|
||||
@ -104,6 +109,18 @@ public class BitmessageAddress {
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] calculateTag(long version, long stream, byte[] ripe) {
|
||||
try {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Encode.varInt(version, out);
|
||||
Encode.varInt(stream, out);
|
||||
out.write(ripe);
|
||||
return Arrays.copyOfRange(Security.doubleSha512(out.toByteArray()), 32, 64);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public long getStream() {
|
||||
return stream;
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ import org.bouncycastle.crypto.paddings.PKCS7Padding;
|
||||
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
|
||||
import org.bouncycastle.crypto.params.KeyParameter;
|
||||
import org.bouncycastle.crypto.params.ParametersWithIV;
|
||||
import org.bouncycastle.jce.interfaces.ECPublicKey;
|
||||
import org.bouncycastle.math.ec.ECPoint;
|
||||
|
||||
import java.io.*;
|
||||
@ -41,36 +40,37 @@ import java.util.Arrays;
|
||||
public class CryptoBox implements Streamable {
|
||||
private final byte[] initializationVector;
|
||||
private final int curveType;
|
||||
private final byte[] xComponent;
|
||||
private final byte[] yComponent;
|
||||
private final ECPoint R;
|
||||
private final byte[] mac;
|
||||
private byte[] encrypted;
|
||||
|
||||
public CryptoBox(Streamable data, byte[] encryptionKey) {
|
||||
this(data, Security.keyToPoint(encryptionKey));
|
||||
}
|
||||
|
||||
public CryptoBox(Streamable data, ECPoint K) {
|
||||
curveType = 0x02CA;
|
||||
|
||||
// 1. The destination public key is called K.
|
||||
ECPublicKey K = Security.getPublicKey(encryptionKey);
|
||||
// 2. Generate 16 random bytes using a secure random number generator. Call them IV.
|
||||
initializationVector = Security.randomBytes(16);
|
||||
|
||||
// 3. Generate a new random EC key pair with private key called r and public key called R.
|
||||
// TODO
|
||||
BigInteger r = null;
|
||||
byte[] r = Security.randomBytes(64);
|
||||
R = Security.createPublicKey(r);
|
||||
// 4. Do an EC point multiply with public key K and private key r. This gives you public key P.
|
||||
ECPoint P = K.getQ().multiply(r).normalize();
|
||||
xComponent = Bytes.stripLeadingZeros(P.getXCoord().getEncoded());
|
||||
yComponent = Bytes.stripLeadingZeros(P.getYCoord().getEncoded());
|
||||
ECPoint P = K.multiply(Security.keyToBigInt(r)).normalize();
|
||||
byte[] X = P.getXCoord().getEncoded();
|
||||
// 5. Use the X component of public key P and calculate the SHA512 hash H.
|
||||
byte[] H = Security.sha512(xComponent);
|
||||
byte[] H = Security.sha512(X);
|
||||
// 6. The first 32 bytes of H are called key_e and the last 32 bytes are called key_m.
|
||||
byte[] key_e = Arrays.copyOfRange(H, 0, 32);
|
||||
byte[] key_m = Arrays.copyOfRange(H, H.length - 32, 32);
|
||||
byte[] key_m = Arrays.copyOfRange(H, 32, 64);
|
||||
// 7. Pad the input text to a multiple of 16 bytes, in accordance to PKCS7.
|
||||
// 8. Encrypt the data with AES-256-CBC, using IV as initialization vector, key_e as encryption key and the padded input text as payload. Call the output cipher text.
|
||||
encrypted = null; // TODO
|
||||
encrypted = crypt(true, Bytes.from(data), key_e);
|
||||
// 9. Calculate a 32 byte MAC with HMACSHA256, using key_m as salt and IV + R + cipher text as data. Call the output MAC.
|
||||
mac = null; // TODO
|
||||
mac = calculateMac(key_m);
|
||||
|
||||
// The resulting data is: IV + R + cipher text + MAC
|
||||
}
|
||||
@ -78,8 +78,7 @@ public class CryptoBox implements Streamable {
|
||||
private CryptoBox(Builder builder) {
|
||||
initializationVector = builder.initializationVector;
|
||||
curveType = builder.curveType;
|
||||
xComponent = builder.xComponent;
|
||||
yComponent = builder.yComponent;
|
||||
R = Security.createPoint(builder.xComponent, builder.yComponent);
|
||||
encrypted = builder.encrypted;
|
||||
mac = builder.mac;
|
||||
}
|
||||
@ -101,53 +100,65 @@ public class CryptoBox implements Streamable {
|
||||
*/
|
||||
public InputStream decrypt(byte[] privateKey) {
|
||||
// 1. The private key used to decrypt is called k.
|
||||
BigInteger K = Security.keyToBigInt(privateKey);
|
||||
BigInteger k = Security.keyToBigInt(privateKey);
|
||||
// 2. Do an EC point multiply with private key k and public key R. This gives you public key P.
|
||||
ECPublicKey R = Security.getPublicKey(xComponent, yComponent);
|
||||
ECPoint P = R.getQ().multiply(K).normalize();
|
||||
ECPoint P = R.multiply(k).normalize();
|
||||
// 3. Use the X component of public key P and calculate the SHA512 hash H.
|
||||
byte[] sha512key = Security.sha512(Bytes.expand(P.getXCoord().toBigInteger().toByteArray(), 32));
|
||||
byte[] H = Security.sha512(P.getXCoord().getEncoded());
|
||||
// 4. The first 32 bytes of H are called key_e and the last 32 bytes are called key_m.
|
||||
byte[] key_e = Arrays.copyOfRange(sha512key, 0, 32);
|
||||
byte[] key_m = Arrays.copyOfRange(sha512key, 32, 64);
|
||||
byte[] key_e = Arrays.copyOfRange(H, 0, 32);
|
||||
byte[] key_m = Arrays.copyOfRange(H, 32, 64);
|
||||
|
||||
// 5. Calculate MAC' with HMACSHA256, using key_m as salt and IV + R + cipher text as data.
|
||||
ByteArrayOutputStream macData = new ByteArrayOutputStream();
|
||||
try {
|
||||
writeWithoutMAC(macData);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
// 6. Compare MAC with MAC'. If not equal, decryption will fail.
|
||||
if (!Arrays.equals(mac, Security.mac(key_m, macData.toByteArray()))) {
|
||||
if (!Arrays.equals(mac, calculateMac(key_m))) {
|
||||
throw new RuntimeException("Invalid MAC while decrypting");
|
||||
}
|
||||
|
||||
// 7. Decrypt the cipher text with AES-256-CBC, using IV as initialization vector, key_e as decryption key
|
||||
// and the cipher text as payload. The output is the padded input text.
|
||||
return new ByteArrayInputStream(crypt(false, encrypted, key_e));
|
||||
}
|
||||
|
||||
private byte[] calculateMac(byte[] key_m) {
|
||||
try {
|
||||
ByteArrayOutputStream macData = new ByteArrayOutputStream();
|
||||
writeWithoutMAC(macData);
|
||||
return Security.mac(key_m, macData.toByteArray());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] crypt(boolean encrypt, byte[] data, byte[] key_e) {
|
||||
BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()), new PKCS7Padding());
|
||||
|
||||
CipherParameters params = new ParametersWithIV(new KeyParameter(key_e), initializationVector);
|
||||
|
||||
cipher.init(false, params);
|
||||
cipher.init(encrypt, params);
|
||||
|
||||
byte[] buffer = new byte[cipher.getOutputSize(encrypted.length)];
|
||||
int length = cipher.processBytes(encrypted, 0, encrypted.length, buffer, 0);
|
||||
byte[] buffer = new byte[cipher.getOutputSize(data.length)];
|
||||
int length = cipher.processBytes(data, 0, data.length, buffer, 0);
|
||||
try {
|
||||
length += cipher.doFinal(buffer, length);
|
||||
} catch (InvalidCipherTextException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
return new ByteArrayInputStream(buffer, 0, length);
|
||||
if (length < buffer.length) {
|
||||
return Arrays.copyOfRange(buffer, 0, length);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private void writeWithoutMAC(OutputStream stream) throws IOException {
|
||||
stream.write(initializationVector);
|
||||
Encode.int16(curveType, stream);
|
||||
Encode.int16(xComponent.length, stream);
|
||||
stream.write(xComponent);
|
||||
Encode.int16(yComponent.length, stream);
|
||||
stream.write(yComponent);
|
||||
byte[] x = R.getXCoord().getEncoded();
|
||||
byte[] y = R.getYCoord().getEncoded();
|
||||
Encode.int16(x.length, stream);
|
||||
stream.write(x);
|
||||
Encode.int16(y.length, stream);
|
||||
stream.write(y);
|
||||
stream.write(encrypted);
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ import ch.dissem.bitmessage.utils.Decode;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* In cases we don't know what to do with an object, we just store its bytes and send it again - we don't really
|
||||
@ -53,4 +54,22 @@ public class GenericPayload extends ObjectPayload {
|
||||
public void write(OutputStream stream) throws IOException {
|
||||
stream.write(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
GenericPayload that = (GenericPayload) o;
|
||||
|
||||
if (stream != that.stream) return false;
|
||||
return Arrays.equals(data, that.data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = (int) (stream ^ (stream >>> 32));
|
||||
result = 31 * result + Arrays.hashCode(data);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ public abstract class ObjectPayload implements Streamable {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void writeBytesToSign(OutputStream out) throws IOException{
|
||||
public void writeBytesToSign(OutputStream out) throws IOException {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,9 @@
|
||||
|
||||
package ch.dissem.bitmessage.entity.payload;
|
||||
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||
import ch.dissem.bitmessage.utils.Decode;
|
||||
import ch.dissem.bitmessage.utils.Security;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -40,11 +42,10 @@ public class V4Pubkey extends Pubkey {
|
||||
this.encrypted = encrypted;
|
||||
}
|
||||
|
||||
public V4Pubkey(byte[] tag, V3Pubkey decrypted) {
|
||||
public V4Pubkey(V3Pubkey decrypted) {
|
||||
this.stream = decrypted.stream;
|
||||
this.tag = tag;
|
||||
this.tag = BitmessageAddress.calculateTag(4, decrypted.getStream(), decrypted.getRipe());
|
||||
this.decrypted = decrypted;
|
||||
this.encrypted = new CryptoBox(decrypted, null);
|
||||
}
|
||||
|
||||
public static V4Pubkey read(InputStream in, long stream, int length) throws IOException {
|
||||
@ -53,6 +54,11 @@ public class V4Pubkey extends Pubkey {
|
||||
CryptoBox.read(in, length - 32));
|
||||
}
|
||||
|
||||
public void encrypt(byte[] privateKey) throws IOException {
|
||||
if (getSignature() == null) throw new IllegalStateException("Pubkey must be signed before encryption.");
|
||||
this.encrypted = new CryptoBox(decrypted, Security.createPublicKey(privateKey));
|
||||
}
|
||||
|
||||
public void decrypt(byte[] privateKey) throws IOException {
|
||||
decrypted = V3Pubkey.read(encrypted.decrypt(privateKey), stream);
|
||||
}
|
||||
|
@ -60,6 +60,14 @@ public class PrivateKey implements Streamable {
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getPrivateSigningKey() {
|
||||
return privateSigningKey;
|
||||
}
|
||||
|
||||
public byte[] getPrivateEncryptionKey() {
|
||||
return privateEncryptionKey;
|
||||
}
|
||||
|
||||
public static PrivateKey read(InputStream is) throws IOException {
|
||||
int version = (int) Decode.varInt(is);
|
||||
long stream = Decode.varInt(is);
|
||||
|
@ -82,7 +82,6 @@ public class Factory {
|
||||
.build();
|
||||
case 4:
|
||||
return new V4Pubkey(
|
||||
null, // FIXME: calculate tag
|
||||
new V3Pubkey.Builder()
|
||||
.stream(stream)
|
||||
.publicSigningKey(publicSigningKey)
|
||||
|
@ -16,6 +16,12 @@
|
||||
|
||||
package ch.dissem.bitmessage.utils;
|
||||
|
||||
import ch.dissem.bitmessage.entity.Streamable;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* A helper class for working with byte arrays interpreted as unsigned big endian integers.
|
||||
*/
|
||||
@ -41,6 +47,9 @@ public class Bytes {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a < b.
|
||||
*/
|
||||
public static boolean lt(byte[] a, byte[] b) {
|
||||
byte[] max = (a.length > b.length ? a : b);
|
||||
byte[] min = (max == a ? b : a);
|
||||
@ -57,6 +66,9 @@ public class Bytes {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a < b, where the first [size] bytes are checked.
|
||||
*/
|
||||
public static boolean lt(byte[] a, byte[] b, int size) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (a[i] != b[i]) {
|
||||
@ -123,14 +135,25 @@ public class Bytes {
|
||||
throw new IllegalArgumentException("'" + c + "' is not a valid hex value");
|
||||
}
|
||||
|
||||
public static int numberOfLeadingZeros(byte[] bytes) {
|
||||
int i;
|
||||
for (i = 0; i < bytes.length; i++) {
|
||||
if (bytes[i] != 0) return i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
public static byte[] stripLeadingZeros(byte[] bytes) {
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
if (bytes[i] != 0) {
|
||||
byte[] result = new byte[bytes.length - i];
|
||||
System.arraycopy(bytes, i, result, 0, bytes.length - i);
|
||||
return result;
|
||||
return Arrays.copyOfRange(bytes, numberOfLeadingZeros(bytes), bytes.length);
|
||||
}
|
||||
|
||||
public static byte[] from(Streamable data) {
|
||||
try {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
data.write(out);
|
||||
return out.toByteArray();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
|
@ -23,10 +23,9 @@ import ch.dissem.bitmessage.ports.ProofOfWorkEngine;
|
||||
import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||
import org.bouncycastle.crypto.ec.CustomNamedCurves;
|
||||
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
|
||||
import org.bouncycastle.jce.ECNamedCurveTable;
|
||||
import org.bouncycastle.jce.interfaces.ECPublicKey;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.math.ec.ECPoint;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -35,7 +34,6 @@ import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.*;
|
||||
import java.security.spec.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
@ -162,62 +160,46 @@ public class Security {
|
||||
public static Pubkey createPubkey(long version, long stream, byte[] privateSigningKey, byte[] privateEncryptionKey,
|
||||
long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) {
|
||||
return Factory.createPubkey(version, stream,
|
||||
createPublicKey(privateSigningKey),
|
||||
createPublicKey(privateEncryptionKey),
|
||||
createPublicKey(privateSigningKey).getEncoded(false),
|
||||
createPublicKey(privateEncryptionKey).getEncoded(false),
|
||||
nonceTrialsPerByte, extraBytes, features);
|
||||
}
|
||||
|
||||
private static byte[] createPublicKey(byte[] privateKey) {
|
||||
return EC_DOMAIN_PARAMETERS.getG().multiply(keyToBigInt(privateKey)).getEncoded(false);
|
||||
public static ECPoint createPublicKey(byte[] privateKey) {
|
||||
return EC_DOMAIN_PARAMETERS.getG().multiply(keyToBigInt(privateKey)).normalize();
|
||||
}
|
||||
|
||||
public static BigInteger keyToBigInt(byte[] privateKey) {
|
||||
return new BigInteger(1, privateKey);
|
||||
}
|
||||
|
||||
private static ECPoint keyToPoint(byte[] publicKey) {
|
||||
BigInteger x = new BigInteger(Arrays.copyOfRange(publicKey, 1, 33));
|
||||
BigInteger y = new BigInteger(Arrays.copyOfRange(publicKey, 33, 65));
|
||||
return new ECPoint(x, y);
|
||||
public static ECPoint keyToPoint(byte[] publicKey) {
|
||||
BigInteger x = new BigInteger(1, Arrays.copyOfRange(publicKey, 1, 33));
|
||||
BigInteger y = new BigInteger(1, Arrays.copyOfRange(publicKey, 33, 65));
|
||||
return EC_DOMAIN_PARAMETERS.getCurve().createPoint(x, y);
|
||||
}
|
||||
|
||||
public static ECPoint createPoint(byte[] x, byte[] y) {
|
||||
return EC_DOMAIN_PARAMETERS.getCurve().createPoint(
|
||||
new BigInteger(1, x),
|
||||
new BigInteger(1, y)
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean isSignatureValid(byte[] bytesToSign, byte[] signature, Pubkey pubkey) {
|
||||
ECPoint W = keyToPoint(pubkey.getSigningKey());
|
||||
try {
|
||||
ECParameterSpec param = null;
|
||||
KeySpec keySpec = new ECPublicKeySpec(W, param);
|
||||
PublicKey publicKey = KeyFactory.getInstance("ECDSA", "BC").generatePublic(keySpec);
|
||||
|
||||
Signature sig = Signature.getInstance("ECDSA", "BC");
|
||||
sig.initVerify(publicKey);
|
||||
sig.update(bytesToSign);
|
||||
return sig.verify(signature);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static ECPublicKey getPublicKey(byte[] publicKey) {
|
||||
if (publicKey[0] != 0x04) throw new IllegalArgumentException("Public key starting with 0x04 expected");
|
||||
return getPublicKey(
|
||||
Arrays.copyOfRange(publicKey, 1, 33),
|
||||
Arrays.copyOfRange(publicKey, 33, 65)
|
||||
);
|
||||
}
|
||||
|
||||
public static ECPublicKey getPublicKey(byte[] X, byte[] Y) {
|
||||
try {
|
||||
ECPoint w = new ECPoint(keyToBigInt(X), keyToBigInt(Y));
|
||||
EllipticCurve curve = EC5Util.convertCurve(EC_DOMAIN_PARAMETERS.getCurve(), EC_CURVE_PARAMETERS.getSeed());
|
||||
ECParameterSpec params = EC5Util.convertSpec(curve, ECNamedCurveTable.getParameterSpec(EC_CURVE_NAME));
|
||||
ECPublicKeySpec keySpec = new ECPublicKeySpec(w, params);
|
||||
return (ECPublicKey) getKeyFactory().generatePublic(keySpec);
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static KeyFactory getKeyFactory() throws NoSuchProviderException, NoSuchAlgorithmException {
|
||||
return KeyFactory.getInstance("EC", "BC");
|
||||
// try {
|
||||
// ECParameterSpec param = null;
|
||||
// KeySpec keySpec = new ECPublicKeySpec(W, param);
|
||||
// PublicKey publicKey = KeyFactory.getInstance("ECDSA", "BC").generatePublic(keySpec);
|
||||
//
|
||||
// Signature sig = Signature.getInstance("ECDSA", "BC");
|
||||
// sig.initVerify(publicKey);
|
||||
// sig.update(bytesToSign);
|
||||
// return sig.verify(signature);
|
||||
// } catch (Exception e) {
|
||||
// throw new RuntimeException(e);
|
||||
// }
|
||||
return false; // TODO
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static ch.dissem.bitmessage.entity.payload.Pubkey.Feature.DOES_ACK;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class BitmessageAddressTest {
|
||||
@ -53,7 +54,7 @@ public class BitmessageAddressTest {
|
||||
|
||||
@Test
|
||||
public void testCreateAddress() {
|
||||
BitmessageAddress address = new BitmessageAddress(new PrivateKey(0, 0, 0));
|
||||
BitmessageAddress address = new BitmessageAddress(new PrivateKey(1, 1000, 1000, DOES_ACK));
|
||||
assertNotNull(address.getPubkey());
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.utils;
|
||||
|
||||
import ch.dissem.bitmessage.entity.payload.CryptoBox;
|
||||
import ch.dissem.bitmessage.entity.payload.GenericPayload;
|
||||
import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyPair;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Created by chris on 10.05.15.
|
||||
*/
|
||||
public class EncryptionTest {
|
||||
@Test
|
||||
public void ensureDecryptedDataIsSameAsBeforeEncryption() throws IOException {
|
||||
GenericPayload before = new GenericPayload(1, Security.randomBytes(100));
|
||||
|
||||
PrivateKey privateKey = new PrivateKey(1, 1000, 1000);
|
||||
CryptoBox cryptoBox = new CryptoBox(before, privateKey.getPubkey().getEncryptionKey());
|
||||
|
||||
GenericPayload after = GenericPayload.read(cryptoBox.decrypt(privateKey.getPrivateEncryptionKey()), 1, 100);
|
||||
|
||||
assertEquals(before, after);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user