diff --git a/build.gradle-template b/build.gradle-template index 8af1b54..14cb9dd 100644 --- a/build.gradle-template +++ b/build.gradle-template @@ -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 { diff --git a/meerkat-common/build.gradle b/meerkat-common/build.gradle index b744fcd..236bd82 100644 --- a/meerkat-common/build.gradle +++ b/meerkat-common/build.gradle @@ -57,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 { @@ -91,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." diff --git a/meerkat-common/src/main/java/meerkat/crypto/Digest.java b/meerkat-common/src/main/java/meerkat/crypto/Digest.java index a715b26..06b012c 100644 --- a/meerkat-common/src/main/java/meerkat/crypto/Digest.java +++ b/meerkat-common/src/main/java/meerkat/crypto/Digest.java @@ -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 */ diff --git a/meerkat-common/src/main/java/meerkat/crypto/DigitalSignature.java b/meerkat-common/src/main/java/meerkat/crypto/DigitalSignature.java index eda41a2..1abad8f 100644 --- a/meerkat-common/src/main/java/meerkat/crypto/DigitalSignature.java +++ b/meerkat-common/src/main/java/meerkat/crypto/DigitalSignature.java @@ -41,8 +41,8 @@ public interface DigitalSignature { /** - * 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. + * 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 diff --git a/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSASignature.java b/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSASignature.java index 4fb8de5..1a5683f 100644 --- a/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSASignature.java +++ b/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSASignature.java @@ -2,13 +2,13 @@ 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.crypto.Digest; import meerkat.protobuf.Crypto; import meerkat.util.Hex; import org.slf4j.Logger; @@ -27,7 +27,7 @@ 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 { @@ -38,6 +38,12 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatur SHA256Digest digest = new SHA256Digest(); + /** + * Buffer used to hold length in for hash update + */ + ByteBuffer lenBuf = ByteBuffer.allocate(4); + + Map loadedCertificates = new HashMap<>(); /** @@ -111,7 +117,7 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatur /** * Add the list of messages to the stream that is being verified/signed. - * Messages are separated with {@link Digest#CONCAT_MARKER} + * Messages are prepended with their length in 32-bit big-endian format. * * @param msg * @throws SignatureException @@ -119,10 +125,11 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatur @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); + lenBuf.clear(); + lenBuf.putInt(msg.getSerializedSize()); + lenBuf.flip(); + signer.update(lenBuf); signer.update(msg.toByteString().asReadOnlyByteBuffer()); } @@ -230,7 +237,34 @@ public class ECDSASignature extends GlobalCryptoSetup implements DigitalSignatur try { Certificate cert = keyStore.getCertificate(alias); logger.trace("keystore entry {}, has cert type {}", alias, cert.getClass()); - Key key = keyStore.getKey(alias, null); + 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); diff --git a/meerkat-common/src/main/java/meerkat/crypto/concrete/SHA256Digest.java b/meerkat-common/src/main/java/meerkat/crypto/concrete/SHA256Digest.java index dc1d2ba..d18a4d1 100644 --- a/meerkat-common/src/main/java/meerkat/crypto/concrete/SHA256Digest.java +++ b/meerkat-common/src/main/java/meerkat/crypto/concrete/SHA256Digest.java @@ -6,6 +6,7 @@ import meerkat.crypto.Digest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.nio.ByteBuffer; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -18,6 +19,11 @@ public class SHA256Digest extends GlobalCryptoSetup implements Digest { MessageDigest hash; + /** + * Used to convert length to bytes in proper order. + */ + ByteBuffer lenBuf = ByteBuffer.allocate(4); + /** * Instantiate with a specified algorithm. * @param algorithm @@ -56,6 +62,11 @@ public class SHA256Digest extends GlobalCryptoSetup implements Digest { @Override public void update(Message msg) { + + lenBuf.clear(); + lenBuf.putInt(msg.getSerializedSize()); + lenBuf.flip(); + hash.update(lenBuf); hash.update(msg.toByteString().asReadOnlyByteBuffer()); } diff --git a/meerkat-common/src/test/resources/certs/.gitattributes b/meerkat-common/src/test/resources/certs/.gitattributes new file mode 100644 index 0000000..f8fcd2f --- /dev/null +++ b/meerkat-common/src/test/resources/certs/.gitattributes @@ -0,0 +1 @@ +signed-messages/* -text \ No newline at end of file