NONCE_SET = new ExpirableSet<>(Interval.ofMinutes(60).toMillis());
+
private MusapLinkConf config;
/**
@@ -86,7 +93,7 @@ public CouplingApiMessage encrypt(final CouplingApiMessage msg, final TransportK
msg.setMac(this.calculateMac(tkeys, msg));
return msg;
} catch (Exception e) {
- log.debug("Message encryption failed for MSISDN " + tkeys, e);
+ log.debug("Message encryption failed for " + tkeys, e);
throw new IOException("Transport security error", e);
}
}
@@ -95,7 +102,7 @@ public CouplingApiMessage encrypt(final CouplingApiMessage msg, final TransportK
* Decrypt a message
* Also calculates and verifies MAC
* @param msg Message to encrypt
- * @param tkeys UUID
+ * @param tkeys Transport Keys
* @return Decrypted message
* @throws IOException
* @throws GeneralSecurityException
@@ -133,59 +140,74 @@ public CouplingApiMessage decrypt(final CouplingApiMessage msg, final TransportK
}
/**
- * Clear transport encryption key cache for the given MSISDN
- * @param uuid
+ * Clear transport encryption key cache for the given MUSAP ID
+ * @param musapid MUSAP ID
*/
- public void clearCache(final String uuid) {
- CACHE.remove(uuid);
+ public void clearCache(final String musapid) {
+ CACHE.remove(musapid);
}
/**
- * Resolve encryption key for given MSISDN
- * @param uuid UUID
+ * Resolve encryption key for given MUSAP ID
+ * @param musapid MUSAP ID
* @return Encryption keys (contains null keys if not found)
*/
- public TransportKeys resolveKeys(final String uuid) {
- if (uuid == null) {
+ public TransportKeys resolveKeys(final String musapid) {
+ if (musapid == null) {
return null;
}
- log.trace("Resolving transport keys for UUID " + uuid);
+ log.trace("Resolving transport keys for MUSAP ID " + musapid);
- final TransportKeys cached = CACHE.get(uuid);
+ final TransportKeys cached = CACHE.get(musapid);
if (cached != null) {
log.trace("Resolved transport keys from cache");
return cached;
}
try {
- //final AppKeys keys = this.config.getAppKeys();
- //try (Connection conn = keys.getConnectionRO("AppTransportEncryption.FETCH")) {
- // final List keyRows = keys.fetchKeysByUUID(conn, uuid);
- // if (keyRows.size() > 0) {
- // AppKeys.Row enc = null;
- // AppKeys.Row mac = null;
- // for (AppKeys.Row k : keyRows) {
- // if (k.keytype == AppKeys.KEYTYPE_AES) enc = k;
- // if (k.keytype == AppKeys.KEYTYPE_MAC) mac = k;
- // }
- // if (enc != null && mac != null) {
- // final TransportKeys txnKeys = new TransportKeys(uuid, enc.key, mac.key);
- // if (this.config.isTransportEncryptionCacheEnabled()) {
- // CACHE.put(uuid, txnKeys);
- // }
- // return txnKeys;
- // }
- // }
- // log.debug("No transport keys found");
- // return null;
- //}
- return null;
-
+ MusapLinkAccount account = AccountStorage.findAccountByMusapId(musapid);
+ return account.getTransportKeys();
} catch (Exception e) {
log.debug("Failed to fetch transport keys", e);
return null;
}
}
+ /**
+ * Check if the nonce in the given message is acceptable.
+ * This compares the nonce to a used nonce list, and verifies that the message
+ * timestamp is not too old.
+ * @param msg Message to check
+ * @return true if nonce is acceptable
+ */
+ public boolean isNonceValid(CouplingApiMessage msg) {
+
+ if (msg == null) return true;
+ if (msg.getBasePayload() == null) return true;
+
+ CouplingApiPayload payload = msg.getBasePayload();
+ String nonce = payload.nonce;
+
+ if (nonce == null) return true;
+ if (NONCE_SET.containsKey(nonce)) {
+ log.warn("Potential replay attack: NONCE already used");
+ return false;
+ }
+ NONCE_SET.add(nonce);
+
+ if (payload.getTimestamp() == null) {
+ log.warn("Potential replay attack: No timestamp");
+ return false;
+ }
+
+ // Check if the timestamp is within an hour
+ if (Instant.now().minus(Duration.ofHours(1)).isBefore(payload.getTimestamp())) {
+ return true;
+ } else {
+ log.warn("Potential replay attack: Timestamp too old (" + payload.getTimestamp() + ")");
+ return false;
+ }
+ }
+
/**
* Calculate MAC for a message
* @param keys Key set of user
@@ -200,19 +222,22 @@ public String calculateMac(final TransportKeys keys, final CouplingApiMessage ms
return msg.calculateMac(keys.mac);
}
+ /**
+ * Class that contains MUSAP transport encryption keys
+ */
public static class TransportKeys {
- public String uuid;
+ public String musapid;
public byte[] enc;
public byte[] mac;
- public TransportKeys(final String uuid, final byte[] enc, final byte[] mac) {
- this.uuid = uuid;
+ public TransportKeys(final String musapid, final byte[] enc, final byte[] mac) {
+ this.musapid = musapid;
this.enc = enc;
this.mac = mac;
}
@Override
public int hashCode() {
final int prime = 31;
- int result = this.uuid != null ? this.uuid.hashCode() : 0;
+ int result = this.musapid != null ? this.musapid.hashCode() : 0;
if (this.enc != null) result = prime * result + Arrays.hashCode(this.enc);
if (this.mac != null) result = prime * result + Arrays.hashCode(this.mac);
return result;
@@ -223,9 +248,8 @@ public boolean equals(Object obj) {
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
TransportKeys other = (TransportKeys) obj;
- if (this.uuid != null) {
- // MSISDN mismatch
- if (!this.uuid.equals(other.uuid)) {
+ if (this.musapid != null) {
+ if (!this.musapid.equals(other.musapid)) {
return false;
}
}
@@ -242,8 +266,8 @@ public boolean equals(Object obj) {
@Override
public String toString() {
- final StringBuilder sb = new StringBuilder("TKey{uuid=");
- sb.append(this.uuid);
+ final StringBuilder sb = new StringBuilder("TKey{musapid=");
+ sb.append(this.musapid);
sb.append(", enc");
sb.append(", mac");
sb.append("}");
diff --git a/src/main/java/fi/methics/webapp/musaplink/AccountStorage.java b/src/main/java/fi/methics/webapp/musaplink/util/db/AccountStorage.java
similarity index 80%
rename from src/main/java/fi/methics/webapp/musaplink/AccountStorage.java
rename to src/main/java/fi/methics/webapp/musaplink/util/db/AccountStorage.java
index 9c6e631..dd0ab09 100644
--- a/src/main/java/fi/methics/webapp/musaplink/AccountStorage.java
+++ b/src/main/java/fi/methics/webapp/musaplink/util/db/AccountStorage.java
@@ -1,4 +1,4 @@
-package fi.methics.webapp.musaplink;
+package fi.methics.webapp.musaplink.util.db;
import java.sql.Connection;
import java.sql.PreparedStatement;
@@ -9,17 +9,14 @@
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
-import java.util.Timer;
-import java.util.TimerTask;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import fi.methics.webapp.musaplink.MusapLinkAccount;
import fi.methics.webapp.musaplink.MusapLinkAccount.MusapKey;
-import fi.methics.webapp.musaplink.util.CouplingCode;
import fi.methics.webapp.musaplink.util.MusapException;
import fi.methics.webapp.musaplink.util.MusapLinkConf;
-import fi.methics.webapp.musaplink.util.db.MusapDb;
/**
* Database class for MUSAP Link Account Storage.
@@ -29,13 +26,10 @@ public class AccountStorage extends MusapDb {
private static final Log log = LogFactory.getLog(AccountStorage.class);
- private static final String INSERT_COUPLING_CODE = "INSERT INTO coupling_codes (couplingcode, linkid, created_dt) VALUES (?,?, ?)";
- private static final String SELECT_COUPLING_CODE = "SELECT linkid, couplingcode FROM coupling_codes WHERE couplingcode=?";
- private static final String DELETE_OLD_COUPLING_CODES = "DELETE FROM coupling_codes WHERE created_dt";
-
private static final String INSERT_ACCOUNT = "INSERT INTO musap_accounts (musapid, fcmtoken, apnstoken, created_dt) VALUES (?,?,?,?)";
private static final String INSERT_LINKID = "INSERT INTO link_ids (musapid, linkid, name) VALUES (?,?,?)";
private static final String INSERT_KEYS = "INSERT INTO transport_keys (musapid, mackey, enckey) VALUES (?,?,?)";
+ private static final String SELECT_KEYS = "SELECT mackey, enckey FROM transport_keys WHERE musapid=?";
private static final String UPDATE_ACCOUNT = "UPDATE musap_accounts SET fcmtoken=?, apnstoken=? WHERE musapid=?";
private static final String SELECT_ACCOUNT = "SELECT musapid, fcmtoken, apnstoken FROM musap_accounts WHERE musapid=?";
@@ -43,7 +37,6 @@ public class AccountStorage extends MusapDb {
private static final String INSERT_KEYDETAILS = "INSERT INTO key_details (musapid, keyid, keyname, certificate, publickey) VALUES (?,?,?,?,?)";
private static final String UPDATE_KEYDETAILS = "UPDATE key_details SET keyname=?, certificate=?, publickey=?, modified_dt=? WHERE musapid=? AND keyid=?";
- private static final String UPDATE_KEYDETAILS_BY_NAME = "UPDATE key_details SET keyname=?, certificate=?, publickey=?, modified_dt=? WHERE musapid=? AND keyname=?";
private static final String SELECT_KEYDETAILS = "SELECT keyid, keyname, certificate, publickey FROM key_details WHERE musapid=? AND keyname=?";
private static final String SELECT_KEYDETAILS_BY_ID = "SELECT keyid, keyname, certificate, publickey FROM key_details WHERE musapid=? AND keyid=?";
private static final String LIST_KEYDETAILS = "SELECT keyid, keyname, certificate, publickey FROM key_details WHERE musapid=?";
@@ -59,7 +52,7 @@ public class AccountStorage extends MusapDb {
* @param linkid
*/
public static void addLinkId(String musapid, String linkid) {
- MusapLinkAccount account = findByMusapId(musapid);
+ MusapLinkAccount account = findAccountByMusapId(musapid);
if (account == null) {
log.warn("No account found with musapid " + musapid + ". Not linking.");
return;
@@ -80,28 +73,12 @@ public static void addLinkId(String musapid, String linkid) {
}
}
- /**
- * Clean old coupling codes
- */
- public static void cleanCouplingCodes() {
- try (Connection conn = getConnection();
- PreparedStatement ps = conn.prepareStatement(DELETE_OLD_COUPLING_CODES))
- {
- int cutoff = MusapLinkConf.getInstance().getCouplingLifetime();
- int cutoffMs = cutoff * 1000;
- ps.setTimestamp(1, new Timestamp(System.currentTimeMillis() - cutoffMs));
- ps.executeUpdate();
- } catch (SQLException e) {
- log.error("Failed clean coupling codes", e);
- }
- }
-
/**
* Find a MUSAP account by Link ID
* @param linkid Link ID
* @return MusapAccount or null
*/
- public static MusapLinkAccount findByLinkId(String linkid) {
+ public static MusapLinkAccount findAccountByLinkId(String linkid) {
if (linkid == null) return null;
try (Connection conn = getConnection();
@@ -110,7 +87,7 @@ public static MusapLinkAccount findByLinkId(String linkid) {
ps.setString(1, linkid);
try (ResultSet result = ps.executeQuery()) {
if (result.next()) {
- return findByMusapId(result.getString(1));
+ return findAccountByMusapId(result.getString(1));
}
}
} catch (SQLException e) {
@@ -125,7 +102,7 @@ public static MusapLinkAccount findByLinkId(String linkid) {
* @param musapid MUSAP ID
* @return MusapAccount or null
*/
- public static MusapLinkAccount findByMusapId(String musapid) {
+ public static MusapLinkAccount findAccountByMusapId(String musapid) {
if (musapid == null) return null;
try (Connection conn = getConnection();
@@ -139,6 +116,7 @@ public static MusapLinkAccount findByMusapId(String musapid) {
account.fcmToken = result.getString(2);
account.apnsToken = result.getString(3);
account.linkids = new HashSet<>(listLinkIds(musapid));
+ fillTransportKeys(conn, account);
return account;
}
}
@@ -149,31 +127,6 @@ public static MusapLinkAccount findByMusapId(String musapid) {
return null;
}
- /**
- * Check if a Link ID is found for the given coupling code. Removes it from the list if found.
- * @param couplingCode Coupling Code
- * @return Link ID if found. Null otherwise.
- */
- public static String findLinkId(String couplingCode) {
-
- String linkid = null;
-
- try (Connection conn = getConnection();
- PreparedStatement ps = conn.prepareStatement(SELECT_COUPLING_CODE))
- {
- ps.setString(1, couplingCode);
- try (ResultSet result = ps.executeQuery()) {
- if (result.next()) {
- linkid = result.getString(1);
- }
- }
- } catch (SQLException e) {
- log.error("Failed find Link ID", e);
- throw new MusapException(e);
- }
- return linkid;
- }
-
/**
* Get KeyID based on the given keyname
* @param account MUSAP account
@@ -319,56 +272,13 @@ public static Collection listKeyDetails(MusapLinkAccount account) {
}
- /**
- * Generate a new Coupling Code for given LinkID. Store the combination.
- * @param linkid Link ID
- * @return Coupling Code
- */
- public static synchronized CouplingCode newCouplingCode(String linkid) {
- CouplingCode couplingCode = new CouplingCode();
-
- while (findLinkId(couplingCode.getCode()) != null) {
- couplingCode = new CouplingCode();
- }
-
- try (Connection conn = getConnection();
- PreparedStatement ps = conn.prepareStatement(INSERT_COUPLING_CODE))
- {
- ps.setString(1, couplingCode.getCode());
- ps.setString(2, linkid);
- ps.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
- ps.executeUpdate();
- } catch (SQLException e) {
- log.error("Failed insert Coupling Code", e);
- throw new MusapException(e);
- }
- return couplingCode;
- }
-
- /**
- * Schedule a transaction cleanup task
- * @param interval Task run interval (milliseconds)
- * @return a handle to the timer
- */
- public static Timer scheduleCleaner(long interval) {
-
- Timer timer = new Timer();
- new Timer().scheduleAtFixedRate(new TimerTask() {
- @Override
- public void run() {
- AccountStorage.cleanCouplingCodes();
- }
- }, interval, interval);
- return timer;
- }
-
/**
* Store a MUSAP account
* @param account New account
*/
public static void storeAccount(MusapLinkAccount account) {
if (account == null || account.musapid == null) {
- log.error("Ignoring account with null musapid");
+ log.error("Ignoring account with null MUSAP ID");
return;
}
@@ -380,11 +290,14 @@ public static void storeAccount(MusapLinkAccount account) {
ps.setString(3, account.apnsToken);
ps.setTimestamp(4, new Timestamp(System.currentTimeMillis()));
ps.executeUpdate();
+
+ storeTransportKeys(conn, account);
} catch (SQLException e) {
log.error("Failed insert MUSAP account", e);
throw new MusapException(e);
}
}
+
/**
* Update a MUSAP account
* @param musapid MUSAP ID
@@ -484,4 +397,57 @@ public static void upsertKeyDetails(MusapLinkAccount account, MusapKey key) {
}
+
+ /**
+ * Store transport encryption keys.
+ * Does nothing if given account object has no keys.
+ * @param conn DB connection
+ * @param account Account that contains the keys
+ */
+ private static void storeTransportKeys(Connection conn, MusapLinkAccount account) {
+ if (account == null) return;
+ if (account.aesKey == null) return;
+ if (account.macKey == null) return;
+
+ log.debug("Storing transport keys for MUSAP ID " + account.musapid);
+ try (PreparedStatement ps = conn.prepareStatement(INSERT_KEYS)) {
+ ps.setString(1, account.musapid);
+ ps.setBytes(2, account.macKey);
+ ps.setBytes(3, account.aesKey);
+ ps.executeUpdate();
+ } catch (SQLException e) {
+ log.error("Failed insert MUSAP transport keys", e);
+ throw new MusapException(e);
+ }
+ }
+
+ /**
+ * Fetch and fill transport encryption keys to given MUSAP account
+ * @param conn DB Connection
+ * @param account Account that should be filled
+ * @return Account with keys
+ */
+ private static MusapLinkAccount fillTransportKeys(Connection conn, MusapLinkAccount account) {
+
+ log.debug("Looking for transport keys");
+ if (account == null) return null;
+
+ try (PreparedStatement ps = conn.prepareStatement(SELECT_KEYS)) {
+ ps.setString(1, account.musapid);
+ try (ResultSet result = ps.executeQuery()) {
+ if (result.next()) {
+ account.macKey = result.getBytes(1);
+ account.aesKey = result.getBytes(2);
+ if (account.aesKey != null) log.debug("Found AES key of " + account.aesKey.length + " bytes");
+ if (account.macKey != null) log.debug("Found MAC key of " + account.macKey.length + " bytes");
+ }
+ }
+ } catch (SQLException e) {
+ log.error("Failed get transport keys", e);
+ throw new MusapException(e);
+ }
+ return account;
+ }
+
+
}
diff --git a/src/main/java/fi/methics/webapp/musaplink/util/db/CouplingStorage.java b/src/main/java/fi/methics/webapp/musaplink/util/db/CouplingStorage.java
new file mode 100644
index 0000000..21e4d38
--- /dev/null
+++ b/src/main/java/fi/methics/webapp/musaplink/util/db/CouplingStorage.java
@@ -0,0 +1,113 @@
+package fi.methics.webapp.musaplink.util.db;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import fi.methics.webapp.musaplink.util.CouplingCode;
+import fi.methics.webapp.musaplink.util.MusapException;
+import fi.methics.webapp.musaplink.util.MusapLinkConf;
+
+/**
+ * Database class for MUSAP Link Coupling Codes.
+ */
+public class CouplingStorage extends MusapDb {
+
+ private static final Log log = LogFactory.getLog(CouplingStorage.class);
+
+ private static final String INSERT_COUPLING_CODE = "INSERT INTO coupling_codes (couplingcode, linkid, created_dt) VALUES (?,?, ?)";
+ private static final String SELECT_COUPLING_CODE = "SELECT linkid, couplingcode FROM coupling_codes WHERE couplingcode=?";
+ private static final String DELETE_OLD_COUPLING_CODES = "DELETE FROM coupling_codes WHERE created_dt";
+
+ /**
+ * Clean old coupling codes
+ */
+ public static void cleanCouplingCodes() {
+ try (Connection conn = getConnection();
+ PreparedStatement ps = conn.prepareStatement(DELETE_OLD_COUPLING_CODES))
+ {
+ int cutoff = MusapLinkConf.getInstance().getCouplingLifetime();
+ int cutoffMs = cutoff * 1000;
+ ps.setTimestamp(1, new Timestamp(System.currentTimeMillis() - cutoffMs));
+ ps.executeUpdate();
+ } catch (SQLException e) {
+ log.error("Failed clean coupling codes", e);
+ }
+ }
+
+ /**
+ * Check if a Link ID is found for the given coupling code. Removes it from the list if found.
+ * @param couplingCode Coupling Code
+ * @return Link ID if found. Null otherwise.
+ */
+ public static String findLinkId(String couplingCode) {
+
+ String linkid = null;
+
+ try (Connection conn = getConnection();
+ PreparedStatement ps = conn.prepareStatement(SELECT_COUPLING_CODE))
+ {
+ ps.setString(1, couplingCode);
+ try (ResultSet result = ps.executeQuery()) {
+ if (result.next()) {
+ linkid = result.getString(1);
+ }
+ }
+ } catch (SQLException e) {
+ log.error("Failed find Link ID", e);
+ throw new MusapException(e);
+ }
+ return linkid;
+ }
+
+ /**
+ * Generate a new Coupling Code for given LinkID. Store the combination.
+ * @param linkid Link ID
+ * @return Coupling Code
+ */
+ public static synchronized CouplingCode newCouplingCode(String linkid) {
+ CouplingCode couplingCode = new CouplingCode();
+
+ while (findLinkId(couplingCode.getCode()) != null) {
+ couplingCode = new CouplingCode();
+ }
+
+ try (Connection conn = getConnection();
+ PreparedStatement ps = conn.prepareStatement(INSERT_COUPLING_CODE))
+ {
+ ps.setString(1, couplingCode.getCode());
+ ps.setString(2, linkid);
+ ps.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
+ ps.executeUpdate();
+ } catch (SQLException e) {
+ log.error("Failed insert Coupling Code", e);
+ throw new MusapException(e);
+ }
+ return couplingCode;
+ }
+
+ /**
+ * Schedule a transaction cleanup task
+ * @param interval Task run interval (milliseconds)
+ * @return a handle to the timer
+ */
+ public static Timer scheduleCleaner(long interval) {
+
+ Timer timer = new Timer();
+ new Timer().scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ CouplingStorage.cleanCouplingCodes();
+ }
+ }, interval, interval);
+ return timer;
+ }
+
+}
diff --git a/src/main/java/fi/methics/webapp/musaplink/TxnStorage.java b/src/main/java/fi/methics/webapp/musaplink/util/db/TxnStorage.java
similarity index 96%
rename from src/main/java/fi/methics/webapp/musaplink/TxnStorage.java
rename to src/main/java/fi/methics/webapp/musaplink/util/db/TxnStorage.java
index 0ffb74d..095c354 100644
--- a/src/main/java/fi/methics/webapp/musaplink/TxnStorage.java
+++ b/src/main/java/fi/methics/webapp/musaplink/util/db/TxnStorage.java
@@ -1,4 +1,4 @@
-package fi.methics.webapp.musaplink;
+package fi.methics.webapp.musaplink.util.db;
import java.sql.Connection;
@@ -20,7 +20,6 @@
import fi.methics.webapp.musaplink.util.MusapException;
import fi.methics.webapp.musaplink.util.MusapLinkConf;
import fi.methics.webapp.musaplink.util.SignatureCallback;
-import fi.methics.webapp.musaplink.util.db.MusapDb;
/**
* Database class for MUSAP Link transaction storage.
@@ -66,7 +65,7 @@ public static SignatureCallback storeRequest(String linkid, SignatureReq req) {
/**
* Store a signature response
* @param transid Transaction ID
- * @param linkid MUSAP Link Account ID
+ * @param musapid MUSAP Link Account ID
* @param resp Signature response
*/
public static void storeResponse(String transid, MusapSignResp resp) {
@@ -84,7 +83,7 @@ public static void storeResponse(String transid, MusapSignResp resp) {
/**
* Delete a transaction that has been handled
* @param transid Transaction ID
- * @param linkid MUSAP Link Account ID
+ * @param musapid MUSAP Link Account ID
*/
public static void deleteTransaction(String transid) {
log.info("Deleting handled transaction (transid=" + transid + ")");
@@ -101,7 +100,7 @@ public static void deleteTransaction(String transid) {
/**
* Get a Signature Response
* @param transid Transaction ID
- * @param linkid MUSAP Link Account ID
+ * @param musapid MUSAP Link Account ID
* @return Signature Response or null if not found
*/
public static MusapSignResp getSignResp(String transid) {
diff --git a/src/main/java/fi/methics/webapp/musaplink/util/etsi204/Etsi204Client.java b/src/main/java/fi/methics/webapp/musaplink/util/etsi204/Etsi204Client.java
index a6639eb..98d342a 100644
--- a/src/main/java/fi/methics/webapp/musaplink/util/etsi204/Etsi204Client.java
+++ b/src/main/java/fi/methics/webapp/musaplink/util/etsi204/Etsi204Client.java
@@ -2,6 +2,8 @@
import java.util.Map;
+import fi.methics.webapp.musaplink.util.MusapLinkConf;
+
/**
* ETSI TS 102 204 signature client
*/
@@ -74,10 +76,37 @@ public String getClientId() {
return this.clientid;
}
+ public MusapLinkConf getConfig() {
+ return MusapLinkConf.getInstance();
+ }
+
public String getSscdType() {
return this.sscdtype;
}
+ /**
+ * Resolve EventID from TransID and attributes.
+ * If attributes contain "eventid", it will be returned.
+ * Otherwise, TransID will be returned.
+ * @param transid TransID
+ * @param attrs Attributes
+ * @return EventID to use - or null if not found
+ */
+ public String resolveEventId(String transid,
+ Map attrs) {
+ String eventid = attrs.get("eventid");
+ return eventid != null ? eventid : transid;
+ }
+
+ /**
+ * Resolve NoSpamCode from attribute "nospamcode".
+ * @param attrs Attributes
+ * @return NoSpamCode to use - or null if not found
+ */
+ public String resolveNospamCode(Map attrs) {
+ return attrs.get("nospamcode");
+ }
+
public static enum ClientType {
REST("rest"),
SOAP("soap");
diff --git a/src/main/java/fi/methics/webapp/musaplink/util/etsi204/Etsi204Response.java b/src/main/java/fi/methics/webapp/musaplink/util/etsi204/Etsi204Response.java
index e8cfd6f..ed99668 100644
--- a/src/main/java/fi/methics/webapp/musaplink/util/etsi204/Etsi204Response.java
+++ b/src/main/java/fi/methics/webapp/musaplink/util/etsi204/Etsi204Response.java
@@ -3,7 +3,12 @@
import java.security.cert.CertificateEncodingException;
import java.util.Base64;
import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSSignedData;
@@ -13,10 +18,13 @@
public class Etsi204Response {
+ private static final Log log = LogFactory.getLog(Etsi204Response.class);
private byte[] signature;
private byte[] publickey;
private byte[] certificate;
+ private List certChain;
+
/**
* Create a response from a Laverca SOAP response
* @param resp Laverca SOAP response
@@ -27,9 +35,10 @@ protected Etsi204Response(EtsiResponse resp) {
if (resp.getPkcs7Signature() != null && resp.getPkcs7Signature().getSignerCert() != null) {
this.certificate = resp.getPkcs7Signature().getSignerCert().getEncoded();
this.publickey = resp.getPkcs7Signature().getSignerCert().getPublicKey().getEncoded();
+ this.certChain = this.getCertificateChain(resp);
}
} catch (Exception e) {
- // Ignore
+ log.error("Failed to parse response details", e);
}
}
@@ -62,6 +71,14 @@ public byte[] getSignature() {
return this.signature;
}
+ /**
+ * Get the certificate chain
+ * @return certificate chain
+ */
+ public List getCertificateChain() {
+ return this.certChain;
+ }
+
/**
* Get the signature as base64 String
* @return signature base64
@@ -112,4 +129,19 @@ public String getCertificateB64() {
return Base64.getEncoder().encodeToString(cert);
}
+ /**
+ * Get the certificate chain
+ * @param resp ETSI TS 102 204 response
+ * @return Certificate chain
+ */
+ private List getCertificateChain(EtsiResponse resp) {
+ return resp.getPkcs7Signature().getCertificateChain().stream().map(c -> {
+ try {
+ return c.getEncoded();
+ } catch (CertificateEncodingException e) {
+ return null;
+ }
+ }).filter(Objects::nonNull).collect(Collectors.toList());
+ }
+
}
diff --git a/src/main/java/fi/methics/webapp/musaplink/util/etsi204/Etsi204RestClient.java b/src/main/java/fi/methics/webapp/musaplink/util/etsi204/Etsi204RestClient.java
index 32917ad..fea5efa 100644
--- a/src/main/java/fi/methics/webapp/musaplink/util/etsi204/Etsi204RestClient.java
+++ b/src/main/java/fi/methics/webapp/musaplink/util/etsi204/Etsi204RestClient.java
@@ -1,11 +1,18 @@
package fi.methics.webapp.musaplink.util.etsi204;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import fi.laverca.ficom.FiComAdditionalServices;
+import fi.laverca.jaxb.mss.AdditionalServiceType;
import fi.methics.laverca.rest.MssClient;
+import fi.methics.laverca.rest.json.AdditionalService;
+import fi.methics.laverca.rest.util.DTBS;
+import fi.methics.laverca.rest.util.MSS_SignatureReqBuilder;
import fi.methics.laverca.rest.util.MssRestException;
import fi.methics.laverca.rest.util.SignatureProfile;
@@ -48,6 +55,25 @@ public Etsi204Response sign(final String msisdn,
SignatureProfile sigprof = SignatureProfile.of(this.getSignatureProfile());
log.debug("Sending a signature request for MSISDN " + msisdn + " and SignatureProfile " + sigprof.getUri());
+
+ MSS_SignatureReqBuilder builder = new MSS_SignatureReqBuilder();
+ builder.withMsisdn(msisdn);
+ builder.withDtbd(dtbd);
+ builder.withDtbs(new DTBS(dtbs, "base64", mimeType));
+ builder.withSignatureProfile(sigprof);
+
+ if (this.enableEventId) {
+ String eventid = this.resolveEventId(transid, attrs);
+ builder.withAdditionalService(AdditionalService.createEventIdService(eventid));
+ }
+ if (this.enableNoSpamCode) {
+ String nospamcode = this.resolveNospamCode(attrs);
+ boolean validate = nospamcode != null;
+ builder.withAdditionalService(AdditionalService.createNoSpamCodeService(validate, nospamcode));
+ }
+
+ this.client.sign(builder.build());
+
byte[] signature = this.client.sign(msisdn, dtbd, dtbs, mimeType, sigprof);
return new Etsi204Response(signature);
} catch (MssRestException e) {
diff --git a/src/main/java/fi/methics/webapp/musaplink/util/etsi204/Etsi204SoapClient.java b/src/main/java/fi/methics/webapp/musaplink/util/etsi204/Etsi204SoapClient.java
index bb227b5..544ff54 100644
--- a/src/main/java/fi/methics/webapp/musaplink/util/etsi204/Etsi204SoapClient.java
+++ b/src/main/java/fi/methics/webapp/musaplink/util/etsi204/Etsi204SoapClient.java
@@ -45,10 +45,13 @@ public Etsi204Response sign(final String msisdn,
List additionalServices = new ArrayList<>();
if (this.enableEventId) {
- additionalServices.add(FiComAdditionalServices.createEventIdService(transid));
+ String eventid = this.resolveEventId(transid, attrs);
+ additionalServices.add(FiComAdditionalServices.createEventIdService(eventid));
}
if (this.enableNoSpamCode) {
- additionalServices.add(FiComAdditionalServices.createNoSpamService(null, false));
+ String nospamcode = this.resolveNospamCode(attrs);
+ boolean validate = nospamcode != null;
+ additionalServices.add(FiComAdditionalServices.createNoSpamService(nospamcode, validate));
}
String mimeType = attrs.get(ATTR_MIMETYPE);
@@ -69,4 +72,5 @@ public Etsi204Response sign(final String msisdn,
}
}
+
}