package com.vtence.molecule.session;
import com.vtence.molecule.crypto.DigestAlgorithm;
import com.vtence.molecule.crypto.HMacDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
public class SecureSessionEncoder implements SessionEncoder {
private final Base64.Encoder encoder = Base64.getEncoder();
private final Base64.Decoder decoder = Base64.getDecoder();
private final List<String> keys = new ArrayList<>();
private final Marshaller<Session> marshaller;
private final DigestAlgorithm digester;
public SecureSessionEncoder(String key) {
this(key, new Serializer<>(Session.class), HMacDigest.SHA256());
}
public SecureSessionEncoder(String key, Marshaller<Session> marshaller, DigestAlgorithm digester) {
this.keys.add(key);
this.marshaller = marshaller;
this.digester = digester;
}
public String encode(Session session) throws Exception {
byte[] data = marshaller.marshall(session);
byte[] digest = digester.compute(currentKey(), data);
return toBase64(data) + "--" + toBase64(digest);
}
public Session decode(String content) throws Exception {
String[] parts = content.split("--");
byte[] data = fromBase64(parts[0]);
byte[] digest = fromBase64(parts.length > 1 ? parts[1] : "");
return digestsMatch(data, digest) ? marshaller.unmarshall(data) : null;
}
public SecureSessionEncoder acceptAlternateKeys(String... oldKeys) {
keys.addAll(Arrays.asList(oldKeys));
return this;
}
private String toBase64(byte[] data) {
return encoder.encodeToString(data);
}
private byte[] fromBase64(String base64) {
return decoder.decode(base64);
}
private String currentKey() {
return keys.get(0);
}
private boolean digestsMatch(byte[] content, byte[] digest) throws Exception {
for (String key : keys) {
if (digester.verify(key, content, digest)) return true;
}
return false;
}
}