Added deterministic version of signature generation (suitable for use in voting booth); improved signature tests
parent
984d7457c6
commit
9bcdb411e2
|
@ -51,7 +51,7 @@ public interface DigitalSignature {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sign the content that was added.
|
* Sign the content that was added using {@link #updateContent(Message)}.
|
||||||
* @return
|
* @return
|
||||||
* @throws SignatureException
|
* @throws SignatureException
|
||||||
*/
|
*/
|
||||||
|
@ -75,7 +75,7 @@ public interface DigitalSignature {
|
||||||
/**
|
/**
|
||||||
* Loads a private signing key. The keystore must include both the public and private
|
* Loads a private signing key. The keystore must include both the public and private
|
||||||
* key parts.
|
* 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.
|
* Calling this method again will replace the key.
|
||||||
*
|
*
|
||||||
* @param keyStoreBuilder A keystore builder that can be used to load a keystore.
|
* @param keyStoreBuilder A keystore builder that can be used to load a keystore.
|
||||||
|
|
|
@ -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 KEYSTORE_TYPE = "PKCS12";
|
||||||
final public static String DEFAULT_SIGNATURE_ALGORITHM = "SHA256withECDSA";
|
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
|
* Buffer used to hold length in for hash update
|
||||||
|
@ -58,6 +58,11 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatur
|
||||||
*/
|
*/
|
||||||
java.security.Signature signer;
|
java.security.Signature signer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The currently loaded signing key.
|
||||||
|
*/
|
||||||
|
PrivateKey loadedSigningKey;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute a fingerprint of a cert as a SHA256 hash.
|
* 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) {
|
public ByteString computeCertificateFingerprint(Certificate cert) {
|
||||||
try {
|
try {
|
||||||
digest.reset();
|
certDigest.reset();
|
||||||
byte[] data = cert.getEncoded();
|
byte[] data = cert.getEncoded();
|
||||||
digest.update(data);
|
certDigest.update(data);
|
||||||
return ByteString.copyFrom(digest.digest());
|
return ByteString.copyFrom(certDigest.digest());
|
||||||
} catch (CertificateEncodingException e) {
|
} catch (CertificateEncodingException e) {
|
||||||
// Shouldn't happen
|
// Shouldn't happen
|
||||||
logger.error("Certificate encoding error", e);
|
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());
|
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);
|
ByteString inStr = ByteString.readFrom(in);
|
||||||
signer.update(inStr.asReadOnlyByteBuffer());
|
signer.update(inStr.asReadOnlyByteBuffer());
|
||||||
}
|
}
|
||||||
|
@ -234,10 +239,13 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatur
|
||||||
String alias = aliases.nextElement();
|
String alias = aliases.nextElement();
|
||||||
logger.trace("Testing keystore entry {}", alias);
|
logger.trace("Testing keystore entry {}", alias);
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Certificate cert = keyStore.getCertificate(alias);
|
Certificate cert = keyStore.getCertificate(alias);
|
||||||
logger.trace("keystore entry {}, has cert type {}", alias, cert.getClass());
|
logger.trace("keystore entry {}, has cert type {}", alias, cert.getClass());
|
||||||
|
|
||||||
Key key;
|
Key key;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
key = keyStore.getKey(alias, null);
|
key = keyStore.getKey(alias, null);
|
||||||
} catch (UnrecoverableKeyException e) {
|
} catch (UnrecoverableKeyException e) {
|
||||||
|
@ -267,9 +275,11 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatur
|
||||||
}
|
}
|
||||||
logger.trace("keystore entry {}, has key type {}", alias, key.getClass());
|
logger.trace("keystore entry {}, has key type {}", alias, key.getClass());
|
||||||
if (key instanceof PrivateKey) {
|
if (key instanceof PrivateKey) {
|
||||||
|
loadedSigningKey = (PrivateKey) key;
|
||||||
loadedSigningKeyId = computeCertificateFingerprint(cert);
|
loadedSigningKeyId = computeCertificateFingerprint(cert);
|
||||||
signer.initSign((PrivateKey) key);
|
signer.initSign(loadedSigningKey);
|
||||||
logger.debug("Loaded signing key with ID {}", Hex.encode(loadedSigningKeyId));
|
logger.debug("Loaded signing key with ID {}", Hex.encode(loadedSigningKeyId));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
logger.info("Certificate {} in keystore does not have a private key", cert.toString());
|
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)
|
if (loadedSigningKeyId != null)
|
||||||
signer.initSign(null);
|
signer.initSign(null);
|
||||||
loadedSigningKeyId = null;
|
loadedSigningKeyId = null;
|
||||||
|
loadedSigningKey = null;
|
||||||
|
// Start garbage collection?
|
||||||
} catch (InvalidKeyException e) {
|
} catch (InvalidKeyException e) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,10 @@ public class SHA256Digest extends GlobalCryptoSetup implements Digest {
|
||||||
hash.update(msg);
|
hash.update(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final public void update(ByteBuffer msg) {
|
||||||
|
hash.update(msg);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reset() {
|
public void reset() {
|
||||||
hash.reset();
|
hash.reset();
|
||||||
|
|
|
@ -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,15 +1,16 @@
|
||||||
package meerkat.crypto.concrete;
|
package meerkat.crypto.concrete;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
|
import com.google.protobuf.Message;
|
||||||
import meerkat.protobuf.Crypto;
|
import meerkat.protobuf.Crypto;
|
||||||
import meerkat.protobuf.BulletinBoardAPI.*;
|
import meerkat.protobuf.BulletinBoardAPI.*;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.util.Arrays;
|
import java.util.Random;
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
@ -29,16 +30,26 @@ public class ECDSASignatureTest {
|
||||||
|
|
||||||
public static String HELLO_WORLD = "hello world!";
|
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
|
@Test
|
||||||
public void loadSignatureKey() throws Exception {
|
public void loadSignatureKey() throws Exception {
|
||||||
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
||||||
char[] password = KEYFILE_PASSWORD.toCharArray();
|
char[] password = KEYFILE_PASSWORD.toCharArray();
|
||||||
|
|
||||||
ECDSASignature sig = new ECDSASignature();
|
KeyStore.Builder keyStore = signer.getPKCS12KeyStoreBuilder(keyStream, password);
|
||||||
|
signer.loadSigningCertificate(keyStore);
|
||||||
KeyStore.Builder keyStore = sig.getPKCS12KeyStoreBuilder(keyStream, password);
|
|
||||||
sig.loadSigningCertificate(keyStore);
|
|
||||||
keyStream.close();
|
keyStream.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,9 +57,7 @@ public class ECDSASignatureTest {
|
||||||
public void loadPEMVerificationKey() throws Exception {
|
public void loadPEMVerificationKey() throws Exception {
|
||||||
InputStream certStream = getClass().getResourceAsStream(CERT1_PEM_EXAMPLE);
|
InputStream certStream = getClass().getResourceAsStream(CERT1_PEM_EXAMPLE);
|
||||||
|
|
||||||
ECDSASignature sig = new ECDSASignature();
|
signer.loadVerificationCertificates(certStream);
|
||||||
|
|
||||||
sig.loadVerificationCertificates(certStream);
|
|
||||||
certStream.close();
|
certStream.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,9 +65,7 @@ public class ECDSASignatureTest {
|
||||||
public void loadDERVerificationKey() throws Exception {
|
public void loadDERVerificationKey() throws Exception {
|
||||||
InputStream certStream = getClass().getResourceAsStream(CERT2_DER_EXAMPLE);
|
InputStream certStream = getClass().getResourceAsStream(CERT2_DER_EXAMPLE);
|
||||||
|
|
||||||
ECDSASignature sig = new ECDSASignature();
|
signer.loadVerificationCertificates(certStream);
|
||||||
|
|
||||||
sig.loadVerificationCertificates(certStream);
|
|
||||||
certStream.close();
|
certStream.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,8 +76,6 @@ public class ECDSASignatureTest {
|
||||||
InputStream msgStream = getClass().getResourceAsStream(MSG_PLAINTEXT_EXAMPLE);
|
InputStream msgStream = getClass().getResourceAsStream(MSG_PLAINTEXT_EXAMPLE);
|
||||||
InputStream sigStream = getClass().getResourceAsStream(MSG_SIG_EXAMPLE);
|
InputStream sigStream = getClass().getResourceAsStream(MSG_SIG_EXAMPLE);
|
||||||
|
|
||||||
ECDSASignature signer = new ECDSASignature();
|
|
||||||
|
|
||||||
signer.loadVerificationCertificates(certStream);
|
signer.loadVerificationCertificates(certStream);
|
||||||
certStream.close();
|
certStream.close();
|
||||||
|
|
||||||
|
@ -81,7 +86,7 @@ public class ECDSASignatureTest {
|
||||||
|
|
||||||
Crypto.Signature builtSig = sig.build();
|
Crypto.Signature builtSig = sig.build();
|
||||||
signer.initVerify(builtSig);
|
signer.initVerify(builtSig);
|
||||||
signer.updateSigner(msgStream);
|
signer.updateContent(msgStream);
|
||||||
assertTrue("Signature did not verify!", signer.verify());
|
assertTrue("Signature did not verify!", signer.verify());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,8 +96,6 @@ public class ECDSASignatureTest {
|
||||||
InputStream msgStream = getClass().getResourceAsStream(MSG_PLAINTEXT_EXAMPLE);
|
InputStream msgStream = getClass().getResourceAsStream(MSG_PLAINTEXT_EXAMPLE);
|
||||||
InputStream sigStream = getClass().getResourceAsStream(MSG_SIG_EXAMPLE);
|
InputStream sigStream = getClass().getResourceAsStream(MSG_SIG_EXAMPLE);
|
||||||
|
|
||||||
ECDSASignature signer = new ECDSASignature();
|
|
||||||
|
|
||||||
signer.loadVerificationCertificates(certStream);
|
signer.loadVerificationCertificates(certStream);
|
||||||
certStream.close();
|
certStream.close();
|
||||||
|
|
||||||
|
@ -107,7 +110,7 @@ public class ECDSASignatureTest {
|
||||||
|
|
||||||
Crypto.Signature builtSig = sig.build();
|
Crypto.Signature builtSig = sig.build();
|
||||||
signer.initVerify(builtSig);
|
signer.initVerify(builtSig);
|
||||||
signer.updateSigner(msgStream);
|
signer.updateContent(msgStream);
|
||||||
assertFalse("Bad Signature passed verification!", signer.verify());
|
assertFalse("Bad Signature passed verification!", signer.verify());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,8 +121,6 @@ public class ECDSASignatureTest {
|
||||||
InputStream msgStream = getClass().getResourceAsStream(MSG_PLAINTEXT_EXAMPLE);
|
InputStream msgStream = getClass().getResourceAsStream(MSG_PLAINTEXT_EXAMPLE);
|
||||||
InputStream sigStream = getClass().getResourceAsStream(MSG_SIG_EXAMPLE);
|
InputStream sigStream = getClass().getResourceAsStream(MSG_SIG_EXAMPLE);
|
||||||
|
|
||||||
ECDSASignature signer = new ECDSASignature();
|
|
||||||
|
|
||||||
signer.loadVerificationCertificates(certStream);
|
signer.loadVerificationCertificates(certStream);
|
||||||
certStream.close();
|
certStream.close();
|
||||||
|
|
||||||
|
@ -132,30 +133,27 @@ public class ECDSASignatureTest {
|
||||||
|
|
||||||
Crypto.Signature builtSig = sig.build();
|
Crypto.Signature builtSig = sig.build();
|
||||||
signer.initVerify(builtSig);
|
signer.initVerify(builtSig);
|
||||||
signer.updateSigner(msgStream);
|
signer.updateContent(msgStream);
|
||||||
assertFalse("Signature doesn't match message but passed verification!", signer.verify());
|
assertFalse("Signature doesn't match message but passed verification!", signer.verify());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
protected void loadSigningKeys() throws Exception {
|
||||||
public void signAndVerify() throws Exception {
|
|
||||||
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
||||||
char[] password = KEYFILE_PASSWORD.toCharArray();
|
char[] password = KEYFILE_PASSWORD.toCharArray();
|
||||||
|
|
||||||
ECDSASignature signer = new ECDSASignature();
|
|
||||||
|
|
||||||
KeyStore.Builder keyStore = signer.getPKCS12KeyStoreBuilder(keyStream, password);
|
KeyStore.Builder keyStore = signer.getPKCS12KeyStoreBuilder(keyStream, password);
|
||||||
signer.loadSigningCertificate(keyStore);
|
signer.loadSigningCertificate(keyStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void signAndVerify() throws Exception {
|
||||||
|
loadSigningKeys();
|
||||||
|
|
||||||
UnsignedBulletinBoardMessage.Builder unsignedMsgBuilder = UnsignedBulletinBoardMessage.newBuilder();
|
BigInteger rawMsg = new BigInteger(50, rand);
|
||||||
unsignedMsgBuilder.setData(ByteString.copyFromUtf8(HELLO_WORLD));
|
Crypto.BigInteger usMsg = Crypto.BigInteger.newBuilder()
|
||||||
unsignedMsgBuilder.addTags("Tag1");
|
.setData(ByteString.copyFrom(rawMsg.toByteArray())).build();
|
||||||
unsignedMsgBuilder.addTags("Tag2");
|
|
||||||
unsignedMsgBuilder.addTags("Tag3");
|
|
||||||
|
|
||||||
UnsignedBulletinBoardMessage usMsg = unsignedMsgBuilder.build();
|
|
||||||
|
|
||||||
signer.updateContent(usMsg);
|
signer.updateContent(usMsg);
|
||||||
Crypto.Signature sig = signer.sign();
|
Crypto.Signature sig = signer.sign();
|
||||||
|
@ -168,5 +166,55 @@ public class ECDSASignatureTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue