Skip to content

Cryptography

Thomas Schwotzer edited this page Aug 15, 2021 · 37 revisions

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.

Helping classes

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.

Signing / Verifying

Your application messages will most probably chapter SerializedMessages

missing / re-write:

  • access Certificates and Keys
  • how to use Cryptoclasses in this lib
  • see crypto package
  • there are changes in v0.6 and higher that have impact on parts of this documentation.
  • Point-to-point encryption fits better to behaviour management.

[/TODO]

This library helps to encrypt / decrypt and sign and verify your data. It supports both, point-to-point-encryption and end-to-end-encryption. This is not a introduction to cryptography.

Point-to-point security

An ASAP session is initiated when ASAP peers encounter each other. ASAP protocol data units are transmitted during that session from one peer to another. We call it a point-to-point connection. PDUs are not encrypted or signed per default. Maybe it is not even necessary. It depends on the used protocol. If you find you protocol safe enough, you can avoid any additional security settings.

Switching signing and encryption on is simple:

ASAPPeer alicePeer = ... // was created somewhere 
alicePeer.getASAPCommunicationControl().setSendEncryptedMessages(true);
alicePeer.getASAPCommunicationControl().setSendSignedMessages(true);

There are four setting. You can choose to send signed and/or encrypted PDUs. You need access the ASAPCommunicationSetting set encryption and or signing on or off. Both are switched on in our example.

Any PDU issued by alicePeer would now be signed and encrypted. Signing works anytime if the security environment had been setup accordingly. The PDU would be signed with Alice’ private key. This signature is attached to the ASAP message.

Encryption can fail if alicePeer has no public key of the recipient. That is the peer with which Alice has running ASAP session. Nothing would happens in that case. Alice would not send any data.

Peers can define that they only accept signed or encrypted PDUs.

alicePeer.getASAPEnginePermissionSettings().setReceivedMessagesMustBeEncrypted(true);
alicePeer.getASAPEnginePermissionSettings().setReceivedMessagesMustBeSigned(true);

Encryption works anytime if the security environment had been setup accordingly and sender used the valid public key of Alice.

alicePeer would only process PDUs which encrypted and the signature can be verified. Verifying can fail for two reasons: alicePeer has a public key of the encountered peer but cannot verify the signature. In that case, the sender cheats about its identity or alicePeer has a wrong public key. Second reason is a missing public key on alicePeer side. Either way, the PDU is not processed.

End-to-end security

ASAP is a routing protocol. Messages can be send over an arbitrary number of peers (we call those exchanges hops) to its recipient. There are lot of good reasons to ensure end-to-end-security: Sender encrypts data for a dedicated recipient. Peer in between would transfer messages but without being able to read the content.

End-to-end-security is about your application data - the content in your ASAP message. Peers in between cannot read messages. Point-to-point-security is about signing the whole PDU. Each peer can read the message - it works against eavesdropping on the network.

It is also quite often a good idea to sign an application message. If you find it useful keep reading.

BasicCryptoParameters

We defined an interface BasicCryptoParameters. Here is a part of the interface:

public interface BasicCryptoParameters {
    PrivateKey getPrivateKey() throws ASAPSecurityException; // get peers' private key
    PublicKey getPublicKey(CharSequence subjectID) throws ASAPSecurityException; // get public key of a peer
    PublicKey getPublicKey() throws ASAPSecurityException; // get peers' public key
}

You don't need to call those message yourself. You just need a refernce to an object implementing this interface, see Setup security environment.

More important, there is a class ASAPCryptoAlgorithms which offers very convient ways to do all that nice crypto stuff we need. That's what we discuss in the next sub sections.

Signing

You application is made up of peers who exchanges messages. Your message structure is opaque to ASAP. They are just an array of bytes. Signing is simple:

BasicCryptoParameters basicCryptoParameters; // see above
ASAPEngine yourEngine = ... // engine of your app
CharSequence yourUri = ... // optional uri describing your message in your app
byte[] yourMessage = ... // your application message
byte[] signature = ASAPCryptoAlgorithms.sign(message, basicCryptoParameters); // sign your message

// produce a message with attached signature
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// add signer name - make verification easier.
ASAPSerialization.writeCharSequenceParameter("Alice", baos);
// add actual message
ASAPSerialization.writeByteArray(message, baos);
// finally: append signature
ASAPSerialization.writeByteArray(signature, baos);
// attach signature to message
byte[] yourSignedMessage = baos.toByteArray();

// let ASAP transmit your signed message which has three parts: signer, message, signature
yourEngine.add(yourURI, yourSignedMessage);

First view lines should look familiar. An ASAP engine was produced, a uri defined. You already created a message and plan to disseminate it with ASAP. But you are going to sign it first. It is just a single line of code. Call ASAPCryptoAlgorithms.sign.

A signature is produced. Signing requires your private key that is provided by our BasicCryptoParameters object, see later. This signature should be transmitted with your data. We produce now a message that consists of three parts: Signer name, signed message and signature. This library provides class ASAPSerialization which makes producing and parsing ASAP related structures to byte arrays very easy. This class is used to produce ASAP PDUs and can help us here as well.

This signed message can now be transmitted with ASAP: yourEngine.add

Verifying

Your application received messages. It is most probably notified by an ASAPChunkListener. You decided to let your application to sign their messages. Now, you need to verify those signatures. The transmitted message consists of three parts. They have to be split into signer, message and signature. Afterwards, the signature can be verified.

CharSequence sender = .. // 
byte[] receivedASAPMessage = ... // received message
// split message and signature
ByteArrayInputStream bais = new ByteArrayInputStream(receivedASAPMessage);
// read signer
String signer = ASAPSerialization.readCharSequenceParameter(bais);
// read message
byte[] yourMessage = ASAPSerialization.readByteArray(bais);
// read signature
byte[] signature = ASAPSerialization.readByteArray(bais);

if(ASAPCryptoAlgorithms.verify(yourMessage, signature, signer, basicCryptoParameters)) {
    // ok - your application message is verified
} else {/* it is not */ } 

Encrypting

Encrypting a message for a specific recipient is not difficult with an asymmetric key pair. We encrypt the message with recipients’ public key. Unfortunately, Asymmetric cryptographic algorithms are slowly and work only on small data sets. Of course, a data set can be split into encrypt-able parts. We choose another – common – solution.

We generate a symmetric key. This key is used to encrypt the data. This key is encrypted with recipients’ public key. We produce now, what we call the encrypted package: Recipient name (not encrypted), encrypted symmetric key, encrypted data.

byte[] yourMessage = ... // produced from your application
CharSequence recipient = "Bob"; // message recipient - "Bob" shall get message
// produce encrypted package
byte[] yourEncryptedMessage = 
    ASAPCryptoAlgorithms.produceEncryptedMessagePackage(message, recipient, basicCryptoParameters);

yourEngine.add(yourURI, yourEncryptedMessage);

Producing that encrypted package is not actually rocket science. We find it useful, to hide this code in ASAPCryptoAlgorithms. Keeps application code shorter.

Decrypting

ASAP makes no difference between encrypted message or message in plain text. They are just byte arrays. You can decrypt you message if you know, that the received bytes are actually a serialized encryption package.

byte[] receivedMessage = ... // received a message which is actually a serialized encryption package
// put it into an input stream
ByteArrayInputStream bais = new ByteArrayInputStream(receivedMessage);
// parse it
ASAPCryptoAlgorithms.EncryptedMessagePackage
    encryptedMessagePackage = ASAPCryptoAlgorithms.parseEncryptedMessagePackage(bais);

// I am Bob. For me?
if(encryptedMessagePackage.getRecipient().equals("Bob")) {
    // encrypt
    byte[] decryptedMessage = 
        ASAPCryptoAlgorithms.decryptPackage(encryptedMessagePackage, basicCryptoParameters);
} // else: not for me - cannot decrypt it

Method ASAPCryptoAlgorithms.parseEncryptedMessagePackage() separates recipient, encrypted symmetric key and encrypted message from the byte array. Recipient is transmitted in plain text. Any peer can check, if it is recipient. Peer in our examples goes apparently by Bob. The ASAPCryptoAlgorithms.decryptPackage()call will not fail if peer is recipient and has the fitting private key. It returns the decrypted message.

Combine signing and encryption

Signing and encryption can be combined of course. Be sure that you sign first and encrypt afterwards.

An example of producing and parsing a signed and encrypted message can be found in SharkNetMessage

Setup security environment

[TODO]

Clone this wiki locally