Merged encryption and signature work from crypto-primitives
commit
ad121a7bfd
|
@ -103,7 +103,6 @@ idea {
|
|||
|
||||
def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java"
|
||||
|
||||
println "Adding $srcDir"
|
||||
// add protobuf generated sources to generated source dir.
|
||||
if ("test".equals(sourceSet.name)) {
|
||||
testSourceDirs += file(srcDir)
|
||||
|
|
|
@ -17,7 +17,6 @@ import com.google.protobuf.ByteString;
|
|||
|
||||
import meerkat.comm.CommunicationException;
|
||||
import meerkat.crypto.concrete.ECDSASignature;
|
||||
import meerkat.crypto.concrete.TestECDSASignature;
|
||||
import meerkat.protobuf.BulletinBoardAPI.BulletinBoardMessage;
|
||||
import meerkat.protobuf.BulletinBoardAPI.FilterType;
|
||||
import meerkat.protobuf.BulletinBoardAPI.MessageFilter;
|
||||
|
@ -48,13 +47,13 @@ public class GenericBulletinBoardServerTest {
|
|||
signers[0] = new ECDSASignature();
|
||||
signers[1] = new ECDSASignature();
|
||||
|
||||
InputStream keyStream = TestECDSASignature.class.getResourceAsStream(KEYFILE_EXAMPLE);
|
||||
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
||||
char[] password = KEYFILE_PASSWORD.toCharArray();
|
||||
|
||||
KeyStore.Builder keyStore = signers[0].getPKCS12KeyStoreBuilder(keyStream, password);
|
||||
signers[0].loadSigningCertificate(keyStore);
|
||||
|
||||
signers[0].loadVerificationCertificates(TestECDSASignature.class.getResourceAsStream(CERT1_PEM_EXAMPLE));
|
||||
signers[0].loadVerificationCertificates(getClass().getResourceAsStream(CERT1_PEM_EXAMPLE));
|
||||
|
||||
random = new SecureRandom();
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ dependencies {
|
|||
compile 'com.google.protobuf:protobuf-java:3.+'
|
||||
|
||||
// Crypto
|
||||
compile 'org.factcenter.qilin:qilin:1.1+'
|
||||
compile 'org.bouncycastle:bcprov-jdk15on:1.53'
|
||||
compile 'org.bouncycastle:bcpkix-jdk15on:1.53'
|
||||
|
||||
|
@ -98,7 +99,6 @@ idea {
|
|||
|
||||
def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java"
|
||||
|
||||
println "Adding $srcDir"
|
||||
// add protobuf generated sources to generated source dir.
|
||||
if ("test".equals(sourceSet.name)) {
|
||||
testSourceDirs += file(srcDir)
|
||||
|
@ -152,6 +152,8 @@ task fatCapsule(type: FatCapsule){
|
|||
*===================================*/
|
||||
|
||||
repositories {
|
||||
|
||||
mavenLocal();
|
||||
|
||||
// Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral)
|
||||
maven {
|
||||
|
|
|
@ -51,7 +51,7 @@ public interface DigitalSignature {
|
|||
|
||||
|
||||
/**
|
||||
* Sign the content that was added.
|
||||
* Sign the content that was added using {@link #updateContent(Message)}.
|
||||
* Reset the DigitalSignature and make it available to sign a new message using the same key.
|
||||
* @return
|
||||
* @throws SignatureException
|
||||
|
@ -76,7 +76,7 @@ public interface DigitalSignature {
|
|||
/**
|
||||
* Loads a private signing key. The keystore must include both the public and private
|
||||
* key parts.
|
||||
* This method must be called before calling {@link #sign(List)}
|
||||
* This method must be called before calling {@link #sign()} or {@link #updateContent(Message)}
|
||||
* Calling this method again will replace the key.
|
||||
*
|
||||
* @param keyStoreBuilder A keystore builder that can be used to load a keystore.
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
package meerkat.crypto;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.google.protobuf.Message;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Random;
|
||||
|
||||
import static meerkat.protobuf.Crypto.*;
|
||||
|
||||
/**
|
||||
|
@ -13,8 +18,23 @@ public interface Encryption {
|
|||
* @param rnd
|
||||
* @return
|
||||
*/
|
||||
RerandomizableEncryptedMessage encrypt(Message plaintext, EncryptionRandomness rnd); // TODO: type of exception; throws
|
||||
RerandomizableEncryptedMessage encrypt(Message plaintext, EncryptionRandomness rnd) throws IOException; // TODO: type of exception; throws
|
||||
|
||||
RerandomizableEncryptedMessage rerandomize(RerandomizableEncryptedMessage msg, EncryptionRandomness rnd);
|
||||
|
||||
/**
|
||||
* Rerandomize a ciphertext using the supplied randomness.
|
||||
* @param msg
|
||||
* @param rnd
|
||||
* @return
|
||||
* @throws InvalidProtocolBufferException
|
||||
*/
|
||||
RerandomizableEncryptedMessage rerandomize(RerandomizableEncryptedMessage msg, EncryptionRandomness rnd) throws InvalidProtocolBufferException;
|
||||
|
||||
/**
|
||||
* Generate randomness compatible with {@link #encrypt(Message, EncryptionRandomness) and
|
||||
* {@link #rerandomize(RerandomizableEncryptedMessage, EncryptionRandomness)}}.
|
||||
* @param rand
|
||||
* @return
|
||||
*/
|
||||
EncryptionRandomness generateRandomness(Random rand);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
package meerkat.crypto.concrete;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.Message;
|
||||
import meerkat.crypto.DigitalSignature;
|
||||
import meerkat.protobuf.Crypto;
|
||||
import meerkat.protobuf.Crypto.Signature;
|
||||
import meerkat.util.Hex;
|
||||
import org.bouncycastle.asn1.ASN1Integer;
|
||||
import org.bouncycastle.asn1.DERSequence;
|
||||
import org.bouncycastle.crypto.Digest;
|
||||
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
|
||||
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.bouncycastle.crypto.signers.DSAKCalculator;
|
||||
import org.bouncycastle.crypto.signers.ECDSASigner;
|
||||
import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.util.DSAEncoder;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
|
||||
import org.bouncycastle.jce.ECKeyUtil;
|
||||
import org.bouncycastle.jce.ECPointUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.callback.PasswordCallback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.*;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.*;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Sign and verify digital signatures.
|
||||
*
|
||||
* Uses deterministic ECDSA signatures as per RFC 6979
|
||||
*
|
||||
* This class uses BouncyCastle directly, so will not work with arbitrary PKCS11 providers.
|
||||
*
|
||||
* This class is not thread-safe (each thread should have its own instance).
|
||||
*/
|
||||
public class ECDSADeterministicSignature extends ECDSASignature {
|
||||
final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
/**
|
||||
* Digest of message contents for deterministic signing.
|
||||
*/
|
||||
SHA256Digest msgDigest = new SHA256Digest();
|
||||
|
||||
/**
|
||||
* The actual signing implementation. (used only signing -- superclass is used for verification)
|
||||
*/
|
||||
ECDSASigner deterministicSigner;
|
||||
|
||||
/**
|
||||
* Output the DER encoding of the ASN.1 sequence r,s
|
||||
* @param r
|
||||
* @param s
|
||||
* @return
|
||||
*/
|
||||
public static byte[] derEncodeSignature(BigInteger r, BigInteger s) {
|
||||
ASN1Integer[] rs = {new ASN1Integer(r), new ASN1Integer(s)};
|
||||
DERSequence seq = new DERSequence(rs);
|
||||
|
||||
try {
|
||||
return seq.getEncoded();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Should never happen! DER Encoding exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
public ECDSADeterministicSignature() {
|
||||
DSAKCalculator kCalk = new HMacDSAKCalculator(new org.bouncycastle.crypto.digests.SHA256Digest());
|
||||
deterministicSigner = new ECDSASigner(kCalk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadSigningCertificate(KeyStore.Builder keyStoreBuilder)
|
||||
throws CertificateException, UnrecoverableKeyException, IOException {
|
||||
super.loadSigningCertificate(keyStoreBuilder);
|
||||
|
||||
if (!(loadedSigningKey instanceof ECPrivateKey)) {
|
||||
logger.error("Wrong private key type (expected ECPrivateKey, got {})", loadedSigningKey.getClass());
|
||||
throw new CertificateException("Wrong signing key type!");
|
||||
}
|
||||
ECPrivateKey key = (ECPrivateKey) loadedSigningKey;
|
||||
|
||||
AsymmetricKeyParameter baseParams;
|
||||
try {
|
||||
baseParams = ECUtil.generatePrivateKeyParameter(key);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new UnrecoverableKeyException("Couldn't convert private key");
|
||||
}
|
||||
|
||||
if (!(baseParams instanceof ECPrivateKeyParameters)) {
|
||||
logger.error("Error converting to bouncycastle type! (got {})", baseParams.getClass());
|
||||
throw new UnrecoverableKeyException("Wrong signing key type!");
|
||||
}
|
||||
|
||||
ECPrivateKeyParameters params = (ECPrivateKeyParameters) baseParams;
|
||||
|
||||
deterministicSigner.init(true, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the list of messages to the stream that is being verified/signed.
|
||||
* Messages are prepended with their length in 32-bit big-endian format.
|
||||
*
|
||||
* @param msg
|
||||
* @throws SignatureException
|
||||
*/
|
||||
@Override
|
||||
public void updateContent(Message msg) throws SignatureException {
|
||||
assert msg != null;
|
||||
|
||||
// We're doing twice the digest work so that we also support verification with the same update.
|
||||
// If this becomes a problem, we can decide which way to update based on which init was called.
|
||||
super.updateContent(msg);
|
||||
msgDigest.update(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateContent(InputStream in) throws IOException, SignatureException {
|
||||
ByteString inStr = ByteString.readFrom(in);
|
||||
signer.update(inStr.asReadOnlyByteBuffer());
|
||||
msgDigest.update(inStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Signature sign() throws SignatureException {
|
||||
Signature.Builder sig = Signature.newBuilder();
|
||||
sig.setType(Crypto.SignatureType.ECDSA);
|
||||
|
||||
BigInteger[] rawSig = deterministicSigner.generateSignature(msgDigest.digest());
|
||||
|
||||
sig.setData(ByteString.copyFrom(derEncodeSignature(rawSig[0], rawSig[1])));
|
||||
|
||||
sig.setSignerId(loadedSigningKeyId);
|
||||
return sig.build();
|
||||
}
|
||||
}
|
|
@ -36,7 +36,7 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatur
|
|||
final public static String KEYSTORE_TYPE = "PKCS12";
|
||||
final public static String DEFAULT_SIGNATURE_ALGORITHM = "SHA256withECDSA";
|
||||
|
||||
SHA256Digest digest = new SHA256Digest();
|
||||
SHA256Digest certDigest = new SHA256Digest();
|
||||
|
||||
/**
|
||||
* Buffer used to hold length in for hash update
|
||||
|
@ -58,6 +58,11 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatur
|
|||
*/
|
||||
java.security.Signature signer;
|
||||
|
||||
/**
|
||||
* The currently loaded signing key.
|
||||
*/
|
||||
PrivateKey loadedSigningKey;
|
||||
|
||||
|
||||
/**
|
||||
* Compute a fingerprint of a cert as a SHA256 hash.
|
||||
|
@ -67,14 +72,14 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatur
|
|||
*/
|
||||
public ByteString computeCertificateFingerprint(Certificate cert) {
|
||||
try {
|
||||
digest.reset();
|
||||
certDigest.reset();
|
||||
byte[] data = cert.getEncoded();
|
||||
digest.update(data);
|
||||
return ByteString.copyFrom(digest.digest());
|
||||
certDigest.update(data);
|
||||
return ByteString.copyFrom(certDigest.digest());
|
||||
} catch (CertificateEncodingException e) {
|
||||
// Shouldn't happen
|
||||
logger.error("Certificate encoding error", e);
|
||||
return ByteString.EMPTY;
|
||||
throw new RuntimeException("Certificate encoding error", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -133,7 +138,7 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatur
|
|||
signer.update(msg.toByteString().asReadOnlyByteBuffer());
|
||||
}
|
||||
|
||||
public void updateSigner(InputStream in) throws IOException, SignatureException {
|
||||
public void updateContent(InputStream in) throws IOException, SignatureException {
|
||||
ByteString inStr = ByteString.readFrom(in);
|
||||
signer.update(inStr.asReadOnlyByteBuffer());
|
||||
}
|
||||
|
@ -234,10 +239,13 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatur
|
|||
String alias = aliases.nextElement();
|
||||
logger.trace("Testing keystore entry {}", alias);
|
||||
|
||||
|
||||
try {
|
||||
Certificate cert = keyStore.getCertificate(alias);
|
||||
logger.trace("keystore entry {}, has cert type {}", alias, cert.getClass());
|
||||
|
||||
Key key;
|
||||
|
||||
try {
|
||||
key = keyStore.getKey(alias, null);
|
||||
} catch (UnrecoverableKeyException e) {
|
||||
|
@ -267,9 +275,11 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatur
|
|||
}
|
||||
logger.trace("keystore entry {}, has key type {}", alias, key.getClass());
|
||||
if (key instanceof PrivateKey) {
|
||||
loadedSigningKey = (PrivateKey) key;
|
||||
loadedSigningKeyId = computeCertificateFingerprint(cert);
|
||||
signer.initSign((PrivateKey) key);
|
||||
signer.initSign(loadedSigningKey);
|
||||
logger.debug("Loaded signing key with ID {}", Hex.encode(loadedSigningKeyId));
|
||||
|
||||
return;
|
||||
} else {
|
||||
logger.info("Certificate {} in keystore does not have a private key", cert.toString());
|
||||
|
@ -297,6 +307,8 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatur
|
|||
if (loadedSigningKeyId != null)
|
||||
signer.initSign(null);
|
||||
loadedSigningKeyId = null;
|
||||
loadedSigningKey = null;
|
||||
// Start garbage collection?
|
||||
} catch (InvalidKeyException e) {
|
||||
// Do nothing
|
||||
}
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
package meerkat.crypto.concrete;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.CodedOutputStream;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.google.protobuf.Message;
|
||||
import meerkat.crypto.Encryption;
|
||||
import meerkat.protobuf.ConcreteCrypto;
|
||||
import meerkat.protobuf.Crypto;
|
||||
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
|
||||
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||
import org.bouncycastle.crypto.params.ECKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.bouncycastle.crypto.util.PublicKeyFactory;
|
||||
import org.bouncycastle.jce.spec.ECParameterSpec;
|
||||
import org.bouncycastle.math.ec.ECCurve;
|
||||
import org.bouncycastle.math.ec.ECFieldElement;
|
||||
import org.bouncycastle.math.ec.ECPoint;
|
||||
import org.bouncycastle.util.BigIntegers;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import qilin.primitives.concrete.ECElGamal;
|
||||
import qilin.primitives.concrete.ECGroup;
|
||||
import qilin.primitives.PseudorandomGenerator;
|
||||
import qilin.primitives.generic.ElGamal;
|
||||
import qilin.util.PRGRandom;
|
||||
import qilin.util.Pair;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.*;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Created by talm on 17/11/15.
|
||||
*/
|
||||
public class ECElGamalEncryption extends GlobalCryptoSetup implements Encryption {
|
||||
final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
public final static String KEY_ALGORITHM = "ECDH";
|
||||
|
||||
/**
|
||||
* The Qilin format El-Gamal public key
|
||||
*/
|
||||
ECElGamal.PK elGamalPK;
|
||||
|
||||
ECCurve curve;
|
||||
|
||||
ECGroup group;
|
||||
|
||||
public ECGroup getGroup() { return group; }
|
||||
|
||||
public ECElGamal.PK getElGamalPK() {
|
||||
return elGamalPK;
|
||||
}
|
||||
|
||||
public void init(ConcreteCrypto.ElGamalPublicKey serializedPk) throws InvalidKeySpecException {
|
||||
AsymmetricKeyParameter keyParam;
|
||||
|
||||
try {
|
||||
keyParam = PublicKeyFactory.createKey(serializedPk.getSubjectPublicKeyInfo().toByteArray());
|
||||
} catch (IOException e) {
|
||||
// Shouldn't every happen
|
||||
logger.error("Invalid Public Key Encoding", e);
|
||||
throw new InvalidKeySpecException("Invalid Public Key Encoding", e);
|
||||
}
|
||||
|
||||
if (!(keyParam instanceof ECPublicKeyParameters)) {
|
||||
logger.error("Public key is a {}, not a valid public EC Key!", keyParam.getClass());
|
||||
throw new InvalidKeySpecException("Not a valid EC public key!");
|
||||
}
|
||||
|
||||
ECDomainParameters params = ((ECKeyParameters) keyParam).getParameters();
|
||||
ECParameterSpec ecParams = new ECParameterSpec(params.getCurve(), params.getG(), params.getN(), params.getH(),
|
||||
params.getSeed());
|
||||
|
||||
curve = params.getCurve();
|
||||
group = new ECGroup(ecParams);
|
||||
|
||||
elGamalPK = new ECElGamal.PK(group, ((ECPublicKeyParameters) keyParam).getQ());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Crypto.RerandomizableEncryptedMessage encrypt(Message plaintext, Crypto.EncryptionRandomness rnd) {
|
||||
|
||||
// We write the message using writeDelimited to so the length gets prepended.
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try {
|
||||
plaintext.writeDelimitedTo(out);
|
||||
} catch (IOException e) {
|
||||
logger.error("Should never happen!", e);
|
||||
throw new RuntimeException("Error in ByteArrayOutputStream!", e);
|
||||
}
|
||||
byte[] msg = out.toByteArray();
|
||||
ECPoint encodedMsg = group.injectiveEncode(msg, new PRGRandom(msg));
|
||||
|
||||
BigInteger rndInt = BigIntegers.fromUnsignedByteArray(rnd.getData().toByteArray());
|
||||
Pair<ECPoint,ECPoint> cipherText = elGamalPK.encrypt(encodedMsg, rndInt);
|
||||
ConcreteCrypto.ElGamalCiphertext encodedCipherText = ConcreteCrypto.ElGamalCiphertext.newBuilder()
|
||||
.setC1(ByteString.copyFrom(cipherText.a.getEncoded(true)))
|
||||
.setC2(ByteString.copyFrom(cipherText.b.getEncoded(true)))
|
||||
.build();
|
||||
|
||||
return Crypto.RerandomizableEncryptedMessage.newBuilder()
|
||||
.setData(encodedCipherText.toByteString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Crypto.RerandomizableEncryptedMessage rerandomize(Crypto.RerandomizableEncryptedMessage msg, Crypto.EncryptionRandomness rnd) throws InvalidProtocolBufferException {
|
||||
BigInteger rndInt = BigIntegers.fromUnsignedByteArray(rnd.getData().toByteArray());
|
||||
Pair<ECPoint,ECPoint> randomizer = elGamalPK.encrypt(curve.getInfinity(), rndInt);
|
||||
ConcreteCrypto.ElGamalCiphertext originalEncodedCipher= ConcreteCrypto.ElGamalCiphertext.parseFrom(msg.getData());
|
||||
|
||||
Pair<ECPoint,ECPoint> originalCipher = new Pair<>(
|
||||
curve.decodePoint(originalEncodedCipher.getC1().toByteArray()),
|
||||
curve.decodePoint(originalEncodedCipher.getC2().toByteArray()));
|
||||
Pair<ECPoint,ECPoint> newCipher = elGamalPK.add(originalCipher, randomizer);
|
||||
|
||||
return Crypto.RerandomizableEncryptedMessage.newBuilder()
|
||||
.setData(
|
||||
ConcreteCrypto.ElGamalCiphertext.newBuilder()
|
||||
.setC1(ByteString.copyFrom(newCipher.a.getEncoded(true)))
|
||||
.setC2(ByteString.copyFrom(newCipher.b.getEncoded(true)))
|
||||
.build().toByteString()
|
||||
).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Crypto.EncryptionRandomness generateRandomness(Random rand) {
|
||||
BigInteger randomInt = new BigInteger(group.getCurveParams().getN().bitLength() - 1, rand);
|
||||
Crypto.EncryptionRandomness retval = Crypto.EncryptionRandomness.newBuilder()
|
||||
.setData(ByteString.copyFrom(BigIntegers.asUnsignedByteArray(randomInt))).build();
|
||||
|
||||
return retval;
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.Provider;
|
||||
import java.security.Security;
|
||||
|
||||
/**
|
||||
|
@ -13,6 +14,7 @@ public class GlobalCryptoSetup {
|
|||
final static Logger logger = LoggerFactory.getLogger(GlobalCryptoSetup.class);
|
||||
|
||||
static boolean loadedBouncyCastle = false;
|
||||
static Provider bouncyCastleProvider;
|
||||
|
||||
public static boolean hasSecp256k1Curve() {
|
||||
// For now we just check if the java version is at least 8
|
||||
|
@ -22,17 +24,20 @@ public class GlobalCryptoSetup {
|
|||
return ((major > 1) || ((major > 0) && (minor > 7)));
|
||||
}
|
||||
|
||||
public static void doSetup() {
|
||||
// Make bouncycastle our default provider if we're running on a JVM version < 8
|
||||
// (earlier version don't support the EC curve we use for signatures)
|
||||
if (!hasSecp256k1Curve() && !loadedBouncyCastle) {
|
||||
loadedBouncyCastle = true;
|
||||
Security.insertProviderAt(new BouncyCastleProvider(), 1);
|
||||
logger.info("Using BouncyCastle instead of native provider to support secp256k1 named curve");
|
||||
public static Provider getBouncyCastleProvider() { doSetup(); return bouncyCastleProvider; }
|
||||
|
||||
public static synchronized void doSetup() {
|
||||
if (bouncyCastleProvider == null) {
|
||||
bouncyCastleProvider = new BouncyCastleProvider();
|
||||
// Make bouncycastle our default provider if we're running on a JVM version < 8
|
||||
// (earlier version don't support the EC curve we use for signatures)
|
||||
if (!hasSecp256k1Curve() && !loadedBouncyCastle) {
|
||||
loadedBouncyCastle = true;
|
||||
Security.insertProviderAt(bouncyCastleProvider, 1);
|
||||
logger.info("Using BouncyCastle instead of native provider to support secp256k1 named curve");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GlobalCryptoSetup() {
|
||||
doSetup();
|
||||
}
|
||||
public GlobalCryptoSetup() { doSetup(); }
|
||||
}
|
||||
|
|
|
@ -78,6 +78,10 @@ public class SHA256Digest extends GlobalCryptoSetup implements Digest {
|
|||
hash.update(msg);
|
||||
}
|
||||
|
||||
final public void update(ByteBuffer msg) {
|
||||
hash.update(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
hash.reset();
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
// Protobufs for specific crypto primitives
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package meerkat;
|
||||
|
||||
import 'meerkat/crypto.proto';
|
||||
|
||||
option java_package = "meerkat.protobuf";
|
||||
|
||||
|
||||
message ElGamalPublicKey {
|
||||
// DER-encoded SubjectPublicKeyInfo as in RFC 3279
|
||||
bytes subject_public_key_info = 1;
|
||||
}
|
||||
|
||||
// An El-Gamal ciphertext
|
||||
// Each group element should be an ASN.1 encoded curve point with compression.
|
||||
message ElGamalCiphertext {
|
||||
bytes c1 = 1; // First group element
|
||||
bytes c2 = 2; // Second group element
|
||||
}
|
|
@ -9,6 +9,10 @@ enum SignatureType {
|
|||
DSA = 1;
|
||||
}
|
||||
|
||||
message BigInteger {
|
||||
bytes data = 1;
|
||||
}
|
||||
|
||||
// A digital signature
|
||||
message Signature {
|
||||
SignatureType type = 1;
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package meerkat.crypto.concrete;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.Message;
|
||||
import meerkat.protobuf.BulletinBoardAPI.UnsignedBulletinBoardMessage;
|
||||
import meerkat.protobuf.Crypto;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyStore;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Created by talm on 12/11/15.
|
||||
*/
|
||||
public class ECDSADeterministicSignatureTest extends ECDSASignatureTest {
|
||||
|
||||
@Override
|
||||
protected ECDSASignature getSigner() { return new ECDSADeterministicSignature(); }
|
||||
|
||||
|
||||
/**
|
||||
* Make sure signatures don't vary
|
||||
*/
|
||||
@Test
|
||||
public void testDeterministicSigning() throws Exception {
|
||||
loadSigningKeys();
|
||||
|
||||
|
||||
for (int i = 0; i < REPEAT_COUNT; ++i) {
|
||||
BigInteger rawMsg = new BigInteger(50, rand);
|
||||
Message msg = Crypto.BigInteger.newBuilder()
|
||||
.setData(ByteString.copyFrom(rawMsg.toByteArray())).build();
|
||||
Crypto.Signature[] sigs = new Crypto.Signature[REPEAT_COUNT];
|
||||
|
||||
signer.updateContent(msg);
|
||||
sigs[0] = signer.sign();
|
||||
byte[] canonicalSig = sigs[0].toByteArray();
|
||||
|
||||
for (int j = 1; j < sigs.length; ++j) {
|
||||
signer.updateContent(msg);
|
||||
sigs[j] = signer.sign();
|
||||
|
||||
byte[] newSig = sigs[j].toByteArray();
|
||||
|
||||
assertArrayEquals("Signatures on same message differ (i="+i+",j="+j+")", canonicalSig, newSig);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +1,16 @@
|
|||
package meerkat.crypto.concrete;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.Message;
|
||||
import meerkat.protobuf.Crypto;
|
||||
import meerkat.crypto.concrete.ECDSASignature;
|
||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
@ -15,7 +18,7 @@ import static org.junit.Assert.assertTrue;
|
|||
/**
|
||||
* Created by talm on 12/11/15.
|
||||
*/
|
||||
public class TestECDSASignature {
|
||||
public class ECDSASignatureTest {
|
||||
public static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12";
|
||||
public static String KEYFILE_PASSWORD = "secret";
|
||||
|
||||
|
@ -27,16 +30,26 @@ public class TestECDSASignature {
|
|||
|
||||
public static String HELLO_WORLD = "hello world!";
|
||||
|
||||
public final static int REPEAT_COUNT = 10;
|
||||
|
||||
Random rand = new Random(0);
|
||||
|
||||
protected ECDSASignature signer;
|
||||
|
||||
protected ECDSASignature getSigner() { return new ECDSASignature(); }
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
signer = getSigner();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadSignatureKey() throws Exception {
|
||||
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
||||
char[] password = KEYFILE_PASSWORD.toCharArray();
|
||||
|
||||
ECDSASignature sig = new ECDSASignature();
|
||||
|
||||
KeyStore.Builder keyStore = sig.getPKCS12KeyStoreBuilder(keyStream, password);
|
||||
sig.loadSigningCertificate(keyStore);
|
||||
KeyStore.Builder keyStore = signer.getPKCS12KeyStoreBuilder(keyStream, password);
|
||||
signer.loadSigningCertificate(keyStore);
|
||||
keyStream.close();
|
||||
}
|
||||
|
||||
|
@ -44,9 +57,7 @@ public class TestECDSASignature {
|
|||
public void loadPEMVerificationKey() throws Exception {
|
||||
InputStream certStream = getClass().getResourceAsStream(CERT1_PEM_EXAMPLE);
|
||||
|
||||
ECDSASignature sig = new ECDSASignature();
|
||||
|
||||
sig.loadVerificationCertificates(certStream);
|
||||
signer.loadVerificationCertificates(certStream);
|
||||
certStream.close();
|
||||
}
|
||||
|
||||
|
@ -54,9 +65,7 @@ public class TestECDSASignature {
|
|||
public void loadDERVerificationKey() throws Exception {
|
||||
InputStream certStream = getClass().getResourceAsStream(CERT2_DER_EXAMPLE);
|
||||
|
||||
ECDSASignature sig = new ECDSASignature();
|
||||
|
||||
sig.loadVerificationCertificates(certStream);
|
||||
signer.loadVerificationCertificates(certStream);
|
||||
certStream.close();
|
||||
}
|
||||
|
||||
|
@ -67,8 +76,6 @@ public class TestECDSASignature {
|
|||
InputStream msgStream = getClass().getResourceAsStream(MSG_PLAINTEXT_EXAMPLE);
|
||||
InputStream sigStream = getClass().getResourceAsStream(MSG_SIG_EXAMPLE);
|
||||
|
||||
ECDSASignature signer = new ECDSASignature();
|
||||
|
||||
signer.loadVerificationCertificates(certStream);
|
||||
certStream.close();
|
||||
|
||||
|
@ -79,7 +86,7 @@ public class TestECDSASignature {
|
|||
|
||||
Crypto.Signature builtSig = sig.build();
|
||||
signer.initVerify(builtSig);
|
||||
signer.updateSigner(msgStream);
|
||||
signer.updateContent(msgStream);
|
||||
assertTrue("Signature did not verify!", signer.verify());
|
||||
}
|
||||
|
||||
|
@ -89,8 +96,6 @@ public class TestECDSASignature {
|
|||
InputStream msgStream = getClass().getResourceAsStream(MSG_PLAINTEXT_EXAMPLE);
|
||||
InputStream sigStream = getClass().getResourceAsStream(MSG_SIG_EXAMPLE);
|
||||
|
||||
ECDSASignature signer = new ECDSASignature();
|
||||
|
||||
signer.loadVerificationCertificates(certStream);
|
||||
certStream.close();
|
||||
|
||||
|
@ -105,7 +110,7 @@ public class TestECDSASignature {
|
|||
|
||||
Crypto.Signature builtSig = sig.build();
|
||||
signer.initVerify(builtSig);
|
||||
signer.updateSigner(msgStream);
|
||||
signer.updateContent(msgStream);
|
||||
assertFalse("Bad Signature passed verification!", signer.verify());
|
||||
}
|
||||
|
||||
|
@ -116,8 +121,6 @@ public class TestECDSASignature {
|
|||
InputStream msgStream = getClass().getResourceAsStream(MSG_PLAINTEXT_EXAMPLE);
|
||||
InputStream sigStream = getClass().getResourceAsStream(MSG_SIG_EXAMPLE);
|
||||
|
||||
ECDSASignature signer = new ECDSASignature();
|
||||
|
||||
signer.loadVerificationCertificates(certStream);
|
||||
certStream.close();
|
||||
|
||||
|
@ -130,30 +133,27 @@ public class TestECDSASignature {
|
|||
|
||||
Crypto.Signature builtSig = sig.build();
|
||||
signer.initVerify(builtSig);
|
||||
signer.updateSigner(msgStream);
|
||||
signer.updateContent(msgStream);
|
||||
assertFalse("Signature doesn't match message but passed verification!", signer.verify());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void signAndVerify() throws Exception {
|
||||
protected void loadSigningKeys() throws Exception {
|
||||
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
||||
char[] password = KEYFILE_PASSWORD.toCharArray();
|
||||
|
||||
ECDSASignature signer = new ECDSASignature();
|
||||
|
||||
KeyStore.Builder keyStore = signer.getPKCS12KeyStoreBuilder(keyStream, password);
|
||||
signer.loadSigningCertificate(keyStore);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signAndVerify() throws Exception {
|
||||
loadSigningKeys();
|
||||
|
||||
UnsignedBulletinBoardMessage.Builder unsignedMsgBuilder = UnsignedBulletinBoardMessage.newBuilder();
|
||||
unsignedMsgBuilder.setData(ByteString.copyFromUtf8(HELLO_WORLD));
|
||||
unsignedMsgBuilder.addTag("Tag1");
|
||||
unsignedMsgBuilder.addTag("Tag2");
|
||||
unsignedMsgBuilder.addTag("Tag3");
|
||||
|
||||
UnsignedBulletinBoardMessage usMsg = unsignedMsgBuilder.build();
|
||||
BigInteger rawMsg = new BigInteger(50, rand);
|
||||
Crypto.BigInteger usMsg = Crypto.BigInteger.newBuilder()
|
||||
.setData(ByteString.copyFrom(rawMsg.toByteArray())).build();
|
||||
|
||||
signer.updateContent(usMsg);
|
||||
Crypto.Signature sig = signer.sign();
|
||||
|
@ -166,5 +166,55 @@ public class TestECDSASignature {
|
|||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void signMultipleAndVerify() throws Exception {
|
||||
loadSigningKeys();
|
||||
|
||||
Message[] msgs = new Message[REPEAT_COUNT];
|
||||
for (int i = 0; i < msgs.length; ++i) {
|
||||
|
||||
BigInteger rawMsg = new BigInteger(50, rand);
|
||||
msgs[i] = Crypto.BigInteger.newBuilder()
|
||||
.setData(ByteString.copyFrom(rawMsg.toByteArray())).build();
|
||||
signer.updateContent(msgs[i]);
|
||||
}
|
||||
|
||||
Crypto.Signature sig = signer.sign();
|
||||
|
||||
signer.loadVerificationCertificates(getClass().getResourceAsStream(CERT1_PEM_EXAMPLE));
|
||||
|
||||
signer.initVerify(sig);
|
||||
for (int i = 0; i < msgs.length; ++i) {
|
||||
signer.updateContent(msgs[i]);
|
||||
}
|
||||
assertTrue("Couldn't verify signature on ", signer.verify());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleSignAndVerify() throws Exception {
|
||||
loadSigningKeys();
|
||||
|
||||
Message[] msgs = new Message[REPEAT_COUNT];
|
||||
Crypto.Signature[] sigs = new Crypto.Signature[REPEAT_COUNT];
|
||||
for (int i = 0; i < msgs.length; ++i) {
|
||||
BigInteger rawMsg = new BigInteger(50, rand);
|
||||
msgs[i] = Crypto.BigInteger.newBuilder()
|
||||
.setData(ByteString.copyFrom(rawMsg.toByteArray())).build();
|
||||
signer.updateContent(msgs[i]);
|
||||
sigs[i] = signer.sign();
|
||||
}
|
||||
|
||||
signer.loadVerificationCertificates(getClass().getResourceAsStream(CERT1_PEM_EXAMPLE));
|
||||
|
||||
|
||||
for (int i = 0; i < msgs.length; ++i) {
|
||||
signer.initVerify(sigs[i]);
|
||||
signer.updateContent(msgs[i]);
|
||||
assertTrue("Couldn't verify signature on ", signer.verify());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
package meerkat.crypto.concrete;
|
||||
|
||||
import meerkat.protobuf.BulletinBoardAPI;
|
||||
import meerkat.protobuf.ConcreteCrypto;
|
||||
import meerkat.protobuf.Crypto;
|
||||
import meerkat.protobuf.Voting;
|
||||
import org.bouncycastle.math.ec.ECPoint;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import qilin.primitives.concrete.ECElGamal;
|
||||
import qilin.primitives.concrete.ECGroup;
|
||||
import qilin.primitives.generic.ElGamal;
|
||||
import qilin.util.Pair;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test class for {@link ECElGamalEncryption}
|
||||
*/
|
||||
public class ECElGamalEncryptionTest {
|
||||
final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
/**
|
||||
* Number of times to repeat probabilistic tests.
|
||||
*/
|
||||
public final static int CONFIDENCE = 10;
|
||||
|
||||
Random rand = new Random(0); // Insecure deterministic random for testing.
|
||||
|
||||
ECElGamal.SK key;
|
||||
ECGroup group;
|
||||
ECElGamalEncryption enc;
|
||||
ConcreteCrypto.ElGamalPublicKey serializedPk;
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
group = new ECGroup("secp256k1");
|
||||
BigInteger sk = ECElGamal.generateSecretKey(group, rand);
|
||||
key = new ECElGamal.SK(group, sk);
|
||||
serializedPk = ECElGamalUtils.serializePk(group, key);
|
||||
|
||||
|
||||
enc = new ECElGamalEncryption();
|
||||
|
||||
enc.init(serializedPk);
|
||||
}
|
||||
|
||||
|
||||
Voting.PlaintextBallot genRandomBallot(int numQuestions, int numAnswers, int maxAnswer) {
|
||||
Voting.PlaintextBallot.Builder ballot = Voting.PlaintextBallot.newBuilder();
|
||||
ballot.setSerialNumber(rand.nextInt(1000000));
|
||||
for (int i = 0; i < numQuestions; ++i) {
|
||||
Voting.BallotAnswer.Builder answers = ballot.addAnswersBuilder();
|
||||
for (int j = 0; j < numAnswers; ++j) {
|
||||
answers.addAnswer(rand.nextInt(maxAnswer));
|
||||
}
|
||||
}
|
||||
return ballot.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing just the key management
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testPkSerialization() throws Exception {
|
||||
ECElGamal.PK pk = enc.getElGamalPK();
|
||||
|
||||
ECPoint point = enc.getGroup().sample(rand);
|
||||
Pair<ECPoint, ECPoint> cipher = pk.encrypt(point, pk.getRandom(rand));
|
||||
|
||||
ECPoint decrypted = key.decrypt(cipher);
|
||||
|
||||
assertEquals("Decrypted value not equal to encrypted value!", point, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryption() throws Exception {
|
||||
for (int i = 0; i < CONFIDENCE; ++i) {
|
||||
Voting.PlaintextBallot msg = genRandomBallot(2,3,16); // 2 questions with 3 answers each, in range 0-15.
|
||||
if (msg.getSerializedSize() > enc.getGroup().getInjectiveEncodeMsgLength()) {
|
||||
logger.error("Test Message too big (|msg|={} > max={}), expect failure.",
|
||||
msg.getSerializedSize(), enc.getGroup().getInjectiveEncodeMsgLength());
|
||||
}
|
||||
|
||||
Crypto.RerandomizableEncryptedMessage cipherText = enc.encrypt(msg, enc.generateRandomness(rand));
|
||||
|
||||
Voting.PlaintextBallot decrypted = ECElGamalUtils.decrypt(Voting.PlaintextBallot.class, key, group, cipherText);
|
||||
|
||||
assertEquals("Decrypted value differs from encrypted value (i="+i+")!", msg, decrypted);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRerandomizeModifiesCiphertext() throws Exception {
|
||||
Voting.PlaintextBallot msg = genRandomBallot(2,3,16); // 2 questions with 3 answers each, in range 0-15.
|
||||
Crypto.RerandomizableEncryptedMessage cipher1 = enc.encrypt(msg, enc.generateRandomness(rand));
|
||||
Crypto.RerandomizableEncryptedMessage cipher2 = enc.rerandomize(cipher1, enc.generateRandomness(rand));
|
||||
assertNotEquals("Rerandomized cipher identical to original!", cipher1, cipher2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRerandomizePreservesPlaintext() throws Exception {
|
||||
for (int i = 0; i < CONFIDENCE; ++i) {
|
||||
Voting.PlaintextBallot msg = genRandomBallot(2,3,16); // 2 questions with 3 answers each, in range 0-15.
|
||||
|
||||
Crypto.RerandomizableEncryptedMessage cipher = enc.encrypt(msg, enc.generateRandomness(rand));
|
||||
Crypto.RerandomizableEncryptedMessage cipher2 = cipher;
|
||||
for (int j = 0; j < CONFIDENCE; ++j)
|
||||
cipher2 = enc.rerandomize(cipher2, enc.generateRandomness(rand));
|
||||
|
||||
Voting.PlaintextBallot decrypted = ECElGamalUtils.decrypt(Voting.PlaintextBallot.class, key, group,
|
||||
cipher2);
|
||||
|
||||
assertEquals("Decrypted value differs from original encrypted value (i="+i+")!", msg, decrypted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
package meerkat.crypto.concrete;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.GeneratedMessage;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.google.protobuf.Message;
|
||||
import meerkat.protobuf.ConcreteCrypto;
|
||||
import meerkat.protobuf.Crypto;
|
||||
import org.bouncycastle.jce.spec.ECParameterSpec;
|
||||
import org.bouncycastle.jce.spec.ECPublicKeySpec;
|
||||
import org.bouncycastle.math.ec.ECPoint;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import qilin.primitives.concrete.ECElGamal;
|
||||
import qilin.primitives.concrete.ECGroup;
|
||||
import qilin.primitives.generic.ElGamal;
|
||||
import qilin.util.Pair;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
|
||||
/**
|
||||
* utilities for ECElgamal
|
||||
*/
|
||||
public class ECElGamalUtils {
|
||||
final static Logger logger = LoggerFactory.getLogger(ECElGamalUtils.class);
|
||||
|
||||
public final static String ENCRYPTION_KEY_ALGORITHM = "ECDH";
|
||||
|
||||
/**
|
||||
* Serialize an El-Gamal public key into a form acceptable by {@link ECElGamalEncryption}
|
||||
* @param pk
|
||||
* @return
|
||||
*/
|
||||
public static ConcreteCrypto.ElGamalPublicKey serializePk(ECGroup group, ElGamal.PK<ECPoint> pk) {
|
||||
ECPoint pkPoint = pk.getPK();
|
||||
ECParameterSpec params = group.getCurveParams();
|
||||
|
||||
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(pkPoint, params);
|
||||
|
||||
try {
|
||||
KeyFactory fact = KeyFactory.getInstance(ENCRYPTION_KEY_ALGORITHM,
|
||||
GlobalCryptoSetup.getBouncyCastleProvider());
|
||||
PublicKey javaPk = fact.generatePublic(pubKeySpec);
|
||||
ConcreteCrypto.ElGamalPublicKey serializedPk = ConcreteCrypto.ElGamalPublicKey.newBuilder()
|
||||
.setSubjectPublicKeyInfo(ByteString.copyFrom(javaPk.getEncoded())).build();
|
||||
|
||||
return serializedPk;
|
||||
} catch (NoSuchAlgorithmException|InvalidKeySpecException e) {
|
||||
logger.error("Should never happen!", e);
|
||||
throw new RuntimeException("Error converting public key!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard (non-threshold) decryption for testing purposes.
|
||||
* @param secretKey
|
||||
* @return
|
||||
*/
|
||||
public static <T extends Message> T decrypt(Class<T> plaintextMessageType, ECElGamal.SK secretKey, ECGroup group, Crypto.RerandomizableEncryptedMessage opaqueCipher)
|
||||
throws InvalidProtocolBufferException {
|
||||
ConcreteCrypto.ElGamalCiphertext cipherText = ConcreteCrypto.ElGamalCiphertext.parseFrom(opaqueCipher.getData());
|
||||
ByteString c1encoded = cipherText.getC1();
|
||||
ByteString c2encoded = cipherText.getC2();
|
||||
|
||||
ECPoint c1 = group.decode(c1encoded.toByteArray());
|
||||
ECPoint c2 = group.decode(c2encoded.toByteArray());
|
||||
|
||||
ECPoint plaintextEncoded = secretKey.decrypt(new Pair<ECPoint, ECPoint>(c1, c2));
|
||||
|
||||
byte[] plaintext = group.injectiveDecode(plaintextEncoded);
|
||||
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(plaintext);
|
||||
|
||||
try {
|
||||
java.lang.reflect.Method newBuilder = plaintextMessageType.getMethod("newBuilder");
|
||||
GeneratedMessage.Builder builder = (GeneratedMessage.Builder) newBuilder.invoke(plaintextMessageType);
|
||||
builder.mergeDelimitedFrom(in);
|
||||
return plaintextMessageType.cast(builder.build());
|
||||
} catch (Exception e) {
|
||||
logger.error("Error parsing incoming message", e);
|
||||
throw new InvalidProtocolBufferException("Plaintext protobuf error");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -73,7 +73,6 @@ idea {
|
|||
|
||||
def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java"
|
||||
|
||||
println "Adding $srcDir"
|
||||
// add protobuf generated sources to generated source dir.
|
||||
if ("test".equals(sourceSet.name)) {
|
||||
testSourceDirs += file(srcDir)
|
||||
|
|
|
@ -73,7 +73,6 @@ idea {
|
|||
|
||||
def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java"
|
||||
|
||||
println "Adding $srcDir"
|
||||
// add protobuf generated sources to generated source dir.
|
||||
if ("test".equals(sourceSet.name)) {
|
||||
testSourceDirs += file(srcDir)
|
||||
|
|
|
@ -72,7 +72,6 @@ idea {
|
|||
|
||||
def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java"
|
||||
|
||||
println "Adding $srcDir"
|
||||
// add protobuf generated sources to generated source dir.
|
||||
if ("test".equals(sourceSet.name)) {
|
||||
testSourceDirs += file(srcDir)
|
||||
|
|
Loading…
Reference in New Issue