Added factory for ACME protocol
This commit is contained in:
parent
4955731abc
commit
708577debf
8 changed files with 192 additions and 58 deletions
|
|
@ -27,7 +27,7 @@ package zutil.math;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Very Simple math functions
|
* Very Simple math functions
|
||||||
*
|
*
|
||||||
* @author Ziver
|
* @author Ziver
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
22
src/zutil/net/acme/AcmeChallengeFactory.java
Normal file
22
src/zutil/net/acme/AcmeChallengeFactory.java
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
package zutil.net.acme;
|
||||||
|
|
||||||
|
import org.shredzone.acme4j.Authorization;
|
||||||
|
import org.shredzone.acme4j.challenge.Challenge;
|
||||||
|
import org.shredzone.acme4j.exception.AcmeException;
|
||||||
|
|
||||||
|
public interface AcmeChallengeFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Challenge object and do any needed actions for the challenge to proceed.
|
||||||
|
*
|
||||||
|
* @param authorization
|
||||||
|
* @return a Challenge object that will be used to authorize a domain.
|
||||||
|
* @throws AcmeException in case of any issues
|
||||||
|
*/
|
||||||
|
Challenge createChallenge(Authorization authorization) throws AcmeException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do any needed cleanup after challenge has completed successfully or failed.
|
||||||
|
*/
|
||||||
|
default void postChallengeAction(Challenge challenge) {}
|
||||||
|
}
|
||||||
|
|
@ -17,14 +17,13 @@ import org.shredzone.acme4j.Certificate;
|
||||||
import org.shredzone.acme4j.Order;
|
import org.shredzone.acme4j.Order;
|
||||||
import org.shredzone.acme4j.Session;
|
import org.shredzone.acme4j.Session;
|
||||||
import org.shredzone.acme4j.Status;
|
import org.shredzone.acme4j.Status;
|
||||||
import org.shredzone.acme4j.challenge.Http01Challenge;
|
import org.shredzone.acme4j.challenge.Challenge;
|
||||||
import org.shredzone.acme4j.exception.AcmeException;
|
import org.shredzone.acme4j.exception.AcmeException;
|
||||||
import org.shredzone.acme4j.util.CSRBuilder;
|
import org.shredzone.acme4j.util.CSRBuilder;
|
||||||
import org.shredzone.acme4j.util.KeyPairUtils;
|
import org.shredzone.acme4j.util.KeyPairUtils;
|
||||||
import zutil.StringUtil;
|
import zutil.StringUtil;
|
||||||
import zutil.log.LogUtil;
|
import zutil.log.LogUtil;
|
||||||
import zutil.net.http.HttpServer;
|
import zutil.net.http.HttpServer;
|
||||||
import zutil.net.http.page.HttpStaticContentPage;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class implementing the ACME protocol (Automatic Certificate Management Environment) used by the LetsEncrypt service.
|
* A class implementing the ACME protocol (Automatic Certificate Management Environment) used by the LetsEncrypt service.
|
||||||
|
|
@ -43,31 +42,35 @@ public class AcmeClient {
|
||||||
|
|
||||||
private String acmeServerUrl;
|
private String acmeServerUrl;
|
||||||
private AcmeDataStore dataStore;
|
private AcmeDataStore dataStore;
|
||||||
|
private AcmeChallengeFactory challengeFactory;
|
||||||
|
|
||||||
|
|
||||||
|
public AcmeClient(AcmeDataStore dataStore, HttpServer httpServer) {
|
||||||
|
this(dataStore, new AcmeHttpChallengeFactory(httpServer), ACME_SERVER_LETSENCRYPT_PRODUCTION);
|
||||||
|
}
|
||||||
|
public AcmeClient(AcmeDataStore dataStore, AcmeChallengeFactory challengeFactory) {
|
||||||
|
this(dataStore, challengeFactory, ACME_SERVER_LETSENCRYPT_PRODUCTION);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Create a new instance of the ACME Client
|
* Create a new instance of the ACME Client
|
||||||
*/
|
*/
|
||||||
public AcmeClient(AcmeDataStore dataStore, String acmeServerUrl) {
|
public AcmeClient(AcmeDataStore dataStore, AcmeChallengeFactory challengeFactory, String acmeServerUrl) {
|
||||||
Security.addProvider(new BouncyCastleProvider());
|
Security.addProvider(new BouncyCastleProvider());
|
||||||
|
|
||||||
this.dataStore = dataStore;
|
this.dataStore = dataStore;
|
||||||
|
this.challengeFactory = challengeFactory;
|
||||||
this.acmeServerUrl = acmeServerUrl;
|
this.acmeServerUrl = acmeServerUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AcmeClient(AcmeDataStore dataStore) {
|
|
||||||
this(dataStore, ACME_SERVER_LETSENCRYPT_PRODUCTION);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a certificate for the given domains. Also takes care for the registration
|
* Generates a certificate for the given domains. Also takes care for the registration
|
||||||
* process.
|
* process.
|
||||||
*
|
*
|
||||||
* @param httpServer the web server where the challenge and response will be performed on and where the certificate will be applied to.
|
|
||||||
* @param domains the domains to get a certificates for
|
* @param domains the domains to get a certificates for
|
||||||
* @return a certificate for the given domains.
|
* @return a certificate for the given domains.
|
||||||
*/
|
*/
|
||||||
public X509Certificate fetchCertificate(HttpServer httpServer, String... domains) throws IOException, AcmeException {
|
public X509Certificate fetchCertificate(String... domains) throws IOException, AcmeException {
|
||||||
// ------------------------------------------------
|
// ------------------------------------------------
|
||||||
// Read in keys
|
// Read in keys
|
||||||
// ------------------------------------------------
|
// ------------------------------------------------
|
||||||
|
|
@ -94,7 +97,10 @@ public class AcmeClient {
|
||||||
|
|
||||||
// Perform all required authorizations
|
// Perform all required authorizations
|
||||||
for (Authorization auth : order.getAuthorizations()) {
|
for (Authorization auth : order.getAuthorizations()) {
|
||||||
execHttpChallenge(auth, httpServer);
|
if (auth.getStatus() == Status.VALID)
|
||||||
|
continue; // The authorization is already valid. No need to process a challenge.
|
||||||
|
|
||||||
|
execDomainChallenge(auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a "Certificate Signing Request" for all of the domains, and sign it with the domain key pair.
|
// Generate a "Certificate Signing Request" for all of the domains, and sign it with the domain key pair.
|
||||||
|
|
@ -106,7 +112,7 @@ public class AcmeClient {
|
||||||
|
|
||||||
// Wait for the order to complete
|
// Wait for the order to complete
|
||||||
try {
|
try {
|
||||||
for (int attempts = 0; attempts < 10; attempts--) {
|
for (int attempts = 0; attempts < 10; attempts++) {
|
||||||
// Did the order pass or fail?
|
// Did the order pass or fail?
|
||||||
if (order.getStatus() == Status.VALID) {
|
if (order.getStatus() == Status.VALID) {
|
||||||
break;
|
break;
|
||||||
|
|
@ -115,7 +121,9 @@ public class AcmeClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for a few seconds
|
// Wait for a few seconds
|
||||||
Thread.sleep(100L + 500L * attempts);
|
long sleep = 100L + 1000L * attempts;
|
||||||
|
logger.fine("Challenge not yet completed, sleeping for: " + StringUtil.formatTimeToString(sleep));
|
||||||
|
Thread.sleep(sleep);
|
||||||
|
|
||||||
// Then update the status
|
// Then update the status
|
||||||
order.update();
|
order.update();
|
||||||
|
|
@ -166,49 +174,38 @@ public class AcmeClient {
|
||||||
* Authorize a domain. It will be associated with your account, so you will be able to
|
* Authorize a domain. It will be associated with your account, so you will be able to
|
||||||
* retrieve a signed certificate for the domain later.
|
* retrieve a signed certificate for the domain later.
|
||||||
*
|
*
|
||||||
* @param auth {@link Authorization} to perform
|
* @param authorization {@link Authorization} to perform
|
||||||
*/
|
*/
|
||||||
private void execHttpChallenge(Authorization auth, HttpServer httpServer) throws AcmeException {
|
private void execDomainChallenge(Authorization authorization) throws AcmeException {
|
||||||
logger.info("Authorization for domain: " + auth.getIdentifier().getDomain());
|
logger.info("Authorization for domain: " + authorization.getIdentifier().getDomain());
|
||||||
|
Challenge challenge = null;
|
||||||
|
|
||||||
// The authorization is already valid. No need to process a challenge.
|
|
||||||
if (auth.getStatus() == Status.VALID) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the desired challenge and prepare it.
|
|
||||||
Http01Challenge challenge = auth.findChallenge(Http01Challenge.class);
|
|
||||||
if (challenge == null) {
|
|
||||||
throw new AcmeException("Found no " + Http01Challenge.TYPE + " challenge.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the challenge is already verified, there's no need to execute it again.
|
|
||||||
if (challenge.getStatus() == Status.VALID)
|
|
||||||
return;
|
|
||||||
|
|
||||||
String url = "http://" + auth.getIdentifier().getDomain();
|
|
||||||
String path = "/.well-known/acme-challenge/" + challenge.getToken();
|
|
||||||
String content = challenge.getAuthorization();
|
|
||||||
|
|
||||||
// Output the challenge, wait for acknowledge...
|
|
||||||
logger.fine("Adding challenge HttpPage at: " + url + path);
|
|
||||||
httpServer.setPage(path, new HttpStaticContentPage(content));
|
|
||||||
|
|
||||||
// Now trigger the challenge.
|
|
||||||
challenge.trigger();
|
|
||||||
|
|
||||||
// Poll for the challenge to complete.
|
|
||||||
try {
|
try {
|
||||||
for (int attempts = 0; attempts < 10; attempts--) {
|
challenge = challengeFactory.createChallenge(authorization);
|
||||||
|
|
||||||
|
// If the challenge is already verified, there's no need to execute it again.
|
||||||
|
if (challenge.getStatus() == Status.VALID)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Now trigger the challenge.
|
||||||
|
challenge.trigger();
|
||||||
|
|
||||||
|
// Poll for the challenge to complete.
|
||||||
|
|
||||||
|
for (int attempts = 0; attempts < 30; attempts++) {
|
||||||
// Did the authorization fail?
|
// Did the authorization fail?
|
||||||
if (challenge.getStatus() == Status.VALID) {
|
if (challenge.getStatus() == Status.VALID) {
|
||||||
break;
|
break;
|
||||||
} else if (challenge.getStatus() == Status.INVALID) {
|
} else if (challenge.getStatus() == Status.INVALID) {
|
||||||
throw new AcmeException("Certificate challenge failed: " + challenge.getError());
|
//throw new AcmeException("Certificate challenge failed: " + challenge.getError());
|
||||||
|
logger.severe("Certificate challenge failed: " + challenge.getError());
|
||||||
|
challenge.trigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for a few seconds
|
// Wait for a few seconds
|
||||||
Thread.sleep(100L + 500L * attempts);
|
long sleep = 100L + 5000L * attempts;
|
||||||
|
logger.fine("Challenge not yet completed, sleeping for: " + StringUtil.formatTimeToString(sleep));
|
||||||
|
Thread.sleep(sleep);
|
||||||
|
|
||||||
// Then update the status
|
// Then update the status
|
||||||
challenge.update();
|
challenge.update();
|
||||||
|
|
@ -216,12 +213,14 @@ public class AcmeClient {
|
||||||
|
|
||||||
// All reattempts are used up and there is still no valid authorization?
|
// All reattempts are used up and there is still no valid authorization?
|
||||||
if (challenge.getStatus() != Status.VALID)
|
if (challenge.getStatus() != Status.VALID)
|
||||||
throw new AcmeException("Failed to pass the challenge for domain " + auth.getIdentifier().getDomain());
|
throw new AcmeException("Failed to pass the challenge for domain " + authorization.getIdentifier().getDomain());
|
||||||
} catch (InterruptedException ex) {
|
} catch (InterruptedException ex) {
|
||||||
logger.log(Level.SEVERE, "Interrupted", ex);
|
logger.log(Level.SEVERE, "Interrupted", ex);
|
||||||
} finally {
|
} finally {
|
||||||
// Cleanup
|
// Cleanup
|
||||||
httpServer.removePage(path);
|
challengeFactory.postChallengeAction(challenge);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.fine("Domain challenge executed successfully.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,6 @@ public class AcmeFileDataStore implements AcmeDataStore {
|
||||||
|
|
||||||
private final File userKeyFile;
|
private final File userKeyFile;
|
||||||
private final File domainKeyFile;
|
private final File domainKeyFile;
|
||||||
private final File domainCsrFile;
|
|
||||||
private final File domainChainFile;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -21,8 +19,6 @@ public class AcmeFileDataStore implements AcmeDataStore {
|
||||||
public AcmeFileDataStore(File folder) {
|
public AcmeFileDataStore(File folder) {
|
||||||
this.userKeyFile = new File(folder, "user.key");
|
this.userKeyFile = new File(folder, "user.key");
|
||||||
this.domainKeyFile = new File(folder, "domain.key");
|
this.domainKeyFile = new File(folder, "domain.key");
|
||||||
this.domainCsrFile = new File(folder, "domain.csr");
|
|
||||||
this.domainChainFile = new File(folder, "domain-chain.crt");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
55
src/zutil/net/acme/AcmeHttpChallengeFactory.java
Normal file
55
src/zutil/net/acme/AcmeHttpChallengeFactory.java
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
package zutil.net.acme;
|
||||||
|
|
||||||
|
import org.shredzone.acme4j.Authorization;
|
||||||
|
import org.shredzone.acme4j.challenge.Challenge;
|
||||||
|
import org.shredzone.acme4j.challenge.Http01Challenge;
|
||||||
|
import org.shredzone.acme4j.exception.AcmeException;
|
||||||
|
import zutil.log.LogUtil;
|
||||||
|
import zutil.net.http.HttpServer;
|
||||||
|
import zutil.net.http.HttpURL;
|
||||||
|
import zutil.net.http.page.HttpStaticContentPage;
|
||||||
|
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class implementing HTTP based challenge logic of the ACME protocol through the Zutil HttpServer.
|
||||||
|
*/
|
||||||
|
public class AcmeHttpChallengeFactory implements AcmeChallengeFactory {
|
||||||
|
private static final Logger logger = LogUtil.getLogger();
|
||||||
|
|
||||||
|
private HttpServer httpServer;
|
||||||
|
private HttpURL url;
|
||||||
|
|
||||||
|
|
||||||
|
public AcmeHttpChallengeFactory(HttpServer httpServer) {
|
||||||
|
this.httpServer = httpServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Challenge createChallenge(Authorization authorization) throws AcmeException {
|
||||||
|
Http01Challenge challenge = authorization.findChallenge(Http01Challenge.class);
|
||||||
|
if (challenge == null) {
|
||||||
|
throw new AcmeException("Found no " + Http01Challenge.TYPE + " challenge.");
|
||||||
|
}
|
||||||
|
|
||||||
|
url = new HttpURL();
|
||||||
|
url.setProtocol("http");
|
||||||
|
url.setHost(authorization.getIdentifier().getDomain());
|
||||||
|
url.setPort(httpServer.getPort());
|
||||||
|
url.setPath("/.well-known/acme-challenge/" + challenge.getToken());
|
||||||
|
|
||||||
|
// Output the challenge, wait for acknowledge...
|
||||||
|
logger.fine("Adding challenge HttpPage at: " + url);
|
||||||
|
httpServer.setPage(url.getPath(), new HttpStaticContentPage(challenge.getAuthorization()));
|
||||||
|
|
||||||
|
return challenge;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postChallengeAction(Challenge challenge) {
|
||||||
|
if (url != null) {
|
||||||
|
httpServer.removePage(url.getPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/zutil/net/acme/AcmeManualDnsChallengeFactory.java
Normal file
41
src/zutil/net/acme/AcmeManualDnsChallengeFactory.java
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
package zutil.net.acme;
|
||||||
|
|
||||||
|
import org.shredzone.acme4j.Authorization;
|
||||||
|
import org.shredzone.acme4j.challenge.Challenge;
|
||||||
|
import org.shredzone.acme4j.challenge.Dns01Challenge;
|
||||||
|
import org.shredzone.acme4j.exception.AcmeException;
|
||||||
|
import zutil.log.LogUtil;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class implementing HTTP based challenge logic of the ACME protocol through the Zutil HttpServer.
|
||||||
|
*/
|
||||||
|
public class AcmeManualDnsChallengeFactory implements AcmeChallengeFactory {
|
||||||
|
private static final Logger logger = LogUtil.getLogger();
|
||||||
|
|
||||||
|
private HashMap<String,Dns01Challenge> challengeCache = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Challenge createChallenge(Authorization authorization) throws AcmeException {
|
||||||
|
if (challengeCache.containsKey(authorization.getIdentifier().getDomain()))
|
||||||
|
return challengeCache.get(authorization.getIdentifier().getDomain());
|
||||||
|
|
||||||
|
Dns01Challenge challenge = authorization.findChallenge(Dns01Challenge.class);
|
||||||
|
if (challenge == null) {
|
||||||
|
throw new AcmeException("Found no " + Dns01Challenge.TYPE + " challenge.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify user of the required manual intervention
|
||||||
|
|
||||||
|
logger.warning(
|
||||||
|
"---------------------------- ATTENTION ----------------------------\n" +
|
||||||
|
"For certificate challenge to pass please create a DNS TXT record:\n" +
|
||||||
|
"_acme-challenge." + authorization.getIdentifier().getDomain() + ". IN TXT " + challenge.getDigest() + "\n" +
|
||||||
|
"--------------------------------------------------------------------");
|
||||||
|
|
||||||
|
challengeCache.put(authorization.getIdentifier().getDomain(), challenge);
|
||||||
|
return challenge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -34,6 +34,8 @@ import java.io.BufferedInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
@ -78,11 +80,23 @@ public class HttpServer extends ThreadedTCPNetworkServer{
|
||||||
logger.info("HTTP Server ready and listening to port: " + port);
|
logger.info("HTTP Server ready and listening to port: " + port);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of the sever
|
* Creates a new instance of the sever which accepts SSL connections
|
||||||
*
|
*
|
||||||
* @param port The port that the server should listen to
|
* @param port The port that the server should listen to
|
||||||
* @param keyStore If this is not null then the server will use SSL connection with this keyStore file path
|
* @param certificate The certificate that should be used for the servers SSL connections
|
||||||
* @param keyStorePass If this is not null then the server will use a SSL connection with the given certificate
|
*/
|
||||||
|
public HttpServer(int port, Certificate certificate) throws IOException {
|
||||||
|
super(port, certificate);
|
||||||
|
initGarbageCollector();
|
||||||
|
|
||||||
|
logger.info("HTTPS Server ready and listening to port: " + port);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Creates a new instance of the sever which accepts SSL connections
|
||||||
|
*
|
||||||
|
* @param port The port that the server should listen to
|
||||||
|
* @param keyStore The keyStore containing the certificate to use for the servers SSL connections
|
||||||
|
* @param keyStorePass The password to unlock the key store.
|
||||||
*/
|
*/
|
||||||
public HttpServer(int port, File keyStore, char[] keyStorePass) throws IOException {
|
public HttpServer(int port, File keyStore, char[] keyStorePass) throws IOException {
|
||||||
super(port, keyStore, keyStorePass);
|
super(port, keyStore, keyStorePass);
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import java.io.IOException;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.security.*;
|
import java.security.*;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
@ -81,7 +82,7 @@ public abstract class ThreadedTCPNetworkServer extends Thread {
|
||||||
* @param port the port that the server should listen to.
|
* @param port the port that the server should listen to.
|
||||||
* @param certificate the certificate for the servers domain.
|
* @param certificate the certificate for the servers domain.
|
||||||
*/
|
*/
|
||||||
public ThreadedTCPNetworkServer(int port, X509Certificate certificate) throws IOException {
|
public ThreadedTCPNetworkServer(int port, Certificate certificate) throws IOException {
|
||||||
this.port = port;
|
this.port = port;
|
||||||
this.serverSocket = createSSLSocket(port, certificate);
|
this.serverSocket = createSSLSocket(port, certificate);
|
||||||
}
|
}
|
||||||
|
|
@ -112,7 +113,7 @@ public abstract class ThreadedTCPNetworkServer extends Thread {
|
||||||
* @param certificate the certificate for the servers domain.
|
* @param certificate the certificate for the servers domain.
|
||||||
* @return a SSLServerSocket object
|
* @return a SSLServerSocket object
|
||||||
*/
|
*/
|
||||||
private static ServerSocket createSSLSocket(int port, X509Certificate certificate) throws IOException{
|
private static ServerSocket createSSLSocket(int port, Certificate certificate) throws IOException{
|
||||||
try {
|
try {
|
||||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
keyStore.load(null); // Create empty keystore
|
keyStore.load(null); // Create empty keystore
|
||||||
|
|
@ -144,10 +145,16 @@ public abstract class ThreadedTCPNetworkServer extends Thread {
|
||||||
return socketFactory.createServerSocket(port);
|
return socketFactory.createServerSocket(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the port that this TCP server is listening to.
|
||||||
|
*/
|
||||||
|
public int getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
logger.info("Listening for TCP Connections on port: " + port);
|
logger.info("Accepting TCP Connections on port: " + port);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
Socket connectionSocket = serverSocket.accept();
|
Socket connectionSocket = serverSocket.accept();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue