-
Notifications
You must be signed in to change notification settings - Fork 6
Cryptography
We have seen how ASAP helps to disseminate messages of your application. Now, we are going to discuss how those messages can be encrypted and signed. Encryption avoids others than the expected receiver to read the message. Signing proofs senders’ identity.
There are two kind of communication in distributed systems like ASAP. Two ASAP peers exchange messages during an encounter. (That can be different by using multicast protocols for an encounter. In that case, more than two peers would meet. We leave this discussion with a two peer encounter. The general idea is the same.)
Those two peers are not necessarily creator or receiver of a message. Your application on another (third) peer might have created that message which is meant to reach an additional (forth) peer or even a number of peers. The first two peers do the routing (for which they deserve thankfulness.)
We call the connection between the first two peers a point-to-point connection. (We avoid the abbreviation P2P to avoid confusion with Peer-to-Peer). This message exchange can be encrypted and signed to prevent eavesdropping, ensures peers’ identity and prevents message change. You do not have to anything but switch it on, see chapter settings.
Message exchange between our third and forth peer is called end-to-end (E2E) communication. A sender creates a message and can sign it. Any receiver can verify (or not) senders identity. A sender can also encrypt the whole message. In that case, no peer but the receiver would be able to read this message. You have to do this. This chapter shows how.
There are two classes that help: ASAPCryptoAlgorithms provides cryptographic algorithms. ASAPKeyStore provide the necessary keys. We discuss usage first. Setting up an ASAPKeyStore is discussed later.
Your application messages will most probably have several parts, see chapter about message serialization. A signatur is just an additional part of your message.
byte[] yourMessage = ... // already created with all parts
ASAPKeyStore asapKeyStore = ... // we see later how to get this reference.
// sign it
byte[] signature = ASAPCryptoAlgorithms.sign(content, asapKeyStore);
// compose a new message with your content and the signature
baos = new ByteArrayOutputStream();
// add your message content
ASAPSerialization.writeByteArray(content, baos);
// add signature
ASAPSerialization.writeByteArray(signature, baos);
// attach signature to message
byte[] signedContent = baos.toByteArray();
// send signedContent as ASAP message..
There is nothing really new in this code, except the signing: ASAPCryptoAlgorithms.sign(content, asapKeyStore)
. It is as simple as that. sign
is a static method. It requires the content which is to be signed and a reference to our key store. It produces a signature. Signature and your content are combined and voilà - you can send a signed message into your ASAP based P2P network.
Peer receive messages. Signed message can be verified. The following code illustrates how:
byte[] signedContent = .. // took content it out from received ASAP message
// split message and signature
ByteArrayInputStream bais = new ByteArrayInputStream(tmpMessage);
byte[] yourMessage = ASAPSerialization.readByteArray(bais);
byte[] signature = ASAPSerialization.readByteArray(bais);
// sender - a peerID
String senderID = ..; // discussion - see text below
// verify
boolean verified = ASAPCryptoAlgorithms.verify(
signedContent, signature, senderID, asapKeyStore);
The first lines are no surprise: We split out message again into your actual message and its signature. We are on receivers’ side now. There is a message from another peer and we plan to verify that signature. We need to now who the sender was. We need its id.
Where to get it? It depends on your application. In most case, you message would contain a sender ID. In that case, you would simply take it out of your message. You could also put sender ID into each ASAP message uri. The uri is part of each ASAP message and is e.g. known in your message received listener. It is your job but can easily be solved.
The actual verification is done in the last line. This call can produce a false
in two cases:
- Receiver has not got senders' public key (yet). There is no chance to verify the signature without it.
- This message was not signed by the sender.
We can rule out the first option:
try {
asapKeyStore.getPublicKey(senderID);
// we have got the key.
}
catch(ASAPSecurityException e) {
// there is no key - we cannot verify
}
Receivers' key store can provide senders' public key or not. With a public key, a failed verification would proof a tempered message. Sending peer was not what it claimed to be. You cannot decide without a public key.
The situation is clear if verify
produces true
. Senders' identity is verified.
Encryption is an algorithm that takes an array of bytes (e.g. your message) and a key. It produces an array of bytes. That product does not give any hint of the original message. Decryption is another algorithm takes that array of bytes and a key. It produces the original message.
The key is interesting. In this context, we can distinguish two general encryption classes: symmetric and asymmetric. The first needs only one key, the later requires two. Both have their pros and cons.
Symmetric encryption is a lot faster and easier to program. We have to find a way to transmit the key from sender to receiver in a secure way. Asymmetric encryption requires two key: One (receivers’ public key) is taken for encryption. Receiver takes its private key to decrypt the message. We need some kind of public key infrastructure (PKI) to disseminate public key. Good new: We have got one in the ASAP universe: See our SharkPKI component.
It proofed useful to combine both technologies: We produce a symmetric key for message encryption. This symmetric key is encrypted by receivers’ public key. We call this an encrypted message package. It can easily be produced.
byte[] yourMessage = ... // produced from your application
CharSequence receiverID = ... // message is encrypted for a receiver
// produce encrypted package
byte[] yourEncryptedMessage =
ASAPCryptoAlgorithms.produceEncryptedMessagePackage(message, receiverID, asapKeyStore);
// send signedContent as ASAP message..
Any details are hidden from you. You can check the code if you like. It is free and open.
A receiver can (try to) decrypt such a package.
byte[] receivedMessage = ... // got this from another peer
// produce a package object from received message
ASAPCryptoAlgorithms.EncryptedMessagePackage
encryptedMessagePackage = ASAPCryptoAlgorithms.parseEncryptedMessagePackage(receivedMessage);
// decrypt it
byte[] yourMessage =
ASAPCryptoAlgorithms.decryptPackage(encryptedMessagePackage, asapKeyStore);
Decryption fails if this peer is not receiver of the package. It can also fail if sender did not used to public key associated with receivers’ private key. We discuss those topic in our PKI project.
A package can tell its expected receiver, though.
CharSequence receiver = encryptedMessagePackage.getReceiver();
Messages can be signed an encrypted. We strongly suggest to sign first and encrypt this signed message afterwards. Receivers would decrypt first and verify senders’ identity in a second step.
It is the same code. An running example can be found in our InMemoSharkMessage class.
Basis of all described algorithms are a key store. It provides public and private key. It is somewhat useless without a system that exchanges public keys. That is what we call a public key infrastructure. There is only one good advice: Use our SharkPKI component if you plan do use security algorithms. Get familiar with our SharkComponent concept and our SharkPKI project.
Nevertheless, you can test your application without including a PKI. Examples can be found in our crypto test package, see CryptoUsage