Merge remote-tracking branch 'origin/signature-implementation'
commit
2ca706492a
|
@ -1,6 +1,10 @@
|
|||
.gradle
|
||||
.idea
|
||||
build
|
||||
bin
|
||||
.settings
|
||||
.classpath
|
||||
.project
|
||||
out
|
||||
*.iml
|
||||
*.ipr
|
||||
|
|
|
@ -57,6 +57,15 @@ dependencies {
|
|||
|
||||
/*==== You probably don't have to edit below this line =======*/
|
||||
|
||||
// The run task added by the application plugin
|
||||
// is also of type JavaExec.
|
||||
tasks.withType(JavaExec) {
|
||||
// Assign all Java system properties from
|
||||
// the command line to the JavaExec task.
|
||||
systemProperties System.properties
|
||||
}
|
||||
|
||||
|
||||
protobuf {
|
||||
// Configure the protoc executable
|
||||
protoc {
|
||||
|
|
|
@ -45,6 +45,10 @@ dependencies {
|
|||
// Google protobufs
|
||||
compile 'com.google.protobuf:protobuf-java:3.+'
|
||||
|
||||
// Crypto
|
||||
compile 'org.bouncycastle:bcprov-jdk15on:1.53'
|
||||
compile 'org.bouncycastle:bcpkix-jdk15on:1.53'
|
||||
|
||||
testCompile 'junit:junit:4.+'
|
||||
|
||||
runtime 'org.codehaus.groovy:groovy:2.4.+'
|
||||
|
@ -53,6 +57,16 @@ dependencies {
|
|||
|
||||
/*==== You probably don't have to edit below this line =======*/
|
||||
|
||||
|
||||
// The run task added by the application plugin
|
||||
// is also of type JavaExec.
|
||||
tasks.withType(JavaExec) {
|
||||
// Assign all Java system properties from
|
||||
// the command line to the JavaExec task.
|
||||
systemProperties System.properties
|
||||
}
|
||||
|
||||
|
||||
protobuf {
|
||||
// Configure the protoc executable
|
||||
protoc {
|
||||
|
@ -87,33 +101,6 @@ idea {
|
|||
* "Fat" Build targets
|
||||
*===================================*/
|
||||
|
||||
task fatjar(type: Jar) {
|
||||
description = "Generate a single jar containing everything. Use -Pfatmain=... to override main class"
|
||||
|
||||
destinationDir = buildDir
|
||||
|
||||
def fatMain = hasProperty('fatmain') ? fatmain : mainClassName
|
||||
def testJar = hasProperty('test')
|
||||
|
||||
appendix = "${fatMain}-fat"
|
||||
|
||||
if (testJar) {
|
||||
from sourceSets.test.output
|
||||
from configurations.testRuntime.collect { it.isDirectory() ? it : zipTree(it) }
|
||||
}
|
||||
|
||||
from sourceSets.main.output // that's it
|
||||
from { configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) } }
|
||||
|
||||
exclude 'META-INF/MANIFEST.MF'
|
||||
exclude 'META-INF/*.SF'
|
||||
exclude 'META-INF/*.DSA'
|
||||
exclude 'META-INF/*.RSA'
|
||||
|
||||
|
||||
manifest { attributes 'Main-Class': fatMain }
|
||||
}
|
||||
|
||||
|
||||
task mavenCapsule(type: MavenCapsule){
|
||||
description = "Generate a capsule jar that automatically downloads and caches dependencies when run."
|
||||
|
|
|
@ -8,13 +8,6 @@ import java.security.MessageDigest;
|
|||
* Created by talm on 11/9/15.
|
||||
*/
|
||||
public interface Digest {
|
||||
|
||||
/**
|
||||
* Marker between messages
|
||||
*/
|
||||
public static final byte[] CONCAT_MARKER = {(byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
|
||||
(byte) 0xba, (byte) 0x1d, (byte) 0xfa, (byte) 0xce};
|
||||
|
||||
/**
|
||||
* Completes the hash computation by performing final operations such as padding.
|
||||
* (copied from {@link MessageDigest#digest()})
|
||||
|
@ -25,9 +18,7 @@ public interface Digest {
|
|||
/**
|
||||
* Updates the digest using the specified message (in serialized wire form)
|
||||
*
|
||||
* Includes a special message concatenation marker (the 64 bit message {@link #CONCAT_MARKER}) in the digest (digesting a single message
|
||||
* will give a different result than the same message split into two messages).
|
||||
* Messages must not contain the {@link #CONCAT_MARKER}) marker.
|
||||
* Each message is (automatically) prepended with its length as a 32-bit big-endian unsigned integer.
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
|
|
|
@ -2,16 +2,92 @@ 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;
|
||||
import java.util.List;
|
||||
import static meerkat.protobuf.Crypto.*;
|
||||
|
||||
/**
|
||||
* Created by talm on 25/10/15.
|
||||
*
|
||||
* Sign arrays of messages
|
||||
* Sign and verifyarrays of messages
|
||||
*/
|
||||
public interface DigitalSignature { // Extends SCAPI DigitalSignature
|
||||
public Signature sign(List<Message> msg);
|
||||
public interface DigitalSignature {
|
||||
final public static String CERTIFICATE_ENCODING_X509 = "X.509";
|
||||
|
||||
/**
|
||||
* Load a set of certificates from an input stream.
|
||||
* 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()}.
|
||||
* @param certStream source from which certificates are loaded
|
||||
* @throws CertificateException on parsing errors
|
||||
*/
|
||||
public void loadVerificationCertificates(InputStream certStream)
|
||||
throws CertificateException;
|
||||
|
||||
/**
|
||||
* Clear the loaded verification certificates.
|
||||
*/
|
||||
public void clearVerificationCertificates();
|
||||
|
||||
|
||||
/**
|
||||
* Add msg to the content stream to be verified / signed. Each message is (automatically)
|
||||
* prepended with its length as a 32-bit big-endian unsigned integer.
|
||||
*
|
||||
* @param msg
|
||||
* @throws SignatureException
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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 keyStoreBuilder A keystore builder that can be used to load a keystore.
|
||||
*/
|
||||
public void loadSigningCertificate(KeyStore.Builder keyStoreBuilder)
|
||||
throws IOException, CertificateException, UnrecoverableKeyException;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Clear the signing key (will require authentication to use again).
|
||||
*/
|
||||
public void clearSigningKey();
|
||||
|
||||
public boolean verify(Signature sig, List<Message> msgs);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,306 @@
|
|||
package meerkat.crypto.concrete;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.*;
|
||||
import java.security.cert.*;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.*;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import meerkat.protobuf.Crypto;
|
||||
import meerkat.util.Hex;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
|
||||
import meerkat.crypto.DigitalSignature;
|
||||
import meerkat.protobuf.Crypto.Signature;
|
||||
|
||||
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.
|
||||
*
|
||||
* This class is not thread-safe (each thread should have its own instance).
|
||||
*/
|
||||
public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignature {
|
||||
final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
final public static String KEYSTORE_TYPE = "PKCS12";
|
||||
final public static String DEFAULT_SIGNATURE_ALGORITHM = "SHA256withECDSA";
|
||||
|
||||
SHA256Digest digest = new SHA256Digest();
|
||||
|
||||
/**
|
||||
* Buffer used to hold length in for hash update
|
||||
*/
|
||||
ByteBuffer lenBuf = ByteBuffer.allocate(4);
|
||||
|
||||
|
||||
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. (used for both signing and verifying)
|
||||
*/
|
||||
java.security.Signature signer;
|
||||
|
||||
|
||||
/**
|
||||
* Compute a fingerprint of a cert as a SHA256 hash.
|
||||
*
|
||||
* @param cert
|
||||
* @return
|
||||
*/
|
||||
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) {
|
||||
this.signer = signer;
|
||||
}
|
||||
|
||||
public ECDSASignature() {
|
||||
try {
|
||||
this.signer = java.security.Signature.getInstance(DEFAULT_SIGNATURE_ALGORITHM);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// Should never happen
|
||||
logger.error("Couldn't find implementation for " + DEFAULT_SIGNATURE_ALGORITHM + " signatures", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadVerificationCertificates(InputStream certStream)
|
||||
throws CertificateException {
|
||||
CertificateFactory certificateFactory = CertificateFactory.getInstance(CERTIFICATE_ENCODING_X509);
|
||||
Collection<? extends Certificate> certs = certificateFactory.generateCertificates(certStream);
|
||||
for (Certificate cert : certs) {
|
||||
// Just checking
|
||||
if (!(cert instanceof X509Certificate)) {
|
||||
logger.error("Certificate must be in X509 format; got {} instead!", cert.getClass().getCanonicalName());
|
||||
continue;
|
||||
}
|
||||
PublicKey pubKey = cert.getPublicKey();
|
||||
ByteString keyId = computeCertificateFingerprint(cert);
|
||||
loadedCertificates.put(keyId, cert);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearVerificationCertificates() {
|
||||
loadedCertificates.clear();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
lenBuf.clear();
|
||||
lenBuf.putInt(msg.getSerializedSize());
|
||||
lenBuf.flip();
|
||||
signer.update(lenBuf);
|
||||
signer.update(msg.toByteString().asReadOnlyByteBuffer());
|
||||
}
|
||||
|
||||
public void updateSigner(InputStream in) throws IOException, SignatureException {
|
||||
ByteString inStr = ByteString.readFrom(in);
|
||||
signer.update(inStr.asReadOnlyByteBuffer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Signature sign() throws SignatureException {
|
||||
Signature.Builder sig = Signature.newBuilder();
|
||||
sig.setType(Crypto.SignatureType.ECDSA);
|
||||
sig.setData(ByteString.copyFrom(signer.sign()));
|
||||
sig.setSignerId(loadedSigningKeyId);
|
||||
return sig.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initVerify(Signature sig)
|
||||
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() {
|
||||
try {
|
||||
return signer.verify(loadedSignature.toByteArray());
|
||||
} catch (SignatureException 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(KeyStore.Builder keyStoreBuilder)
|
||||
throws IOException, CertificateException, UnrecoverableKeyException {
|
||||
try {
|
||||
|
||||
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();
|
||||
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) {
|
||||
// This might be a keystore that doesn't support callback handlers
|
||||
// (e.g., Java 8 PKCS12)
|
||||
// Manually extract password using callback handler
|
||||
char[] password = null;
|
||||
KeyStore.ProtectionParameter prot = keyStoreBuilder.getProtectionParameter(alias);
|
||||
|
||||
if (prot instanceof KeyStore.PasswordProtection) {
|
||||
password = ((KeyStore.PasswordProtection) prot).getPassword();
|
||||
} else if (prot instanceof KeyStore.CallbackHandlerProtection) {
|
||||
PasswordCallback callback = new PasswordCallback("Password for " + alias + "?", false);
|
||||
Callback[] callbacks = { callback };
|
||||
try {
|
||||
((KeyStore.CallbackHandlerProtection) prot).getCallbackHandler().handle(callbacks);
|
||||
password = callback.getPassword();
|
||||
} catch (UnsupportedCallbackException e1) {
|
||||
logger.error("PasswordCallback fallback not supported!", e1);
|
||||
throw new UnrecoverableKeyException("Couldn't use password callback to get key");
|
||||
}
|
||||
} else {
|
||||
logger.error("Unrecognized protection handler for keystore: {}", prot.getClass());
|
||||
throw new UnrecoverableKeyException("Unrecognized protection handler for keystore");
|
||||
}
|
||||
key = keyStore.getKey(alias, password);
|
||||
}
|
||||
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);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
logger.error("NoSuchAlgorithmException exception", e);
|
||||
throw new CertificateException(e);
|
||||
}
|
||||
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() {
|
||||
try {
|
||||
// TODO: Check if this really clears the key from memory
|
||||
if (loadedSigningKeyId != null)
|
||||
signer.initSign(null);
|
||||
loadedSigningKeyId = null;
|
||||
} catch (InvalidKeyException e) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package meerkat.crypto.concrete;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.Security;
|
||||
|
||||
/**
|
||||
* A class that performs required crypto setup
|
||||
*/
|
||||
public class GlobalCryptoSetup {
|
||||
final static Logger logger = LoggerFactory.getLogger(GlobalCryptoSetup.class);
|
||||
|
||||
static boolean loadedBouncyCastle = false;
|
||||
|
||||
public static boolean hasSecp256k1Curve() {
|
||||
// For now we just check if the java version is at least 8
|
||||
String[] version = System.getProperty("java.version").split("\\.");
|
||||
int major = Integer.parseInt(version[0]);
|
||||
int minor = Integer.parseInt(version[1]);
|
||||
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 GlobalCryptoSetup() {
|
||||
doSetup();
|
||||
}
|
||||
}
|
|
@ -1,22 +1,29 @@
|
|||
package meerkat.crypto.concrete;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.Message;
|
||||
import meerkat.crypto.Digest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* Created by talm on 11/9/15.
|
||||
*/
|
||||
public class SHA256Digest implements Digest {
|
||||
public class SHA256Digest extends GlobalCryptoSetup implements Digest {
|
||||
final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
public static final String SHA256 = "SHA-256";
|
||||
|
||||
MessageDigest hash;
|
||||
|
||||
/**
|
||||
* Used to convert length to bytes in proper order.
|
||||
*/
|
||||
ByteBuffer lenBuf = ByteBuffer.allocate(4);
|
||||
|
||||
/**
|
||||
* Instantiate with a specified algorithm.
|
||||
* @param algorithm
|
||||
|
@ -32,7 +39,7 @@ public class SHA256Digest implements Digest {
|
|||
public SHA256Digest() { this(true); }
|
||||
|
||||
|
||||
/**
|
||||
/**SHA
|
||||
* Instantiate with the default (SHA-256) algorithm,
|
||||
* or create an empty class (for cloning)
|
||||
*/
|
||||
|
@ -55,9 +62,22 @@ public class SHA256Digest implements Digest {
|
|||
|
||||
@Override
|
||||
public void update(Message msg) {
|
||||
|
||||
lenBuf.clear();
|
||||
lenBuf.putInt(msg.getSerializedSize());
|
||||
lenBuf.flip();
|
||||
hash.update(lenBuf);
|
||||
hash.update(msg.toByteString().asReadOnlyByteBuffer());
|
||||
}
|
||||
|
||||
final public void update(ByteString msg) {
|
||||
hash.update(msg.asReadOnlyByteBuffer());
|
||||
}
|
||||
|
||||
final public void update(byte[] msg) {
|
||||
hash.update(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
hash.reset();
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -13,14 +13,18 @@ enum SignatureType {
|
|||
message Signature {
|
||||
SignatureType type = 1;
|
||||
|
||||
// Data encoding depends on type; default is x509 BER-encoded
|
||||
// Data encoding depends on type; default is DER-encoded
|
||||
bytes data = 2;
|
||||
|
||||
// ID of the signer (should be the fingerprint of the signature verification key)
|
||||
bytes signer_id = 3;
|
||||
}
|
||||
|
||||
// Public key used to verify signatures
|
||||
message SignatureVerificationKey {
|
||||
SignatureType type = 1;
|
||||
|
||||
// Data encoding depends on type; default is x509 DER-encoded
|
||||
bytes data = 2;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
|
||||
|
||||
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
|
||||
import ch.qos.logback.classic.filter.ThresholdFilter
|
||||
import ch.qos.logback.core.ConsoleAppender
|
||||
import ch.qos.logback.core.util.Duration
|
||||
import static ch.qos.logback.classic.Level.*
|
||||
|
||||
if (System.getProperty("log.debug") != null) {
|
||||
println "Logback configuration debugging enabled"
|
||||
|
||||
statusListener(OnConsoleStatusListener)
|
||||
}
|
||||
|
||||
def LOG_LEVEL = toLevel(System.getProperty("log.level"), INFO)
|
||||
|
||||
def haveBeagle = System.getProperty("log.beagle") != null
|
||||
def logOps = System.getProperty("log.ops") != null
|
||||
|
||||
appender("CONSOLE", ConsoleAppender) {
|
||||
|
||||
filter(ThresholdFilter) {
|
||||
level = toLevel(System.getProperty("log.level"), TRACE)
|
||||
}
|
||||
|
||||
encoder(PatternLayoutEncoder) {
|
||||
pattern = "%d{HH:mm:ss.SSS} [%thread %file:%line] %-5level %logger{0} - %msg%n"
|
||||
}
|
||||
}
|
||||
|
||||
def appenders = [ "CONSOLE" ]
|
||||
|
||||
if (haveBeagle) {
|
||||
appender("SOCKET", SocketAppender) {
|
||||
includeCallerData = true
|
||||
remoteHost = "localhost"
|
||||
port = 4321
|
||||
reconnectionDelay = new Duration(10000)
|
||||
}
|
||||
|
||||
appenders += ["SOCKET"]
|
||||
}
|
||||
|
||||
root(LOG_LEVEL, appenders)
|
||||
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
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;
|
||||
|
||||
/**
|
||||
* Created by talm on 12/11/15.
|
||||
*/
|
||||
public class TestECDSASignature {
|
||||
public static String KEYFILE_EXAMPLE = "/certs/enduser-certs/user1-key-with-password-secret.p12";
|
||||
public static String KEYFILE_PASSWORD = "secret";
|
||||
|
||||
public static String CERT1_PEM_EXAMPLE = "/certs/enduser-certs/user1.crt";
|
||||
public static String CERT2_DER_EXAMPLE = "/certs/enduser-certs/user2.der";
|
||||
|
||||
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 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);
|
||||
keyStream.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadPEMVerificationKey() throws Exception {
|
||||
InputStream certStream = getClass().getResourceAsStream(CERT1_PEM_EXAMPLE);
|
||||
|
||||
ECDSASignature sig = new ECDSASignature();
|
||||
|
||||
sig.loadVerificationCertificates(certStream);
|
||||
certStream.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadDERVerificationKey() throws Exception {
|
||||
InputStream certStream = getClass().getResourceAsStream(CERT2_DER_EXAMPLE);
|
||||
|
||||
ECDSASignature sig = new ECDSASignature();
|
||||
|
||||
sig.loadVerificationCertificates(certStream);
|
||||
certStream.close();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void verifyValidSig() 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));
|
||||
|
||||
Crypto.Signature builtSig = sig.build();
|
||||
signer.initVerify(builtSig);
|
||||
signer.updateSigner(msgStream);
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
Certs and private keys for testing generated using OpenSSL
|
||||
|
||||
.crt and .pem files are in PEM format
|
||||
.der files are in binary DER format
|
||||
|
||||
files that have a name of the form *-with-password-xxxx.pem are encrypted with the password xxxx
|
Binary file not shown.
|
@ -0,0 +1,8 @@
|
|||
-----BEGIN EC PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-256-CBC,243D718A0D80C59590E582A26E87A49C
|
||||
|
||||
RG6ITUTIdbJdWYX57oMn3tTCzHJSTjXAIZLjoVxy/v4UFYjluaFhGonIlbH1q2pP
|
||||
ueu29Q3eT6144ypB8ARUJ1x0kRX1OL9zNHgdF9ulrCf9/nhGyC2nL+tHZ0YPbxoQ
|
||||
+6yCQcRWvjUXLVzPEUnwMuHXJDpaXES8X0R4CISQKIA=
|
||||
-----END EC PRIVATE KEY-----
|
Binary file not shown.
|
@ -0,0 +1,5 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQge8JqCoaLoZq61aQki5Xm
|
||||
GppcfAAkhHDGNQw/wLof5LmhRANCAAQJD1kW6BsNkRY9tslaugpOJOaoKX4uBz4S
|
||||
Q96lPaPWkatNVgQchwNeB/hdjZwNuwE7A7XAwr69HFmhXRhsM005
|
||||
-----END PRIVATE KEY-----
|
|
@ -0,0 +1,4 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAECQ9ZFugbDZEWPbbJWroKTiTmqCl+Lgc+
|
||||
EkPepT2j1pGrTVYEHIcDXgf4XY2cDbsBOwO1wMK+vRxZoV0YbDNNOQ==
|
||||
-----END PUBLIC KEY-----
|
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDFjCCArygAwIBAgICEAAwCgYIKoZIzj0EAwIwgYIxKTAnBgNVBAMMIE1lZXJr
|
||||
YXQgVm90aW5nIEludGVybWVkaWF0ZSBDQSAxMRMwEQYDVQQIDApTb21lLVN0YXRl
|
||||
MQswCQYDVQQGEwJJTDEVMBMGA1UECgwMSURDIEhlcnpsaXlhMRwwGgYDVQQLDBNN
|
||||
ZWVya2F0IFZvdGluZyBUZWFtMB4XDTE1MTExMTE2MTM1NFoXDTI1MTEwODE2MTM1
|
||||
NFowbjEaMBgGA1UEAwwRUG9sbGluZyBTdGF0aW9uIDExEzARBgNVBAgMClNvbWUt
|
||||
U3RhdGUxCzAJBgNVBAYTAklMMRUwEwYDVQQKDAxJREMgSGVyemxpeWExFzAVBgNV
|
||||
BAsMDk1lZXJrYXQgVm90aW5nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAECQ9ZFugb
|
||||
DZEWPbbJWroKTiTmqCl+Lgc+EkPepT2j1pGrTVYEHIcDXgf4XY2cDbsBOwO1wMK+
|
||||
vRxZoV0YbDNNOaOCATYwggEyMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFLamS8o2
|
||||
hFNd0vWy/irEBNWVNwFXMB8GA1UdIwQYMBaAFBeyv0c75eT6PNumHo9TZ2B9vtcp
|
||||
MAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATBEBgNVHR8EPTA7MDmg
|
||||
N6A1hjNodHRwOi8vY3JsLmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRp
|
||||
YXRlMS5jcmwwegYIKwYBBQUHAQEEbjBsMEEGCCsGAQUFBzAChjVodHRwOi8vcGtp
|
||||
LmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRpYXRlLWNhLmNydDAnBggr
|
||||
BgEFBQcwAYYbaHR0cDovL29jc3AuZmFjdGNlbnRlci5vcmcvMAoGCCqGSM49BAMC
|
||||
A0gAMEUCIQD6QbhNNmB3AVVqhmXuiLA7WF6raShw6n0g/VloVGQebQIgEvxYclpO
|
||||
MMynt5wH6X65rtn4Q1EGaDMvNbFweCDsldk=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,9 @@
|
|||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIBOjCB4QIBADCBgTELMAkGA1UEBhMCSUwxEzARBgNVBAgMClNvbWUtU3RhdGUx
|
||||
ETAPBgNVBAcMCEhlcnpsaXlhMRUwEwYDVQQKDAxJREMgSGVyemxpeWExFzAVBgNV
|
||||
BAsMDk1lZXJrYXQgVm90aW5nMRowGAYDVQQDDBFQb2xsaW5nIFN0YXRpb24gMTBW
|
||||
MBAGByqGSM49AgEGBSuBBAAKA0IABAkPWRboGw2RFj22yVq6Ck4k5qgpfi4HPhJD
|
||||
3qU9o9aRq01WBByHA14H+F2NnA27ATsDtcDCvr0cWaFdGGwzTTmgADAKBggqhkjO
|
||||
PQQDAgNIADBFAiEA8gmIhALr7O5M1QLReGH3jheildTIr1mDWl14WyMf9U4CIF23
|
||||
mInyo4VqNHLzxMLg5Cn3Oddokng3OXa63y4nTfv+
|
||||
-----END CERTIFICATE REQUEST-----
|
|
@ -0,0 +1,5 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgYpBEO+XWm/n6VPeMVK76
|
||||
mrZkDTpiwLsDykG7M4fU5RKhRANCAAR71/kVGyA3hdxcLBBT3NPQF6R3LholmLRN
|
||||
qhnvHqzJWuy7ev+Xbuxtt9AN0ajyeFDy8Oe1bUSidnLyQi+nXC0f
|
||||
-----END PRIVATE KEY-----
|
|
@ -0,0 +1,4 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEe9f5FRsgN4XcXCwQU9zT0Bekdy4aJZi0
|
||||
TaoZ7x6syVrsu3r/l27sbbfQDdGo8nhQ8vDntW1EonZy8kIvp1wtHw==
|
||||
-----END PUBLIC KEY-----
|
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDFjCCArygAwIBAgICEAEwCgYIKoZIzj0EAwIwgYIxKTAnBgNVBAMMIE1lZXJr
|
||||
YXQgVm90aW5nIEludGVybWVkaWF0ZSBDQSAxMRMwEQYDVQQIDApTb21lLVN0YXRl
|
||||
MQswCQYDVQQGEwJJTDEVMBMGA1UECgwMSURDIEhlcnpsaXlhMRwwGgYDVQQLDBNN
|
||||
ZWVya2F0IFZvdGluZyBUZWFtMB4XDTE1MTExMTE2MjAzM1oXDTI1MTEwODE2MjAz
|
||||
M1owbjEaMBgGA1UEAwwRUG9sbGluZyBTdGF0aW9uIDIxEzARBgNVBAgMClNvbWUt
|
||||
U3RhdGUxCzAJBgNVBAYTAklMMRUwEwYDVQQKDAxJREMgSGVyemxpeWExFzAVBgNV
|
||||
BAsMDk1lZXJrYXQgVm90aW5nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEe9f5FRsg
|
||||
N4XcXCwQU9zT0Bekdy4aJZi0TaoZ7x6syVrsu3r/l27sbbfQDdGo8nhQ8vDntW1E
|
||||
onZy8kIvp1wtH6OCATYwggEyMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFKCdquYj
|
||||
DGHqAHt+4PIDlw0h2UvuMB8GA1UdIwQYMBaAFBeyv0c75eT6PNumHo9TZ2B9vtcp
|
||||
MAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATBEBgNVHR8EPTA7MDmg
|
||||
N6A1hjNodHRwOi8vY3JsLmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRp
|
||||
YXRlMS5jcmwwegYIKwYBBQUHAQEEbjBsMEEGCCsGAQUFBzAChjVodHRwOi8vcGtp
|
||||
LmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRpYXRlLWNhLmNydDAnBggr
|
||||
BgEFBQcwAYYbaHR0cDovL29jc3AuZmFjdGNlbnRlci5vcmcvMAoGCCqGSM49BAMC
|
||||
A0gAMEUCIQDpo5B0vvEJSax3YzOMfE8l0pfDUIKLdBWJVGeq0VLtIgIgVr0+4/0e
|
||||
n+R+l1OVOLh2GirloOgbv5Ch5BQ2pQNAG2Y=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,9 @@
|
|||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIBOzCB4QIBADCBgTELMAkGA1UEBhMCSUwxEzARBgNVBAgMClNvbWUtU3RhdGUx
|
||||
ETAPBgNVBAcMCEhlcnpsaXlhMRUwEwYDVQQKDAxJREMgSGVyemxpeWExFzAVBgNV
|
||||
BAsMDk1lZXJrYXQgVm90aW5nMRowGAYDVQQDDBFQb2xsaW5nIFN0YXRpb24gMjBW
|
||||
MBAGByqGSM49AgEGBSuBBAAKA0IABHvX+RUbIDeF3FwsEFPc09AXpHcuGiWYtE2q
|
||||
Ge8erMla7Lt6/5du7G230A3RqPJ4UPLw57VtRKJ2cvJCL6dcLR+gADAKBggqhkjO
|
||||
PQQDAgNJADBGAiEA6Ls/ojRaZT+u4YeOBYcPbRcJE3jSTe1Sm/lR7fDyEhMCIQCk
|
||||
UOca+e2b8+CqM3CURBv6TqUMmZ3HeMRvEAxFPqOWSw==
|
||||
-----END CERTIFICATE REQUEST-----
|
Binary file not shown.
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDFjCCArygAwIBAgICEAAwCgYIKoZIzj0EAwIwgYIxKTAnBgNVBAMMIE1lZXJr
|
||||
YXQgVm90aW5nIEludGVybWVkaWF0ZSBDQSAxMRMwEQYDVQQIDApTb21lLVN0YXRl
|
||||
MQswCQYDVQQGEwJJTDEVMBMGA1UECgwMSURDIEhlcnpsaXlhMRwwGgYDVQQLDBNN
|
||||
ZWVya2F0IFZvdGluZyBUZWFtMB4XDTE1MTExMTE2MTM1NFoXDTI1MTEwODE2MTM1
|
||||
NFowbjEaMBgGA1UEAwwRUG9sbGluZyBTdGF0aW9uIDExEzARBgNVBAgMClNvbWUt
|
||||
U3RhdGUxCzAJBgNVBAYTAklMMRUwEwYDVQQKDAxJREMgSGVyemxpeWExFzAVBgNV
|
||||
BAsMDk1lZXJrYXQgVm90aW5nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAECQ9ZFugb
|
||||
DZEWPbbJWroKTiTmqCl+Lgc+EkPepT2j1pGrTVYEHIcDXgf4XY2cDbsBOwO1wMK+
|
||||
vRxZoV0YbDNNOaOCATYwggEyMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFLamS8o2
|
||||
hFNd0vWy/irEBNWVNwFXMB8GA1UdIwQYMBaAFBeyv0c75eT6PNumHo9TZ2B9vtcp
|
||||
MAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATBEBgNVHR8EPTA7MDmg
|
||||
N6A1hjNodHRwOi8vY3JsLmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRp
|
||||
YXRlMS5jcmwwegYIKwYBBQUHAQEEbjBsMEEGCCsGAQUFBzAChjVodHRwOi8vcGtp
|
||||
LmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRpYXRlLWNhLmNydDAnBggr
|
||||
BgEFBQcwAYYbaHR0cDovL29jc3AuZmFjdGNlbnRlci5vcmcvMAoGCCqGSM49BAMC
|
||||
A0gAMEUCIQD6QbhNNmB3AVVqhmXuiLA7WF6raShw6n0g/VloVGQebQIgEvxYclpO
|
||||
MMynt5wH6X65rtn4Q1EGaDMvNbFweCDsldk=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDFjCCArygAwIBAgICEAEwCgYIKoZIzj0EAwIwgYIxKTAnBgNVBAMMIE1lZXJr
|
||||
YXQgVm90aW5nIEludGVybWVkaWF0ZSBDQSAxMRMwEQYDVQQIDApTb21lLVN0YXRl
|
||||
MQswCQYDVQQGEwJJTDEVMBMGA1UECgwMSURDIEhlcnpsaXlhMRwwGgYDVQQLDBNN
|
||||
ZWVya2F0IFZvdGluZyBUZWFtMB4XDTE1MTExMTE2MjAzM1oXDTI1MTEwODE2MjAz
|
||||
M1owbjEaMBgGA1UEAwwRUG9sbGluZyBTdGF0aW9uIDIxEzARBgNVBAgMClNvbWUt
|
||||
U3RhdGUxCzAJBgNVBAYTAklMMRUwEwYDVQQKDAxJREMgSGVyemxpeWExFzAVBgNV
|
||||
BAsMDk1lZXJrYXQgVm90aW5nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEe9f5FRsg
|
||||
N4XcXCwQU9zT0Bekdy4aJZi0TaoZ7x6syVrsu3r/l27sbbfQDdGo8nhQ8vDntW1E
|
||||
onZy8kIvp1wtH6OCATYwggEyMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFKCdquYj
|
||||
DGHqAHt+4PIDlw0h2UvuMB8GA1UdIwQYMBaAFBeyv0c75eT6PNumHo9TZ2B9vtcp
|
||||
MAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATBEBgNVHR8EPTA7MDmg
|
||||
N6A1hjNodHRwOi8vY3JsLmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRp
|
||||
YXRlMS5jcmwwegYIKwYBBQUHAQEEbjBsMEEGCCsGAQUFBzAChjVodHRwOi8vcGtp
|
||||
LmZhY3RjZW50ZXIub3JnL21lZXJrYXQtaW50ZXJtZWRpYXRlLWNhLmNydDAnBggr
|
||||
BgEFBQcwAYYbaHR0cDovL29jc3AuZmFjdGNlbnRlci5vcmcvMAoGCCqGSM49BAMC
|
||||
A0gAMEUCIQDpo5B0vvEJSax3YzOMfE8l0pfDUIKLdBWJVGeq0VLtIgIgVr0+4/0e
|
||||
n+R+l1OVOLh2GirloOgbv5Ch5BQ2pQNAG2Y=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,2 @@
|
|||
V 251108161354Z 1000 unknown /CN=Polling Station 1/ST=Some-State/C=IL/O=IDC Herzliya/OU=Meerkat Voting
|
||||
V 251108162033Z 1001 unknown /CN=Polling Station 2/ST=Some-State/C=IL/O=IDC Herzliya/OU=Meerkat Voting
|
|
@ -0,0 +1 @@
|
|||
unique_subject = no
|
|
@ -0,0 +1 @@
|
|||
unique_subject = no
|
|
@ -0,0 +1 @@
|
|||
V 251108161354Z 1000 unknown /CN=Polling Station 1/ST=Some-State/C=IL/O=IDC Herzliya/OU=Meerkat Voting
|
|
@ -0,0 +1 @@
|
|||
1002
|
|
@ -0,0 +1 @@
|
|||
1001
|
|
@ -0,0 +1 @@
|
|||
1000
|
|
@ -0,0 +1,5 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgJvMhOfcQfdK/42QlBbri
|
||||
IYXLM/gVHq/yppOykDqB3s6hRANCAAQoShAtCGW5c9pk/4/sKN1qjCgDKngqJpba
|
||||
kku6cIDqXDr+aHsl+/KdSHd46OI3fEynl+/Pc85wRsaY6Z7b1PdS
|
||||
-----END PRIVATE KEY-----
|
|
@ -0,0 +1,21 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDfDCCAyGgAwIBAgICEAAwCgYIKoZIzj0EAwIwgbAxCzAJBgNVBAYTAklMMRMw
|
||||
EQYDVQQIDApTb21lLVN0YXRlMREwDwYDVQQHDAhIZXJ6bGl5YTEUMBIGA1UECgwL
|
||||
SURDIEhlcmxpeWExHzAdBgNVBAsMFk1lZXJrYXQgVm90aW5nIFByb2plY3QxGDAW
|
||||
BgNVBAMMD1Rlc3RpbmcgUm9vdCBDQTEoMCYGCSqGSIb3DQEJARYZdGVzdGluZy1j
|
||||
YUBmYWN0Y2VudGVyLm9yZzAeFw0xNTExMTExNjA4MDJaFw0yNTExMDgxNjA4MDJa
|
||||
MIGCMSkwJwYDVQQDDCBNZWVya2F0IFZvdGluZyBJbnRlcm1lZGlhdGUgQ0EgMTET
|
||||
MBEGA1UECAwKU29tZS1TdGF0ZTELMAkGA1UEBhMCSUwxFTATBgNVBAoMDElEQyBI
|
||||
ZXJ6bGl5YTEcMBoGA1UECwwTTWVlcmthdCBWb3RpbmcgVGVhbTBWMBAGByqGSM49
|
||||
AgEGBSuBBAAKA0IABChKEC0IZblz2mT/j+wo3WqMKAMqeComltqSS7pwgOpcOv5o
|
||||
eyX78p1Id3jo4jd8TKeX789zznBGxpjpntvU91KjggFYMIIBVDAPBgNVHRMBAf8E
|
||||
BTADAQH/MB0GA1UdDgQWBBQXsr9HO+Xk+jzbph6PU2dgfb7XKTAfBgNVHSMEGDAW
|
||||
gBSJD9L1fLmX4A9CBoLsYXn3OPy1ojALBgNVHQ8EBAMCAaYwEwYDVR0lBAwwCgYI
|
||||
KwYBBQUHAwEwPgYDVR0fBDcwNTAzoDGgL4YtaHR0cDovL2NybC5mYWN0Y2VudGVy
|
||||
Lm9yZy9tZWVya2F0LXJvb3QtY2EuY3JsMCsGA1UdEQQkMCKCIE1lZXJrYXQgVm90
|
||||
aW5nIEludGVybWVkaWF0ZSBDQSAxMHIGCCsGAQUFBwEBBGYwZDA5BggrBgEFBQcw
|
||||
AoYtaHR0cDovL3BraS5mYWN0Y2VudGVyLm9yZy9tZWVya2F0LXJvb3QtY2EuY3J0
|
||||
MCcGCCsGAQUFBzABhhtodHRwOi8vb2NzcC5mYWN0Y2VudGVyLm9yZy8wCgYIKoZI
|
||||
zj0EAwIDSQAwRgIhALEMHq2ssC9rLXiG8v6NcZetwwxdu3B3LW9s0KeGoNIEAiEA
|
||||
skA56tMnhiZe38msyanRyRrAHyBI2fGs6GP3UBrg2P8=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,10 @@
|
|||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIBTTCB9QIBADCBlTELMAkGA1UEBhMCSUwxEzARBgNVBAgMClNvbWUtU3RhdGUx
|
||||
ETAPBgNVBAcMCEhlcnpsaXlhMRUwEwYDVQQKDAxJREMgSGVyemxpeWExHDAaBgNV
|
||||
BAsME01lZXJrYXQgVm90aW5nIFRlYW0xKTAnBgNVBAMMIE1lZXJrYXQgVm90aW5n
|
||||
IEludGVybWVkaWF0ZSBDQSAxMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEKEoQLQhl
|
||||
uXPaZP+P7CjdaowoAyp4KiaW2pJLunCA6lw6/mh7JfvynUh3eOjiN3xMp5fvz3PO
|
||||
cEbGmOme29T3UqAAMAoGCCqGSM49BAMCA0cAMEQCIFlyJO5NFqnMUu5hOlQa872E
|
||||
yy0V3zkqeN6Aly+LtEQqAiAfHwbi1lkJOZT2tOX8gfJzcac2jKmbgIhmITNq7uma
|
||||
Wg==
|
||||
-----END CERTIFICATE REQUEST-----
|
|
@ -0,0 +1,46 @@
|
|||
[ ca ]
|
||||
default_ca = myca
|
||||
|
||||
[ crl_ext ]
|
||||
issuerAltName=issuer:copy
|
||||
authorityKeyIdentifier=keyid:always
|
||||
|
||||
[ myca ]
|
||||
dir = ./
|
||||
new_certs_dir = $dir
|
||||
unique_subject = no
|
||||
certificate = $dir/intermediate-ca-1.crt
|
||||
database = $dir/certindex
|
||||
private_key = $dir/intermediate-ca-1-private-key.pem
|
||||
serial = $dir/certserial
|
||||
default_days = 3650
|
||||
default_md = sha256
|
||||
policy = myca_policy
|
||||
x509_extensions = myca_extensions
|
||||
crlnumber = $dir/crlnumber
|
||||
default_crl_days = 3650
|
||||
|
||||
[ myca_policy ]
|
||||
commonName = supplied
|
||||
stateOrProvinceName = optional
|
||||
countryName = optional
|
||||
emailAddress = optional
|
||||
organizationName = supplied
|
||||
organizationalUnitName = optional
|
||||
|
||||
[ myca_extensions ]
|
||||
basicConstraints = critical,CA:FALSE
|
||||
keyUsage = critical,any
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always,issuer
|
||||
keyUsage = digitalSignature,keyEncipherment
|
||||
extendedKeyUsage = serverAuth
|
||||
crlDistributionPoints = @crl_section
|
||||
authorityInfoAccess = @ocsp_section
|
||||
|
||||
[crl_section]
|
||||
URI.0 = http://crl.factcenter.org/meerkat-intermediate1.crl
|
||||
|
||||
[ocsp_section]
|
||||
caIssuers;URI.0 = http://pki.factcenter.org/meerkat-intermediate-ca.crt
|
||||
OCSP;URI.0 = http://ocsp.factcenter.org/
|
|
@ -0,0 +1,21 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDfDCCAyGgAwIBAgICEAAwCgYIKoZIzj0EAwIwgbAxCzAJBgNVBAYTAklMMRMw
|
||||
EQYDVQQIDApTb21lLVN0YXRlMREwDwYDVQQHDAhIZXJ6bGl5YTEUMBIGA1UECgwL
|
||||
SURDIEhlcmxpeWExHzAdBgNVBAsMFk1lZXJrYXQgVm90aW5nIFByb2plY3QxGDAW
|
||||
BgNVBAMMD1Rlc3RpbmcgUm9vdCBDQTEoMCYGCSqGSIb3DQEJARYZdGVzdGluZy1j
|
||||
YUBmYWN0Y2VudGVyLm9yZzAeFw0xNTExMTExNjA4MDJaFw0yNTExMDgxNjA4MDJa
|
||||
MIGCMSkwJwYDVQQDDCBNZWVya2F0IFZvdGluZyBJbnRlcm1lZGlhdGUgQ0EgMTET
|
||||
MBEGA1UECAwKU29tZS1TdGF0ZTELMAkGA1UEBhMCSUwxFTATBgNVBAoMDElEQyBI
|
||||
ZXJ6bGl5YTEcMBoGA1UECwwTTWVlcmthdCBWb3RpbmcgVGVhbTBWMBAGByqGSM49
|
||||
AgEGBSuBBAAKA0IABChKEC0IZblz2mT/j+wo3WqMKAMqeComltqSS7pwgOpcOv5o
|
||||
eyX78p1Id3jo4jd8TKeX789zznBGxpjpntvU91KjggFYMIIBVDAPBgNVHRMBAf8E
|
||||
BTADAQH/MB0GA1UdDgQWBBQXsr9HO+Xk+jzbph6PU2dgfb7XKTAfBgNVHSMEGDAW
|
||||
gBSJD9L1fLmX4A9CBoLsYXn3OPy1ojALBgNVHQ8EBAMCAaYwEwYDVR0lBAwwCgYI
|
||||
KwYBBQUHAwEwPgYDVR0fBDcwNTAzoDGgL4YtaHR0cDovL2NybC5mYWN0Y2VudGVy
|
||||
Lm9yZy9tZWVya2F0LXJvb3QtY2EuY3JsMCsGA1UdEQQkMCKCIE1lZXJrYXQgVm90
|
||||
aW5nIEludGVybWVkaWF0ZSBDQSAxMHIGCCsGAQUFBwEBBGYwZDA5BggrBgEFBQcw
|
||||
AoYtaHR0cDovL3BraS5mYWN0Y2VudGVyLm9yZy9tZWVya2F0LXJvb3QtY2EuY3J0
|
||||
MCcGCCsGAQUFBzABhhtodHRwOi8vb2NzcC5mYWN0Y2VudGVyLm9yZy8wCgYIKoZI
|
||||
zj0EAwIDSQAwRgIhALEMHq2ssC9rLXiG8v6NcZetwwxdu3B3LW9s0KeGoNIEAiEA
|
||||
skA56tMnhiZe38msyanRyRrAHyBI2fGs6GP3UBrg2P8=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1 @@
|
|||
V 251108160802Z 1000 unknown /CN=Meerkat Voting Intermediate CA 1/ST=Some-State/C=IL/O=IDC Herzliya/OU=Meerkat Voting Team
|
|
@ -0,0 +1 @@
|
|||
unique_subject = no
|
|
@ -0,0 +1 @@
|
|||
1001
|
|
@ -0,0 +1 @@
|
|||
1000
|
|
@ -0,0 +1 @@
|
|||
1000
|
|
@ -0,0 +1,61 @@
|
|||
[ ca ]
|
||||
default_ca = myca
|
||||
|
||||
[ crl_ext ]
|
||||
issuerAltName=issuer:copy
|
||||
authorityKeyIdentifier=keyid:always
|
||||
|
||||
[ myca ]
|
||||
dir = ./
|
||||
new_certs_dir = $dir
|
||||
unique_subject = no
|
||||
certificate = $dir/root-ca.crt
|
||||
database = $dir/certindex
|
||||
private_key = $dir/root-ca-private-key.pem
|
||||
serial = $dir/certserial
|
||||
default_days = 3650
|
||||
default_md = sha256
|
||||
policy = myca_policy
|
||||
x509_extensions = myca_extensions
|
||||
crlnumber = $dir/crlnumber
|
||||
default_crl_days = 3650
|
||||
|
||||
[ myca_policy ]
|
||||
commonName = supplied
|
||||
stateOrProvinceName = optional
|
||||
countryName = optional
|
||||
emailAddress = optional
|
||||
organizationName = supplied
|
||||
organizationalUnitName = optional
|
||||
|
||||
[ myca_extensions ]
|
||||
basicConstraints = critical,CA:TRUE
|
||||
keyUsage = critical,any
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always,issuer
|
||||
keyUsage = digitalSignature,keyEncipherment,cRLSign,keyCertSign
|
||||
extendedKeyUsage = serverAuth
|
||||
crlDistributionPoints = @crl_section
|
||||
subjectAltName = @alt_names
|
||||
authorityInfoAccess = @ocsp_section
|
||||
|
||||
[ v3_ca ]
|
||||
basicConstraints = critical,CA:TRUE,pathlen:0
|
||||
keyUsage = critical,any
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always,issuer
|
||||
keyUsage = digitalSignature,keyEncipherment,cRLSign,keyCertSign
|
||||
extendedKeyUsage = serverAuth
|
||||
crlDistributionPoints = @crl_section
|
||||
subjectAltName = @alt_names
|
||||
authorityInfoAccess = @ocsp_section
|
||||
|
||||
[alt_names]
|
||||
DNS.0 = Meerkat Voting Intermediate CA 1
|
||||
|
||||
[crl_section]
|
||||
URI.0 = http://crl.factcenter.org/meerkat-root-ca.crl
|
||||
|
||||
[ocsp_section]
|
||||
caIssuers;URI.0 = http://pki.factcenter.org/meerkat-root-ca.crt
|
||||
OCSP;URI.0 = http://ocsp.factcenter.org/
|
|
@ -0,0 +1,8 @@
|
|||
-----BEGIN EC PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-256-CBC,B8CA131346FD6C9568A6C80935F2AF14
|
||||
|
||||
8q1seEln39/tQTo5KqN+qNRhd0fQ0oC71dYpfTHsP0NlNmjMtwKo2niFwzjxnSyP
|
||||
vpJjGzUlnq30ucbeJA7CDm/1cmYAU5gGQ7gldgpi2TQVS+EBjqi/Y5P9AlrgLv6K
|
||||
tKe4AvkqQcpi4ZvlUL9xmNaM9jEH4syopR9YClEMfa8=
|
||||
-----END EC PRIVATE KEY-----
|
Binary file not shown.
|
@ -0,0 +1,5 @@
|
|||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHQCAQEEIEi9y6pSKu1kDZcIfQQAnojl1iFxm32W0DVCp2P6HRrkoAcGBSuBBAAK
|
||||
oUQDQgAEoijIYF12bpA0tcjyQnWZGQ4lzdBGR+hK/5al/M+zFgFwvWHoWf6yJsSB
|
||||
ymviB5yUaH+cE+/3LXlGbpRzYKLBYQ==
|
||||
-----END EC PRIVATE KEY-----
|
|
@ -0,0 +1,17 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIICpTCCAkygAwIBAgIJAJoVb07aGgNaMAoGCCqGSM49BAMCMIGwMQswCQYDVQQG
|
||||
EwJJTDETMBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UEBwwISGVyemxpeWExFDAS
|
||||
BgNVBAoMC0lEQyBIZXJsaXlhMR8wHQYDVQQLDBZNZWVya2F0IFZvdGluZyBQcm9q
|
||||
ZWN0MRgwFgYDVQQDDA9UZXN0aW5nIFJvb3QgQ0ExKDAmBgkqhkiG9w0BCQEWGXRl
|
||||
c3RpbmctY2FAZmFjdGNlbnRlci5vcmcwHhcNMTUxMTExMTUzODE4WhcNMjUxMTA4
|
||||
MTUzODE4WjCBsDELMAkGA1UEBhMCSUwxEzARBgNVBAgMClNvbWUtU3RhdGUxETAP
|
||||
BgNVBAcMCEhlcnpsaXlhMRQwEgYDVQQKDAtJREMgSGVybGl5YTEfMB0GA1UECwwW
|
||||
TWVlcmthdCBWb3RpbmcgUHJvamVjdDEYMBYGA1UEAwwPVGVzdGluZyBSb290IENB
|
||||
MSgwJgYJKoZIhvcNAQkBFhl0ZXN0aW5nLWNhQGZhY3RjZW50ZXIub3JnMFYwEAYH
|
||||
KoZIzj0CAQYFK4EEAAoDQgAEoijIYF12bpA0tcjyQnWZGQ4lzdBGR+hK/5al/M+z
|
||||
FgFwvWHoWf6yJsSBymviB5yUaH+cE+/3LXlGbpRzYKLBYaNQME4wHQYDVR0OBBYE
|
||||
FIkP0vV8uZfgD0IGguxhefc4/LWiMB8GA1UdIwQYMBaAFIkP0vV8uZfgD0IGguxh
|
||||
efc4/LWiMAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwIDRwAwRAIgNftHrW30Git8
|
||||
VFQKyMCkasauSpEHpAGdcRAhRHqUQMUCIDxw++trz/Iv8818xVB1ARr9EQAmH0aC
|
||||
7MHETGuiBC7L
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,3 @@
|
|||
-----BEGIN EC PARAMETERS-----
|
||||
BgUrgQQACg==
|
||||
-----END EC PARAMETERS-----
|
|
@ -0,0 +1 @@
|
|||
|
Binary file not shown.
Loading…
Reference in New Issue