Complete token generation and exchange between ACS and externalApp
This commit is contained in:
@@ -28,15 +28,16 @@ public class Main {
|
|||||||
SSLContext ctx = SSLContext.getInstance("TLS");
|
SSLContext ctx = SSLContext.getInstance("TLS");
|
||||||
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
||||||
|
|
||||||
//requestACS(ctx);
|
requestACS(ctx, ACS_SERVER_PORT);
|
||||||
|
requestACS(ctx, AUTH_PORT);
|
||||||
|
|
||||||
Thread ACQServer = SSLServerFactory.createServer(ctx, ACQ_SERVER_PORT, Main::handleRequest);
|
Thread ACQServer = SSLServerFactory.createServer(ctx, ACQ_SERVER_PORT, Main::handleRequest);
|
||||||
ACQServer.start();
|
ACQServer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void requestACS(SSLContext ctx) {
|
public static void requestACS(SSLContext ctx, int port) {
|
||||||
SSLSocketFactory factory = ctx.getSocketFactory();
|
SSLSocketFactory factory = ctx.getSocketFactory();
|
||||||
try (SSLSocket socket = (SSLSocket) factory.createSocket(Main.HOST, ACS_SERVER_PORT)) {
|
try (SSLSocket socket = (SSLSocket) factory.createSocket(Main.HOST, port)) {
|
||||||
socket.startHandshake();
|
socket.startHandshake();
|
||||||
|
|
||||||
String message = "Hello ACS";
|
String message = "Hello ACS";
|
||||||
@@ -50,6 +51,9 @@ public class Main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static void handleRequest(SSLSocket serverSocket) {
|
public static void handleRequest(SSLSocket serverSocket) {
|
||||||
Logger.displayInfo("Request handled");
|
Logger.displayInfo("Request handled");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
package acs.acs.src;
|
package acs.acs.src;
|
||||||
|
|
||||||
|
import acs.acs.src.json.Payload;
|
||||||
import common.common.src.crypto.KeyFactory;
|
import common.common.src.crypto.KeyFactory;
|
||||||
|
import common.common.src.crypto.CryptoUtils;
|
||||||
|
import common.common.src.json.JsonManager;
|
||||||
import common.common.src.logger.Logger;
|
import common.common.src.logger.Logger;
|
||||||
import common.common.src.requestHandlers.SSLServerFactory;
|
import common.common.src.requestHandlers.SSLServerFactory;
|
||||||
import common.common.src.socket.SocketManager;
|
import common.common.src.socket.SocketManager;
|
||||||
|
|
||||||
import javax.net.ssl.*;
|
import javax.net.ssl.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.*;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static common.common.src.ports.Ports.ACS_SERVER_PORT;
|
import static common.common.src.ports.Ports.ACS_SERVER_PORT;
|
||||||
import static common.common.src.ports.Ports.PORT_AUTH;
|
import static common.common.src.ports.Ports.AUTH_PORT;
|
||||||
|
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
@@ -20,6 +26,8 @@ public class Main {
|
|||||||
private static final String TRUST_STORE_PATH = "assets/certs/acs/acs.truststore.p12";
|
private static final String TRUST_STORE_PATH = "assets/certs/acs/acs.truststore.p12";
|
||||||
private static final String TRUST_STORE_PWD = "hepl_truststore";
|
private static final String TRUST_STORE_PWD = "hepl_truststore";
|
||||||
|
|
||||||
|
private static String generatedToken;
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
|
||||||
KeyFactory loader = new KeyFactory();
|
KeyFactory loader = new KeyFactory();
|
||||||
@@ -27,11 +35,12 @@ public class Main {
|
|||||||
KeyManagerFactory kmf = loader.loadKeyStore(KEY_STORE_PATH, KEY_STORE_PWD);
|
KeyManagerFactory kmf = loader.loadKeyStore(KEY_STORE_PATH, KEY_STORE_PWD);
|
||||||
TrustManagerFactory tmf = loader.loadTrustStore(TRUST_STORE_PATH, TRUST_STORE_PWD);
|
TrustManagerFactory tmf = loader.loadTrustStore(TRUST_STORE_PATH, TRUST_STORE_PWD);
|
||||||
|
|
||||||
|
|
||||||
SSLContext ctx = SSLContext.getInstance("TLS");
|
SSLContext ctx = SSLContext.getInstance("TLS");
|
||||||
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
||||||
|
|
||||||
Thread serverThread = SSLServerFactory.createServer(ctx, ACS_SERVER_PORT, Main::handleRequest);
|
Thread serverThread = SSLServerFactory.createServer(ctx, ACS_SERVER_PORT, Main::handleRequest);
|
||||||
Thread authThread = SSLServerFactory.createServer(ctx, PORT_AUTH, Main::handleAuth);
|
Thread authThread = SSLServerFactory.createServer(ctx, AUTH_PORT, Main::handleAuth);
|
||||||
|
|
||||||
serverThread.start();
|
serverThread.start();
|
||||||
authThread.start();
|
authThread.start();
|
||||||
@@ -53,11 +62,30 @@ public class Main {
|
|||||||
|
|
||||||
private static void handleAuth(SSLSocket clientSocket) {
|
private static void handleAuth(SSLSocket clientSocket) {
|
||||||
try{
|
try{
|
||||||
String response = SocketManager.readResponse(clientSocket);
|
String msg = SocketManager.readResponse(clientSocket);
|
||||||
Logger.displayReceived(response);
|
Logger.displayReceived(msg);
|
||||||
SocketManager.send(clientSocket, "TOKEN_HERE");
|
|
||||||
} catch (IOException e) {
|
Payload payload = JsonManager.deserialize(msg, Payload.class);
|
||||||
throw new RuntimeException(e);
|
PublicKey publicKey = CryptoUtils.getPublicKey(TRUST_STORE_PATH, TRUST_STORE_PWD, "externalApp");
|
||||||
|
|
||||||
|
byte[] signatureBytes = Base64.getDecoder().decode(payload.signature());
|
||||||
|
if (!CryptoUtils.checkSignature(publicKey, payload.data(), signatureBytes)) {
|
||||||
|
clientSocket.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.displayInfo("Signature OK");
|
||||||
|
|
||||||
|
PrivateKey privateKey = CryptoUtils.getPrivateKey(KEY_STORE_PATH, KEY_STORE_PWD, "acs");
|
||||||
|
String token = CryptoUtils.generateToken();
|
||||||
|
String signedToken = CryptoUtils.signData(privateKey, token);
|
||||||
|
|
||||||
|
Map<String, Object> data = Map.of("token", token, "signature", signedToken);
|
||||||
|
String response = JsonManager.serialize(data);
|
||||||
|
SocketManager.send(clientSocket, response);
|
||||||
|
generatedToken = token;
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw new RuntimeException(ioe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
10
src/main/java/acs/acs/src/json/CreditCard.java
Normal file
10
src/main/java/acs/acs/src/json/CreditCard.java
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package acs.acs.src.json;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
public record CreditCard(
|
||||||
|
@JsonProperty("cardNumber")
|
||||||
|
String number,
|
||||||
|
@JsonProperty("expirationDate")
|
||||||
|
String expirationDate) {
|
||||||
|
}
|
||||||
10
src/main/java/acs/acs/src/json/Payload.java
Normal file
10
src/main/java/acs/acs/src/json/Payload.java
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package acs.acs.src.json;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
public record Payload(
|
||||||
|
@JsonProperty("data")
|
||||||
|
String data,
|
||||||
|
@JsonProperty("signature")
|
||||||
|
String signature) {
|
||||||
|
}
|
||||||
63
src/main/java/common/common/src/crypto/CryptoUtils.java
Normal file
63
src/main/java/common/common/src/crypto/CryptoUtils.java
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package common.common.src.crypto;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
public class CryptoUtils {
|
||||||
|
|
||||||
|
public static String signData(PrivateKey privateKey, String plainData) {
|
||||||
|
try {
|
||||||
|
Signature signature = Signature.getInstance("SHA256withRSA");
|
||||||
|
signature.initSign(privateKey);
|
||||||
|
signature.update(plainData.getBytes(StandardCharsets.UTF_8));
|
||||||
|
byte[] signedBytes = signature.sign();
|
||||||
|
|
||||||
|
return Base64.getEncoder().encodeToString(signedBytes);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean checkSignature(PublicKey publicKey, String data, byte[] signature) {
|
||||||
|
try {
|
||||||
|
Signature sig = Signature.getInstance("SHA256withRSA");
|
||||||
|
sig.initVerify(publicKey);
|
||||||
|
|
||||||
|
sig.update(data.getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
return sig.verify(signature);
|
||||||
|
} catch (Exception e){
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String generateToken(){
|
||||||
|
byte[] bytes = new byte[32];
|
||||||
|
new SecureRandom().nextBytes(bytes);
|
||||||
|
return Base64.getEncoder().encodeToString(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrivateKey getPrivateKey(String keyStorePath, String keyStorePwd, String alias) {
|
||||||
|
try {
|
||||||
|
KeyFactory keyFactory = new KeyFactory();
|
||||||
|
KeyStore keyStore = keyFactory.createStore("PKCS12", keyStorePath, keyStorePwd);
|
||||||
|
return keyFactory.createPrivateKey(keyStore, alias, keyStorePwd);
|
||||||
|
} catch (Exception e){
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PublicKey getPublicKey(String trustStorePath, String truStorePwd, String alias) {
|
||||||
|
try {
|
||||||
|
KeyFactory loader = new KeyFactory();
|
||||||
|
KeyStore trustStore = loader.createStore("PKCS12", trustStorePath, truStorePwd);
|
||||||
|
Certificate cert = trustStore.getCertificate(alias);
|
||||||
|
return cert.getPublicKey();
|
||||||
|
} catch (Exception e){
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ public class KeyFactory {
|
|||||||
return (PrivateKey) keyStore.getKey(alias, keystorePwd.toCharArray());
|
return (PrivateKey) keyStore.getKey(alias, keystorePwd.toCharArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyStore createKeyStore(String algorithm, String keyStorePath, String pwd){
|
public KeyStore createStore(String algorithm, String keyStorePath, String pwd){
|
||||||
try (FileInputStream fis = new FileInputStream(keyStorePath)) {
|
try (FileInputStream fis = new FileInputStream(keyStorePath)) {
|
||||||
char[] keystorePass = pwd.toCharArray();
|
char[] keystorePass = pwd.toCharArray();
|
||||||
|
|
||||||
@@ -25,40 +25,26 @@ public class KeyFactory {
|
|||||||
|
|
||||||
public KeyManagerFactory loadKeyStore(String path, String pwd) {
|
public KeyManagerFactory loadKeyStore(String path, String pwd) {
|
||||||
|
|
||||||
KeyManagerFactory kmf = null;
|
try{
|
||||||
|
KeyStore keyStore = createStore("PKCS12", path, pwd);
|
||||||
try (FileInputStream fis = new FileInputStream(path)) {
|
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
|
||||||
char[] keystorePass = pwd.toCharArray();
|
kmf.init(keyStore, pwd.toCharArray());
|
||||||
|
return kmf;
|
||||||
KeyStore ks = KeyStore.getInstance("PKCS12");
|
} catch (Exception e){
|
||||||
ks.load(fis, keystorePass);
|
|
||||||
|
|
||||||
kmf = KeyManagerFactory.getInstance("SunX509");
|
|
||||||
kmf.init(ks, keystorePass);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return kmf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TrustManagerFactory loadTrustStore(String path, String pwd) {
|
public TrustManagerFactory loadTrustStore(String path, String pwd) {
|
||||||
|
|
||||||
TrustManagerFactory tmf = null;
|
try{
|
||||||
|
KeyStore ts = createStore("PKCS12", path, pwd);
|
||||||
try (FileInputStream fis = new FileInputStream(path)) {
|
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
|
||||||
char[] truststorePass = pwd.toCharArray();
|
|
||||||
|
|
||||||
KeyStore ts = KeyStore.getInstance("PKCS12");
|
|
||||||
ts.load(fis, truststorePass);
|
|
||||||
|
|
||||||
tmf = TrustManagerFactory.getInstance("SunX509");
|
|
||||||
tmf.init(ts);
|
tmf.init(ts);
|
||||||
|
return tmf;
|
||||||
} catch (Exception e){
|
} catch (Exception e){
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return tmf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
33
src/main/java/common/common/src/json/JsonManager.java
Normal file
33
src/main/java/common/common/src/json/JsonManager.java
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package common.common.src.json;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class JsonManager {
|
||||||
|
|
||||||
|
public static <T> T deserialize(String jsonString, Class<T> clazz) {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
try {
|
||||||
|
return mapper.readValue(jsonString, clazz);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String serialize(Map<String, Object> map) {
|
||||||
|
try {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
ObjectNode json = mapper.createObjectNode();
|
||||||
|
map.forEach((k, v) -> {
|
||||||
|
json.put(k, v.toString());
|
||||||
|
});
|
||||||
|
return mapper.writeValueAsString(json);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ public class Ports {
|
|||||||
/**
|
/**
|
||||||
* Port d'écoute du service ACS pour la communication avec l'application externe.
|
* Port d'écoute du service ACS pour la communication avec l'application externe.
|
||||||
*/
|
*/
|
||||||
public static int PORT_AUTH = 8786;
|
public static int AUTH_PORT = 8786;
|
||||||
public static final int HTTP_SERVER_PORT = 8043;
|
public static final int HTTP_SERVER_PORT = 8043;
|
||||||
public static final int ACS_SERVER_PORT = 8443;
|
public static final int ACS_SERVER_PORT = 8443;
|
||||||
public static final int ACQ_SERVER_PORT = 8543;
|
public static final int ACQ_SERVER_PORT = 8543;
|
||||||
|
|||||||
@@ -1,25 +1,26 @@
|
|||||||
package externalApp.externalApp.src;
|
package externalApp.externalApp.src;
|
||||||
|
|
||||||
|
import common.common.src.crypto.CryptoUtils;
|
||||||
import common.common.src.crypto.KeyFactory;
|
import common.common.src.crypto.KeyFactory;
|
||||||
import common.common.src.ports.Ports;
|
import common.common.src.json.JsonManager;
|
||||||
import common.common.src.logger.Logger;
|
import common.common.src.logger.Logger;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import common.common.src.socket.SocketManager;
|
import common.common.src.socket.SocketManager;
|
||||||
|
import externalApp.externalApp.src.json.Payload;
|
||||||
|
|
||||||
import javax.net.ssl.*;
|
import javax.net.ssl.*;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.*;
|
import java.security.*;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static common.common.src.ports.Ports.ACS_HOST;
|
import static common.common.src.ports.Ports.*;
|
||||||
import static common.common.src.ports.Ports.ACS_SERVER_PORT;
|
|
||||||
|
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
@@ -42,7 +43,7 @@ public class Main {
|
|||||||
|
|
||||||
KeyFactory keyFactory = new KeyFactory();
|
KeyFactory keyFactory = new KeyFactory();
|
||||||
|
|
||||||
KeyStore keyStore = keyFactory.createKeyStore("PKCS12", KEY_STORE_PATH, KEY_STORE_PWD);
|
KeyStore keyStore = keyFactory.createStore("PKCS12", KEY_STORE_PATH, KEY_STORE_PWD);
|
||||||
KeyManagerFactory kmf = keyFactory.loadKeyStore(KEY_STORE_PATH, KEY_STORE_PWD);
|
KeyManagerFactory kmf = keyFactory.loadKeyStore(KEY_STORE_PATH, KEY_STORE_PWD);
|
||||||
TrustManagerFactory tmf = keyFactory.loadTrustStore(TRUST_STORE_PATH, TRUST_STORE_PWD);
|
TrustManagerFactory tmf = keyFactory.loadTrustStore(TRUST_STORE_PATH, TRUST_STORE_PWD);
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@ public class Main {
|
|||||||
SSLContext ctx = SSLContext.getInstance("TLS");
|
SSLContext ctx = SSLContext.getInstance("TLS");
|
||||||
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
||||||
|
|
||||||
sendToACS(ctx, finalPayload);
|
sendToACS(ctx, finalPayload, AUTH_PORT);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,11 +93,10 @@ public class Main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void sendToACS(SSLContext ctx, String payload) {
|
private static void sendToACS(SSLContext ctx, String payload, int port) {
|
||||||
Logger.displayInfo("Envoi des informations au service d'authentification à l'hôte " + Ports.ACS_HOST + ":" + Ports.PORT_AUTH);
|
|
||||||
|
|
||||||
SSLSocketFactory factory = ctx.getSocketFactory();
|
SSLSocketFactory factory = ctx.getSocketFactory();
|
||||||
try (SSLSocket socket = (SSLSocket) factory.createSocket(ACS_HOST, ACS_SERVER_PORT)) {
|
try (SSLSocket socket = (SSLSocket) factory.createSocket(ACS_HOST, port)) {
|
||||||
socket.startHandshake();
|
socket.startHandshake();
|
||||||
|
|
||||||
SocketManager.send(socket, payload);
|
SocketManager.send(socket, payload);
|
||||||
@@ -104,10 +104,19 @@ public class Main {
|
|||||||
|
|
||||||
String response = SocketManager.readResponse(socket);
|
String response = SocketManager.readResponse(socket);
|
||||||
Logger.displayReceived(response);
|
Logger.displayReceived(response);
|
||||||
|
|
||||||
|
PublicKey publicKey = CryptoUtils.getPublicKey(TRUST_STORE_PATH, TRUST_STORE_PWD, "acs");
|
||||||
|
Payload responsePayload = JsonManager.deserialize(response, Payload.class);
|
||||||
|
|
||||||
|
byte[] signatureBytes = Base64.getDecoder().decode(responsePayload.signature());
|
||||||
|
if(CryptoUtils.checkSignature(publicKey, responsePayload.token(), signatureBytes)){
|
||||||
|
Logger.displayInfo(responsePayload.token());
|
||||||
|
} else {
|
||||||
|
Logger.displayInfo("Token NOK");
|
||||||
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/main/java/externalApp/externalApp/src/json/Payload.java
Normal file
10
src/main/java/externalApp/externalApp/src/json/Payload.java
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package externalApp.externalApp.src.json;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
public record Payload(
|
||||||
|
@JsonProperty("token")
|
||||||
|
String token,
|
||||||
|
@JsonProperty("signature")
|
||||||
|
String signature) {
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user