Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 155 additions & 60 deletions src/main/java/net/sharksystem/asap/crypto/ASAPCryptoAlgorithms.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import java.security.PublicKey;

/**
* Defines basic methods we need to implement signing and verification and to get information how to encrypt
* Basic methods and constants necessary for encryption/ decryption and signing/ verification.
*/
public interface ASAPCryptoParameterStorage {
String DEFAULT_ASYMMETRIC_ENCRYPTION_ALGORITHM = "RSA/ECB/PKCS1Padding";
Expand All @@ -18,18 +18,21 @@ public interface ASAPCryptoParameterStorage {
String DEFAULT_ASYMMETRIC_SIGNATURE_ALGORITHM = "SHA256withRSA";

/**
*
* @return private key of local device - for signing
* @throws ASAPSecurityException
* Returns this device's private key, generating a key pair if none is available yet.
* @return the private key
* @throws ASAPSecurityException if no key is available and key generation fails
* @see ASAPKeyStore#generateKeyPair()
*/
PrivateKey getPrivateKey() throws ASAPSecurityException;

// debugging
// PrivateKey getPrivateKey(CharSequence subjectID) throws ASAPSecurityException;

/**
* @return public key of local device - for signing
* @throws ASAPSecurityException
* Returns this device's public key, generating a key pair if none is available yet.
* @return the public key
* @throws ASAPSecurityException if no key is available and key generation fails
* @see ASAPKeyStore#generateKeyPair()
*/
PublicKey getPublicKey() throws ASAPSecurityException;

Expand All @@ -39,6 +42,11 @@ public interface ASAPCryptoParameterStorage {

String getAsymmetricSigningAlgorithm();

/**
* Generates a symmetric key using this store's default symmetric encryption algorithm
* @return the symmetric key
* @throws ASAPSecurityException if the default encryption algorithm is not available in the current environment
*/
SecretKey generateSymmetricKey() throws ASAPSecurityException;

String getSymmetricEncryptionAlgorithm();
Expand Down
29 changes: 19 additions & 10 deletions src/main/java/net/sharksystem/asap/crypto/ASAPKeyStore.java
Original file line number Diff line number Diff line change
@@ -1,36 +1,45 @@
package net.sharksystem.asap.crypto;

import net.sharksystem.asap.ASAPException;
import net.sharksystem.asap.ASAPSecurityException;
import net.sharksystem.fs.ExtraData;

import java.io.IOException;
import java.security.PublicKey;

/**
* Basic requirement to encrypt / decrypt and sign/verify messages.
* A key store provides access to local key pairs as well as utilities for managing
* the key pair of the store's owner.
* <p>
* Key stores are required for encryption/ decryption as well as creating
* and verifying signatures, see e.g. {@link ASAPCryptoAlgorithms}.
*/
public interface ASAPKeyStore extends ASAPCryptoParameterStorage {
/**
* @param subjectID
* @return public key of recipient - to encrypt
* @throws ASAPSecurityException if key cannot be found
* Returns the public key of the specified peer.
* @param subjectID ID of the peer
* @return public key of the peer whose ID matches the given ID
* @throws ASAPSecurityException if the requested key is not present in the key store
*/
PublicKey getPublicKey(CharSequence subjectID) throws ASAPSecurityException;

/**
*
* @param peerID
* @return true if peerID is owners' id.
* Determines if a peer is the owner of this key store.
* @param peerID ID of the peer to test
* @return true if and only if the specified ID matches this key store's owner's ID
*/
boolean isOwner(CharSequence peerID);

CharSequence getOwner();

/**
* A new key pair is created
* Generates a new key pair for this key store's owner and stores it.
* @throws ASAPSecurityException if key generation fails, e.g. this key store's cryptographic algorithm
* is not available in the current environment or an invalid key size is used
*/
void generateKeyPair() throws ASAPSecurityException;

/**
* Updates the persistence backend for this key store.
* @param extraData the persistence interface to use
*/
void setMementoTarget(ExtraData extraData);
}
90 changes: 65 additions & 25 deletions src/main/java/net/sharksystem/asap/crypto/InMemoASAPKeyStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;

/**
* A key store that lives entirely in memory.
* Key pair generation for the owner is performed lazily and currently limited to RSA with a key size of 2048 bits.
*/
public class InMemoASAPKeyStore implements ASAPKeyStore {
private static final CharSequence ASAP_KEYSTORE_MEMENTO_KEY = "asapKeyStoreMemento";
private PrivateKey privateKey;
Expand All @@ -29,20 +33,19 @@ public class InMemoASAPKeyStore implements ASAPKeyStore {
private HashMap<CharSequence, PublicKey> peerPublicKeys = new HashMap<CharSequence, PublicKey>();

/**
* Setup key store, A key pair will be generated
* @param ownerID
* @throws ASAPSecurityException
* Constructs a new key store for the given owner.
* @param ownerID ID of this key store's owner
*/
public InMemoASAPKeyStore(CharSequence ownerID) throws ASAPSecurityException {
// generate owners key pair;
public InMemoASAPKeyStore(CharSequence ownerID) {
this.ownerID = ownerID;
//this.generateKeyPair();
}

/**
* Setup key store with a key pair
* @param ownerID
* @param ownerKeyPair
* Constructs a new key store for the given owner with the specified key pair.
* @param ownerID ID of this key store's owner
* @param ownerKeyPair key pair of the owner
* @param keyPairCreationTime a timestamp representing the date the given key pair was created on, in milliseconds
* since epoch (January 1, 1970 UTC).
* @deprecated
*/
public InMemoASAPKeyStore(CharSequence ownerID, KeyPair ownerKeyPair, long keyPairCreationTime) {
Expand All @@ -60,6 +63,12 @@ protected void setKeyPair(KeyPair keyPair) {
this.keyPair = keyPair;
}

/**
* Returns the key pair of this store's owner.
* If this pair does not exist yet, the store attempts to generate and store one.
* @return the key pair. null if no pair exists and generation fails.
* @see ASAPKeyStore#generateKeyPair()
*/
public KeyPair getKeyPair() {
if(this.keyPair == null) {
Log.writeLog(this, "key pair not yet generated - do it now");
Expand All @@ -74,19 +83,23 @@ public KeyPair getKeyPair() {
return this.keyPair;
}

@Override
public SecretKey generateSymmetricKey() throws ASAPSecurityException {
return ASAPCryptoAlgorithms.generateSymmetricKey(
DEFAULT_SYMMETRIC_KEY_TYPE, DEFAULT_SYMMETRIC_KEY_SIZE);
}

@Override
public long getKeysCreationTime() throws ASAPSecurityException {
return this.keyPairCreationTime;
}

@Override
public int getSymmetricKeyLen() {
return ASAPKeyStore.DEFAULT_SYMMETRIC_KEY_SIZE;
}

@Override
public void generateKeyPair() throws ASAPSecurityException {
this.setKeyPair(this.generateNewKeyPair());
this.privateKey = this.keyPair.getPrivate();
Expand All @@ -97,7 +110,7 @@ public void generateKeyPair() throws ASAPSecurityException {

private KeyPair generateNewKeyPair() throws ASAPSecurityException {
Log.writeLog(this, "create key pair");
KeyPairGenerator keyGen = null;
KeyPairGenerator keyGen;
try {
keyGen = KeyPairGenerator.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
Expand Down Expand Up @@ -214,16 +227,21 @@ public void addKeyPair(String peerID, KeyPair keyPair) {
}

/**
* Write storage owners' public key to stream
* @param os
* Writes this store's owner's public key to the given output stream.
* If no key exists yet, the store attempts to generate it.
* The key's metadata is written as well to enable the reader to reconstruct it.
* @param os the stream to write to
* @throws ASAPSecurityException if no key exists and key generation fails
* @throws IOException if an I/O error occurs
* @see ASAPKeyStore#generateKeyPair()
* @see InMemoASAPKeyStore#readPublicKey(CharSequence, InputStream)
*/
public void writePublicKey(OutputStream os) throws ASAPSecurityException, IOException {
PublicKey publicKey = this.getPublicKey();

String format = publicKey.getFormat();
String algorithm = publicKey.getAlgorithm();
byte[] byteEncodedPublicKey = publicKey.getEncoded();
int length = byteEncodedPublicKey.length;

// makes it much easier to serialize simple data
DataOutputStream dos = new DataOutputStream(os);
Expand All @@ -238,11 +256,14 @@ public void writePublicKey(OutputStream os) throws ASAPSecurityException, IOExce
}

/**
* Read public key from another peer from an extern source (input stream)
* @param peer
* @param is
* Reads a public key from the given input stream and adds it to this store.
* @param peer ID of the public key's owner
* @param is the stream to read from
* @throws IOException if an I/O error occurs
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
*/
public void readPublicKey(CharSequence peer, InputStream is) throws ASAPSecurityException, IOException, NoSuchAlgorithmException, InvalidKeySpecException {
public void readPublicKey(CharSequence peer, InputStream is) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
DataInputStream dis = new DataInputStream(is);

// read in same order as written
Expand All @@ -253,7 +274,7 @@ public void readPublicKey(CharSequence peer, InputStream is) throws ASAPSecurity
// allocate memory
byte[] byteEncodedPublicKey = new byte[len];

// read encoded key directly from stream
// FIXME: Method currently doesn't store the received key as per the specification.
is.read(byteEncodedPublicKey);

// create key object
Expand Down Expand Up @@ -297,22 +318,33 @@ private void save() {
}
}

/**
* Returns a byte representation of this store's owner's key pair and related metadata.
* If no key pair exists yet, the store attempts to create one.
* @return a byte array holding the owner's key pair
* @throws ASAPException if no key pair exists and key generation fails
* @throws IOException if an I/O error occurs
* @see ASAPKeyStore#generateKeyPair()
* @see InMemoASAPKeyStore#restoreMemento(byte[])
*/
public byte[] getMemento() throws ASAPException, IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();

PrivateKey privateKey = this.getPrivateKey();
// encoded private key
ASAPSerialization.writeByteArray(this.getPrivateKey().getEncoded(), baos);
ASAPSerialization.writeByteArray(privateKey.getEncoded(), baos);
// private key algorithm
ASAPSerialization.writeCharSequenceParameter(this.getPrivateKey().getAlgorithm(), baos);
ASAPSerialization.writeCharSequenceParameter(privateKey.getAlgorithm(), baos);
// private key format
ASAPSerialization.writeCharSequenceParameter(this.getPrivateKey().getFormat(), baos);
ASAPSerialization.writeCharSequenceParameter(privateKey.getFormat(), baos);

PublicKey publicKey = this.getPublicKey();
// public key encoded
ASAPSerialization.writeByteArray(this.getPublicKey().getEncoded(), baos);
ASAPSerialization.writeByteArray(publicKey.getEncoded(), baos);
// public key algorithm
ASAPSerialization.writeCharSequenceParameter(this.getPublicKey().getAlgorithm(), baos);
ASAPSerialization.writeCharSequenceParameter(publicKey.getAlgorithm(), baos);
// public key format
ASAPSerialization.writeCharSequenceParameter(this.getPublicKey().getFormat(), baos);
ASAPSerialization.writeCharSequenceParameter(publicKey.getFormat(), baos);

// creation time
ASAPSerialization.writeLongParameter(this.keyPairCreationTime, baos);
Expand All @@ -323,6 +355,14 @@ public byte[] getMemento() throws ASAPException, IOException {
return baos.toByteArray();
}

/**
* Constructs a key pair from the given byte array and stores it as this key store's owner's.
* Currently, only the RSA cryptosystem is supported.
* @param mementoData the byte representation of the key pair
* @throws IOException if an I/O error occurs
* @throws ASAPException if the array's end is reached or constructing the key pair fails
* @see InMemoASAPKeyStore#getMemento()
*/
public void restoreMemento(byte[] mementoData) throws IOException, ASAPException {
ByteArrayInputStream bais = new ByteArrayInputStream(mementoData);

Expand All @@ -341,7 +381,7 @@ public void restoreMemento(byte[] mementoData) throws IOException, ASAPException
this.privateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
this.publicKey = kf.generatePublic(new X509EncodedKeySpec(publicKeyBytes));
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new ASAPException("cannot restore keystore form memento: " + e.getLocalizedMessage());
throw new ASAPException("cannot restore keystore from memento: " + e.getLocalizedMessage());
}

// reset those simple data after that critical stuff.
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/net/sharksystem/asap/crypto/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* Interfaces and classes for managing and using cryptographic keys
*/
package net.sharksystem.asap.crypto;
31 changes: 2 additions & 29 deletions src/main/java/net/sharksystem/asap/engine/ASAPInternalPeerFS.java
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,7 @@ public ASAPKeyStore getAsapKeyStore() throws ASAPSecurityException {
///////////////////////////////////////////////////////////////////////////////////////////////////////////
private ExtraData extraData = null;

@Override
public ExtraData getExtraData() throws SharkException, IOException {
if(this.extraData == null) {
this.extraData = new ExtraDataFS(this.rootFolderName, "asapPeerExtraData");
Expand All @@ -712,44 +713,16 @@ public ExtraData getExtraData() throws SharkException, IOException {
return this.extraData;
}

public void putExtra(CharSequence key, byte[] value) throws IOException, SharkException {
this.getExtraData().putExtra(key, value);
}

@Override
public void putExtra(CharSequence key, Integer value) throws IOException, SharkException {
this.getExtraData().putExtra(key, value);
}

@Override
public void putExtra(CharSequence key, CharSequence value) throws IOException, SharkException {
public void putExtra(CharSequence key, byte[] value) throws IOException, SharkException {
this.getExtraData().putExtra(key, value);
}

@Override
public void putExtra(CharSequence key, Set<CharSequence> value) throws IOException, SharkException {
this.getExtraData().putExtra(key, value);
}

public byte[] getExtra(CharSequence key) throws IOException, SharkException {
return this.getExtraData().getExtra(key);
}

@Override
public int getExtraInteger(CharSequence key) throws IOException, SharkException {
return this.getExtraData().getExtraInteger(key);
}

@Override
public Set<CharSequence> getExtraCharSequenceSetParameter(CharSequence key) throws IOException, SharkException {
return this.getExtraData().getExtraCharSequenceSetParameter(key);
}

@Override
public String getExtraString(CharSequence key) throws IOException, SharkException {
return this.getExtraData().getExtraString(key);
}

@Override
public void removeAll() throws IOException, SharkException {
this.getExtraData().removeAll();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,13 @@ public static byte readByteParameter(InputStream is) throws IOException, ASAPExc
return readByte(is);
}

/**
* Reads a byte from the given stream.
* @param is the stream to read from
* @return the next byte
* @throws IOException if an I/O error occurs
* @throws ASAPException if the end of the stream is reached
*/
public static byte readByte(InputStream is) throws IOException, ASAPException {
try {
int value = is.read();
Expand Down
Loading