Used API compatible with PCKS11 (e.g., smartcards); refactored
parent
b839447f87
commit
baba4df3a9
|
@ -2,9 +2,12 @@ package meerkat.crypto;
|
|||
|
||||
import com.google.protobuf.Message;
|
||||
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.callback.PasswordCallback;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.SignatureException;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.CertificateException;
|
||||
|
@ -24,7 +27,7 @@ public interface DigitalSignature {
|
|||
* This will consume the entire stream.
|
||||
* Certificates can be either DER-encoded (binary) or PEM (base64) encoded.
|
||||
* This may be called multiple times to load several different certificates.
|
||||
* It must be called before calling {@link #verify(Signature, List)}.
|
||||
* It must be called before calling {@link #verify()}.
|
||||
* @param certStream source from which certificates are loaded
|
||||
* @throws CertificateException on parsing errors
|
||||
*/
|
||||
|
@ -36,35 +39,55 @@ public interface DigitalSignature {
|
|||
*/
|
||||
public void clearVerificationCertificates();
|
||||
|
||||
|
||||
/**
|
||||
* Verify a signature on a list of messages.
|
||||
* The signature is computed on the serialized messages, separated with
|
||||
* the {@link Digest#CONCAT_MARKER} string.
|
||||
* For the signature to be valid, the verification key corresponding
|
||||
* to the signature must have been loaded using {@link #loadVerificationCertificates(InputStream)};
|
||||
* otherwise a CertificateException is thrown
|
||||
* Add msg to the content stream to be verified / signed. Each message is always (automatically)
|
||||
* prepended with its length as a 32-bit unsigned integer in network byte order.
|
||||
*
|
||||
* @param sig The signature on the messages
|
||||
* @param msgs The messages themselves
|
||||
* @return
|
||||
* @throws CertificateException the certificate required for verification was not loaded.
|
||||
* @param msg
|
||||
* @throws SignatureException
|
||||
*/
|
||||
public boolean verify(Signature sig, List<Message> msgs)
|
||||
public void updateContent(Message msg) throws SignatureException;
|
||||
|
||||
|
||||
/**
|
||||
* Sign the content that was added.
|
||||
* @return
|
||||
* @throws SignatureException
|
||||
*/
|
||||
Signature sign() throws SignatureException;
|
||||
|
||||
/**
|
||||
* Initialize the verifier with the certificate whose Id is in sig.
|
||||
* @param sig
|
||||
* @throws CertificateException
|
||||
* @throws InvalidKeyException
|
||||
*/
|
||||
void initVerify(Signature sig)
|
||||
throws CertificateException, InvalidKeyException;
|
||||
|
||||
/**
|
||||
* Loads a private signing key. The certificate must be in PKCS12 format and include both
|
||||
* the public and private keys.
|
||||
* Verify the updated content using the initialized signature.
|
||||
* @return
|
||||
*/
|
||||
public boolean verify();
|
||||
|
||||
/**
|
||||
* 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)}
|
||||
* Calling this method again will replace the key.
|
||||
*
|
||||
* @param keyStream
|
||||
* @param keyStoreBuilder A keystore builder that can be used to load a keystore.
|
||||
*/
|
||||
public void loadSigningCertificate(InputStream keyStream, char[] password)
|
||||
public void loadSigningCertificate(KeyStore.Builder keyStoreBuilder)
|
||||
throws IOException, CertificateException, UnrecoverableKeyException;
|
||||
|
||||
public Signature sign(List<Message> msg) throws SignatureException;
|
||||
|
||||
|
||||
/**
|
||||
* Clear the signing key (will require authentication to use again).
|
||||
*/
|
||||
public void clearSigningKey();
|
||||
|
||||
}
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
package meerkat.crypto.concrete;
|
||||
|
||||
import java.io.IOError;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.security.*;
|
||||
import java.security.cert.*;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.*;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import meerkat.crypto.Digest;
|
||||
import meerkat.protobuf.Crypto;
|
||||
import meerkat.util.Hex;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -22,15 +19,18 @@ import com.google.protobuf.Message;
|
|||
import meerkat.crypto.DigitalSignature;
|
||||
import meerkat.protobuf.Crypto.Signature;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.callback.PasswordCallback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
|
||||
|
||||
/**
|
||||
* Sign and verify digital signatures.
|
||||
* <p>
|
||||
* <p/>
|
||||
* This class is not thread-safe (each thread should have its own instance).
|
||||
*/
|
||||
public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignature {
|
||||
public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignature {
|
||||
final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
final public static String KEYSTORE_TYPE = "PKCS12";
|
||||
|
@ -38,27 +38,39 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatu
|
|||
|
||||
SHA256Digest digest = new SHA256Digest();
|
||||
|
||||
Map<ByteString, PublicKey> loadedCertificates = new HashMap<>();
|
||||
Map<ByteString, Certificate> loadedCertificates = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Signature currently loaded (will be used in calls to {@link #verify()}).
|
||||
*/
|
||||
ByteString loadedSignature = null;
|
||||
|
||||
ByteString loadedSigningKeyId = null;
|
||||
|
||||
/**
|
||||
* The actual signing implementation.
|
||||
* The actual signing implementation. (used for both signing and verifying)
|
||||
*/
|
||||
java.security.Signature signer;
|
||||
|
||||
|
||||
/**
|
||||
* Compute a fingerprint of a public key as a SHA256 hash.
|
||||
* Compute a fingerprint of a cert as a SHA256 hash.
|
||||
*
|
||||
* @param pubKey
|
||||
* @param cert
|
||||
* @return
|
||||
*/
|
||||
public ByteString computePublicKeyID(PublicKey pubKey) {
|
||||
digest.reset();
|
||||
byte[] data = pubKey.getEncoded();
|
||||
digest.update(data);
|
||||
return ByteString.copyFrom(digest.digest());
|
||||
public ByteString computeCertificateFingerprint(Certificate cert) {
|
||||
try {
|
||||
digest.reset();
|
||||
byte[] data = cert.getEncoded();
|
||||
digest.update(data);
|
||||
return ByteString.copyFrom(digest.digest());
|
||||
} catch (CertificateEncodingException e) {
|
||||
// Shouldn't happen
|
||||
logger.error("Certificate encoding error", e);
|
||||
return ByteString.EMPTY;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ECDSASignature(java.security.Signature signer) {
|
||||
|
@ -70,7 +82,7 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatu
|
|||
this.signer = java.security.Signature.getInstance(DEFAULT_SIGNATURE_ALGORITHM);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// Should never happen
|
||||
logger.error("Couldn't find implementation for {} signatures: {}", DEFAULT_SIGNATURE_ALGORITHM, e);
|
||||
logger.error("Couldn't find implementation for " + DEFAULT_SIGNATURE_ALGORITHM + " signatures", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,8 +98,8 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatu
|
|||
continue;
|
||||
}
|
||||
PublicKey pubKey = cert.getPublicKey();
|
||||
ByteString keyId = computePublicKeyID(pubKey);
|
||||
loadedCertificates.put(keyId, pubKey);
|
||||
ByteString keyId = computeCertificateFingerprint(cert);
|
||||
loadedCertificates.put(keyId, cert);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,17 +109,21 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatu
|
|||
}
|
||||
|
||||
|
||||
public void updateSigner(List<Message> msgs) throws SignatureException {
|
||||
if (msgs == null)
|
||||
return;
|
||||
boolean first = true;
|
||||
for (Message msg : msgs) {
|
||||
if (!first) {
|
||||
signer.update(Digest.CONCAT_MARKER);
|
||||
}
|
||||
first = false;
|
||||
signer.update(msg.toByteString().asReadOnlyByteBuffer());
|
||||
}
|
||||
/**
|
||||
* Add the list of messages to the stream that is being verified/signed.
|
||||
* Messages are separated with {@link Digest#CONCAT_MARKER}
|
||||
*
|
||||
* @param msg
|
||||
* @throws SignatureException
|
||||
*/
|
||||
@Override
|
||||
public void updateContent(Message msg) throws SignatureException {
|
||||
assert msg != null;
|
||||
int len = msg.getSerializedSize();
|
||||
|
||||
byte[] lenBytes = { (byte) ((len >>> 24) & 0xff), (byte) ((len >>> 16) & 0xff), (byte) ((len >>> 8) & 0xff), (byte) (len & 0xff) };
|
||||
signer.update(lenBytes);
|
||||
signer.update(msg.toByteString().asReadOnlyByteBuffer());
|
||||
}
|
||||
|
||||
public void updateSigner(InputStream in) throws IOException, SignatureException {
|
||||
|
@ -115,6 +131,7 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatu
|
|||
signer.update(inStr.asReadOnlyByteBuffer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Signature sign() throws SignatureException {
|
||||
Signature.Builder sig = Signature.newBuilder();
|
||||
sig.setType(Crypto.SignatureType.ECDSA);
|
||||
|
@ -123,105 +140,121 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatu
|
|||
return sig.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initVerify(Signature sig)
|
||||
throws CertificateException, InvalidKeyException {
|
||||
PublicKey pubKey = loadedCertificates.get(sig.getSignerId());
|
||||
if (pubKey == null) {
|
||||
logger.warn("No public key loaded for ID {}!", sig.getSignerId());
|
||||
throw new CertificateException("No public key loaded for " + sig.getSignerId());
|
||||
}
|
||||
signer.initVerify(pubKey);
|
||||
}
|
||||
|
||||
public boolean verify(Signature sig) {
|
||||
try {
|
||||
return signer.verify(sig.getData().toByteArray());
|
||||
} catch (SignatureException e) {
|
||||
// Should never happen!
|
||||
logger.error("Signature exception: {}", e);
|
||||
return false;
|
||||
throws CertificateException, InvalidKeyException {
|
||||
Certificate cert = loadedCertificates.get(sig.getSignerId());
|
||||
if (cert == null) {
|
||||
logger.warn("No certificate loaded for ID {}!", sig.getSignerId());
|
||||
throw new CertificateException("No certificate loaded for " + sig.getSignerId());
|
||||
}
|
||||
signer.initVerify(cert.getPublicKey());
|
||||
loadedSignature = sig.getData();
|
||||
loadedSigningKeyId = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(Signature sig, List<Message> msgs)
|
||||
throws CertificateException {
|
||||
public boolean verify() {
|
||||
try {
|
||||
initVerify(sig);
|
||||
updateSigner(msgs);
|
||||
return verify(sig);
|
||||
} catch (InvalidKeyException e) {
|
||||
logger.error("Invalid key exception: {}", e);
|
||||
return false;
|
||||
return signer.verify(loadedSignature.toByteArray());
|
||||
} catch (SignatureException e) {
|
||||
// Should never happen!
|
||||
logger.error("Signature exception: {}", e);
|
||||
// Happens only if signature is invalid!
|
||||
logger.error("Signature exception", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to more easily deal with simple password-protected files.
|
||||
*
|
||||
* @param password
|
||||
* @return
|
||||
*/
|
||||
public CallbackHandler getFixedPasswordHandler(final char[] password) {
|
||||
return new CallbackHandler() {
|
||||
@Override
|
||||
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
|
||||
for (Callback callback : callbacks) {
|
||||
if (callback instanceof PasswordCallback) {
|
||||
PasswordCallback passwordCallback = (PasswordCallback) callback;
|
||||
logger.debug("Requested password ({})", passwordCallback.getPrompt());
|
||||
passwordCallback.setPassword(password);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a keystore from an input stream in PKCS12 format.
|
||||
*
|
||||
* @param keyStream
|
||||
* @param password
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws CertificateException
|
||||
* @throws KeyStoreException
|
||||
* @throws NoSuchAlgorithmException
|
||||
*/
|
||||
public KeyStore.Builder getPKCS12KeyStoreBuilder(InputStream keyStream, char[] password)
|
||||
throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
|
||||
KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);
|
||||
keyStore.load(keyStream, password);
|
||||
return KeyStore.Builder.newInstance(keyStore, new KeyStore.CallbackHandlerProtection(getFixedPasswordHandler(password)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* For now we only support PKCS12.
|
||||
* TODO: Support for PKCS11 as well.
|
||||
*
|
||||
* @param keyStoreBuilder
|
||||
* @throws IOException
|
||||
* @throws CertificateException
|
||||
* @throws UnrecoverableKeyException
|
||||
*/
|
||||
@Override
|
||||
public void loadSigningCertificate(InputStream keyStream, char[] password)
|
||||
public void loadSigningCertificate(KeyStore.Builder keyStoreBuilder)
|
||||
throws IOException, CertificateException, UnrecoverableKeyException {
|
||||
try {
|
||||
KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);
|
||||
|
||||
keyStore.load(keyStream, password);
|
||||
|
||||
KeyStore.ProtectionParameter protParam =
|
||||
new KeyStore.PasswordProtection(password);
|
||||
KeyStore keyStore = keyStoreBuilder.getKeyStore();
|
||||
|
||||
// Iterate through all aliases until we find the first privatekey
|
||||
Enumeration<String> aliases = keyStore.aliases();
|
||||
while (aliases.hasMoreElements()) {
|
||||
String alias = aliases.nextElement();
|
||||
try {
|
||||
KeyStore.Entry entry = keyStore.getEntry(alias, protParam);
|
||||
if (entry instanceof KeyStore.PrivateKeyEntry) {
|
||||
KeyStore.PrivateKeyEntry privEntry = (KeyStore.PrivateKeyEntry) entry;
|
||||
PrivateKey privKey = privEntry.getPrivateKey();
|
||||
PublicKey pubKey = privEntry.getCertificate().getPublicKey();
|
||||
loadedSigningKeyId = computePublicKeyID(pubKey);
|
||||
signer.initSign(privKey);
|
||||
return;
|
||||
}
|
||||
} catch (InvalidKeyException e) {
|
||||
logger.info("Read invalid key: {}", e);
|
||||
} catch (UnrecoverableEntryException e) {
|
||||
logger.info("Read unrecoverable entry: {}", e);
|
||||
}
|
||||
logger.trace("Testing keystore entry {}", alias);
|
||||
|
||||
try {
|
||||
Certificate cert = keyStore.getCertificate(alias);
|
||||
logger.trace("keystore entry {}, has cert type {}", alias, cert.getClass());
|
||||
Key key = keyStore.getKey(alias, null);
|
||||
logger.trace("keystore entry {}, has key type {}", alias, key.getClass());
|
||||
if (key instanceof PrivateKey) {
|
||||
loadedSigningKeyId = computeCertificateFingerprint(cert);
|
||||
signer.initSign((PrivateKey) key);
|
||||
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());
|
||||
}
|
||||
} catch(InvalidKeyException e) {
|
||||
logger.info("Read invalid key", e);
|
||||
} catch(UnrecoverableEntryException e) {
|
||||
logger.info("Read unrecoverable entry", e);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (KeyStoreException e) {
|
||||
logger.error("Keystore exception: {}", e);
|
||||
logger.error("Keystore exception", e);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
logger.error("NoSuchAlgorithmException exception: {}", e);
|
||||
logger.error("NoSuchAlgorithmException exception", e);
|
||||
throw new CertificateException(e);
|
||||
}
|
||||
throw new UnrecoverableKeyException("Didn't find valid private key entry in stream!");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Signature sign(List<Message> msgs) {
|
||||
try {
|
||||
updateSigner(msgs);
|
||||
return sign();
|
||||
} catch (SignatureException e) {
|
||||
logger.error("Signature exception: {}", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Signature sign(InputStream in) throws IOException {
|
||||
try {
|
||||
updateSigner(in);
|
||||
return sign();
|
||||
} catch (SignatureException e) {
|
||||
logger.error("Signature exception: {}", e);
|
||||
return null;
|
||||
}
|
||||
logger.error("Didn't find valid private key entry in keystore");
|
||||
throw new UnrecoverableKeyException("Didn't find valid private key entry in keystore!");
|
||||
}
|
||||
|
||||
public void clearSigningKey() {
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package meerkat.util;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
/**
|
||||
* Convert to/from Hex
|
||||
*/
|
||||
public class Hex {
|
||||
/**
|
||||
* Encode a {@link ByteString} as a hex string.
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static String encode(ByteString str) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
for (byte b : str) {
|
||||
s.append(Integer.toHexString(((int) b) & 0xff));
|
||||
}
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
public static String encode(byte[] bytes) {
|
||||
return encode(ByteString.copyFrom(bytes));
|
||||
}
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@ def logOps = System.getProperty("log.ops") != null
|
|||
appender("CONSOLE", ConsoleAppender) {
|
||||
|
||||
filter(ThresholdFilter) {
|
||||
level = toLevel(System.getProperty("log.level"), DEBUG)
|
||||
level = toLevel(System.getProperty("log.level"), TRACE)
|
||||
}
|
||||
|
||||
encoder(PatternLayoutEncoder) {
|
||||
|
|
|
@ -2,10 +2,16 @@ package meerkat.crypto.concrete;
|
|||
|
||||
import com.google.protobuf.ByteString;
|
||||
import meerkat.protobuf.Crypto;
|
||||
import meerkat.protobuf.Voting;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
|
@ -21,15 +27,18 @@ public class TestECDSASignature {
|
|||
public static String MSG_PLAINTEXT_EXAMPLE = "/certs/signed-messages/helloworld.txt";
|
||||
public static String MSG_SIG_EXAMPLE = "/certs/signed-messages/helloworld.txt.sha256sig";
|
||||
|
||||
public static String HELLO_WORLD = "hello world!";
|
||||
|
||||
|
||||
@Test
|
||||
public void testLoadSignatureKey() throws Exception {
|
||||
public void loadSignatureKey() throws Exception {
|
||||
InputStream keyStream = getClass().getResourceAsStream(KEYFILE_EXAMPLE);
|
||||
char[] password = KEYFILE_PASSWORD.toCharArray();
|
||||
|
||||
ECDSASignature sig = new ECDSASignature();
|
||||
|
||||
sig.loadSigningCertificate(keyStream, password);
|
||||
KeyStore.Builder keyStore = sig.getPKCS12KeyStoreBuilder(keyStream, password);
|
||||
sig.loadSigningCertificate(keyStore);
|
||||
keyStream.close();
|
||||
}
|
||||
|
||||
|
@ -73,7 +82,91 @@ public class TestECDSASignature {
|
|||
Crypto.Signature builtSig = sig.build();
|
||||
signer.initVerify(builtSig);
|
||||
signer.updateSigner(msgStream);
|
||||
assertTrue("Signature did not verify!", signer.verify(builtSig));
|
||||
assertTrue("Signature did not verify!", signer.verify());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyInvalidSig() throws Exception {
|
||||
InputStream certStream = getClass().getResourceAsStream(CERT1_PEM_EXAMPLE);
|
||||
InputStream msgStream = getClass().getResourceAsStream(MSG_PLAINTEXT_EXAMPLE);
|
||||
InputStream sigStream = getClass().getResourceAsStream(MSG_SIG_EXAMPLE);
|
||||
|
||||
ECDSASignature signer = new ECDSASignature();
|
||||
|
||||
signer.loadVerificationCertificates(certStream);
|
||||
certStream.close();
|
||||
|
||||
Crypto.Signature.Builder sig = Crypto.Signature.newBuilder();
|
||||
sig.setType(Crypto.SignatureType.ECDSA);
|
||||
sig.setSignerId(signer.loadedCertificates.entrySet().iterator().next().getKey());
|
||||
byte[] sigData = ByteString.readFrom(sigStream).toByteArray();
|
||||
++sigData[0];
|
||||
|
||||
sig.setData(ByteString.copyFrom(sigData));
|
||||
|
||||
|
||||
Crypto.Signature builtSig = sig.build();
|
||||
signer.initVerify(builtSig);
|
||||
signer.updateSigner(msgStream);
|
||||
assertFalse("Bad Signature passed verification!", signer.verify());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void verifyInvalidMsg() throws Exception {
|
||||
InputStream certStream = getClass().getResourceAsStream(CERT1_PEM_EXAMPLE);
|
||||
InputStream msgStream = getClass().getResourceAsStream(MSG_PLAINTEXT_EXAMPLE);
|
||||
InputStream sigStream = getClass().getResourceAsStream(MSG_SIG_EXAMPLE);
|
||||
|
||||
ECDSASignature signer = new ECDSASignature();
|
||||
|
||||
signer.loadVerificationCertificates(certStream);
|
||||
certStream.close();
|
||||
|
||||
Crypto.Signature.Builder sig = Crypto.Signature.newBuilder();
|
||||
sig.setType(Crypto.SignatureType.ECDSA);
|
||||
sig.setSignerId(signer.loadedCertificates.entrySet().iterator().next().getKey());
|
||||
sig.setData(ByteString.readFrom(sigStream));
|
||||
byte[] msgData = ByteString.readFrom(msgStream).toByteArray();
|
||||
++msgData[0];
|
||||
|
||||
Crypto.Signature builtSig = sig.build();
|
||||
signer.initVerify(builtSig);
|
||||
signer.updateSigner(msgStream);
|
||||
assertFalse("Signature doesn't match message but passed verification!", signer.verify());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void signAndVerify() 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);
|
||||
|
||||
|
||||
Voting.UnsignedBulletinBoardMessage.Builder unsignedMsgBuilder = Voting.UnsignedBulletinBoardMessage.newBuilder();
|
||||
unsignedMsgBuilder.setData(ByteString.copyFromUtf8(HELLO_WORLD));
|
||||
unsignedMsgBuilder.addTags("Tag1");
|
||||
unsignedMsgBuilder.addTags("Tag2");
|
||||
unsignedMsgBuilder.addTags("Tag3");
|
||||
|
||||
Voting.UnsignedBulletinBoardMessage usMsg = unsignedMsgBuilder.build();
|
||||
|
||||
signer.updateContent(usMsg);
|
||||
Crypto.Signature sig = signer.sign();
|
||||
|
||||
signer.loadVerificationCertificates(getClass().getResourceAsStream(CERT1_PEM_EXAMPLE));
|
||||
|
||||
signer.initVerify(sig);
|
||||
signer.updateContent(usMsg);
|
||||
assertTrue("Couldn't verify signature on ", signer.verify());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue