ACME client will now reuse existing accounts instead of creating new one every time and renamed user variables to account.
This commit is contained in:
parent
98f53adcfb
commit
3552c21404
3 changed files with 86 additions and 53 deletions
|
|
@ -7,7 +7,6 @@ import java.security.KeyPair;
|
|||
import java.security.Security;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
|
@ -65,29 +64,11 @@ public class AcmeClient {
|
|||
this.acmeServerUrl = acmeServerUrl;
|
||||
|
||||
// ------------------------------------------------
|
||||
// Read in keys
|
||||
// ------------------------------------------------
|
||||
|
||||
KeyPair userKeyPair = dataStore.loadUserKeyPair(); // Load the user key file. If there is no key file, create a new one.
|
||||
KeyPair domainKeyPair = dataStore.loadDomainKeyPair(); // Load or create a key pair for the domains. This should not be same as the userKeyPair!
|
||||
|
||||
if (userKeyPair == null) {
|
||||
logger.fine("Creating new user keys.");
|
||||
userKeyPair = KeyPairUtils.createKeyPair(KEY_SIZE);
|
||||
dataStore.storeUserKeyPair(userKeyPair);
|
||||
}
|
||||
if (domainKeyPair == null) {
|
||||
logger.fine("Creating new domain keys.");
|
||||
domainKeyPair = KeyPairUtils.createKeyPair(KEY_SIZE);
|
||||
dataStore.storeDomainKeyPair(domainKeyPair);
|
||||
}
|
||||
|
||||
// ------------------------------------------------
|
||||
// Start user authorization process
|
||||
// Start user authentication process
|
||||
// ------------------------------------------------
|
||||
|
||||
Session session = new Session(acmeServerUrl);
|
||||
acmeAccount = getAccount(session, userKeyPair); // Get the Account. If there is no account yet, create a new one.
|
||||
acmeAccount = getAccount(session); // Get the Account. If there is no account yet, create a new one.
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -138,10 +119,19 @@ public class AcmeClient {
|
|||
execDomainChallenge(challenge);
|
||||
}
|
||||
|
||||
// Load or create a key pair for the domains. This should not be same as the userKeyPair!
|
||||
KeyPair domainKeyPair = dataStore.getDomainKeyPair();
|
||||
|
||||
if (domainKeyPair == null) {
|
||||
logger.fine("Creating new domain keys.");
|
||||
domainKeyPair = KeyPairUtils.createKeyPair(KEY_SIZE);
|
||||
dataStore.storeDomainKeyPair(domainKeyPair);
|
||||
}
|
||||
|
||||
// Generate one "Certificate Signing Request" for all the domains, and sign it with the domain key pair.
|
||||
CSRBuilder csrBuilder = new CSRBuilder();
|
||||
csrBuilder.addDomains(domains);
|
||||
csrBuilder.sign(dataStore.loadDomainKeyPair());
|
||||
csrBuilder.sign(domainKeyPair);
|
||||
|
||||
order.execute(csrBuilder.getEncoded()); // Order the certificate
|
||||
|
||||
|
|
@ -173,6 +163,7 @@ public class AcmeClient {
|
|||
logger.info("The certificate for domains '" + StringUtil.join(",", domains) + "' has been successfully generated.");
|
||||
|
||||
// Cleanup
|
||||
|
||||
order = null;
|
||||
challenges.clear();
|
||||
|
||||
|
|
@ -184,29 +175,42 @@ public class AcmeClient {
|
|||
* Finds your {@link Account} at the ACME server. It will be found by your user's
|
||||
* public key. If your key is not known to the server yet, a new account will be
|
||||
* created.
|
||||
* <p>
|
||||
* This is a simple way of finding your {@link Account}. A better way is to get the
|
||||
* URL of your new account with {@link Account#getLocation()} and store it somewhere.
|
||||
* If you need to get access to your account later, reconnect to it via {@link
|
||||
* Session#login(URL, KeyPair)} by using the stored location.
|
||||
*
|
||||
* @param session {@link Session} to bind with
|
||||
* @return {@link Account}
|
||||
*/
|
||||
private Account getAccount(Session session, KeyPair accountKey) throws AcmeException {
|
||||
private Account getAccount(Session session) throws AcmeException {
|
||||
// Load the account key pair
|
||||
|
||||
URL accountLocation = dataStore.getAccountLocation();
|
||||
KeyPair accountKeyPair = dataStore.getAccountKeyPair();
|
||||
|
||||
// Ask the user to accept the TOS, if server provides us with a link.
|
||||
|
||||
URI tos = session.getMetadata().getTermsOfService();
|
||||
if (tos != null) {
|
||||
logger.info("By using this service you accept the Terms of Service: " + tos);
|
||||
}
|
||||
|
||||
Account account = new AccountBuilder()
|
||||
.agreeToTermsOfService()
|
||||
.useKeyPair(accountKey)
|
||||
.create(session);
|
||||
logger.info("Registered a new user, URL: " + account.getLocation());
|
||||
// Create a new account or login existing user
|
||||
|
||||
return account;
|
||||
if (accountLocation == null || accountKeyPair == null) {
|
||||
logger.fine("Creating new account keys.");
|
||||
accountKeyPair = KeyPairUtils.createKeyPair(KEY_SIZE);
|
||||
|
||||
Account account = new AccountBuilder()
|
||||
.agreeToTermsOfService()
|
||||
.useKeyPair(accountKeyPair)
|
||||
.create(session);
|
||||
|
||||
logger.info("Successfully registered new account, URL: " + account.getLocation());
|
||||
dataStore.storeAccountKeyPair(account.getLocation(), accountKeyPair);
|
||||
return account;
|
||||
} else {
|
||||
logger.info("Logging in existing account: " + accountLocation);
|
||||
Login login = session.login(accountLocation, accountKeyPair);
|
||||
return login.getAccount();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -238,7 +242,7 @@ public class AcmeClient {
|
|||
}
|
||||
|
||||
// Wait for a few seconds
|
||||
long sleep = 100L + 5000L * attempts;
|
||||
long sleep = 100L + 1000L * attempts;
|
||||
logger.fine("Challenge not yet completed, sleeping for: " + StringUtil.formatTimeToString(sleep));
|
||||
Thread.sleep(sleep);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,29 +1,38 @@
|
|||
package zutil.net.acme;
|
||||
|
||||
import java.net.URL;
|
||||
import java.security.KeyPair;
|
||||
|
||||
public interface AcmeDataStore {
|
||||
|
||||
/**
|
||||
* Loads a user key pair.
|
||||
* Loads an accounts key pair.
|
||||
*
|
||||
* @return a KeyPair for the user account, null if no KeyPar was found.
|
||||
* @return a KeyPair for the account, null if no KeyPair is found.
|
||||
*/
|
||||
KeyPair loadUserKeyPair();
|
||||
URL getAccountLocation();
|
||||
|
||||
/**
|
||||
* Stores a user key pair for later usage.
|
||||
* Retrieve an account key pair.
|
||||
*
|
||||
* @param keyPair the keys for the user account
|
||||
* @return a KeyPair object for the account, null if no KeyPair is found.
|
||||
*/
|
||||
void storeUserKeyPair(KeyPair keyPair);
|
||||
KeyPair getAccountKeyPair();
|
||||
|
||||
/**
|
||||
* Stores an accounts key pair for later usage.
|
||||
*
|
||||
* @param accountLocation the URL to the account profile, this is used as an identifier to the ACME service.
|
||||
* @param accountKeyPair the keys for the user account
|
||||
*/
|
||||
void storeAccountKeyPair(URL accountLocation, KeyPair accountKeyPair);
|
||||
|
||||
/**
|
||||
* Loads a domain key pair.
|
||||
*
|
||||
* @return a KeyPair for the domains, null if no KeyPar was found.
|
||||
* @return a KeyPair object for the domains, null if no KeyPar was found.
|
||||
*/
|
||||
KeyPair loadDomainKeyPair();
|
||||
KeyPair getDomainKeyPair();
|
||||
|
||||
/**
|
||||
* Stores a domain key pair for later usage.
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
package zutil.net.acme;
|
||||
|
||||
import org.shredzone.acme4j.util.KeyPairUtils;
|
||||
import zutil.io.file.FileUtil;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.security.KeyPair;
|
||||
|
||||
public class AcmeFileDataStore implements AcmeDataStore {
|
||||
|
||||
private final File userKeyFile;
|
||||
private final File accountLocationFile;
|
||||
private final File accountKeyFile;
|
||||
private final File domainKeyFile;
|
||||
|
||||
|
||||
|
|
@ -17,12 +20,25 @@ public class AcmeFileDataStore implements AcmeDataStore {
|
|||
* @param folder is the folder there the different files should be stored in.
|
||||
*/
|
||||
public AcmeFileDataStore(File folder) {
|
||||
this.userKeyFile = new File(folder, "user.key");
|
||||
this.accountLocationFile = new File(folder, "accountLocation.cfg");
|
||||
this.accountKeyFile = new File(folder, "account.key");
|
||||
this.domainKeyFile = new File(folder, "domain.key");
|
||||
}
|
||||
|
||||
|
||||
public KeyPair loadUserKeyPair(){
|
||||
@Override
|
||||
public URL getAccountLocation() {
|
||||
if (accountKeyFile.exists()) {
|
||||
try {
|
||||
return new URL(FileUtil.getContent(accountKeyFile));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public KeyPair getAccountKeyPair(){
|
||||
if (domainKeyFile.exists()) {
|
||||
try (FileReader fr = new FileReader(domainKeyFile)) {
|
||||
return KeyPairUtils.readKeyPair(fr);
|
||||
|
|
@ -32,17 +48,20 @@ public class AcmeFileDataStore implements AcmeDataStore {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
public void storeUserKeyPair(KeyPair keyPair){
|
||||
try (FileWriter fw = new FileWriter(userKeyFile)) {
|
||||
KeyPairUtils.writeKeyPair(keyPair, fw);
|
||||
|
||||
public void storeAccountKeyPair(URL accountLocation, KeyPair accounKeyPair){
|
||||
try (FileWriter fw = new FileWriter(accountKeyFile)) {
|
||||
FileUtil.setContent(accountLocationFile, accountLocation.toString());
|
||||
KeyPairUtils.writeKeyPair(accounKeyPair, fw);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public KeyPair loadDomainKeyPair() {
|
||||
if (userKeyFile.exists()) {
|
||||
try (FileReader fr = new FileReader(userKeyFile)) {
|
||||
|
||||
public KeyPair getDomainKeyPair() {
|
||||
if (accountKeyFile.exists()) {
|
||||
try (FileReader fr = new FileReader(accountKeyFile)) {
|
||||
return KeyPairUtils.readKeyPair(fr);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
|
|
@ -50,8 +69,9 @@ public class AcmeFileDataStore implements AcmeDataStore {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void storeDomainKeyPair(KeyPair keyPair){
|
||||
try (FileWriter fw = new FileWriter(userKeyFile)) {
|
||||
try (FileWriter fw = new FileWriter(accountKeyFile)) {
|
||||
KeyPairUtils.writeKeyPair(keyPair, fw);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue