mirror of https://github.com/shred/acme4j
				
				
				
			Make the challenge selectable
							parent
							
								
									7b6af21cd1
								
							
						
					
					
						commit
						78ccec7d1d
					
				|  | @ -26,7 +26,10 @@ import java.util.Collection; | ||||||
| 
 | 
 | ||||||
| import javax.swing.JOptionPane; | import javax.swing.JOptionPane; | ||||||
| 
 | 
 | ||||||
|  | import org.shredzone.acme4j.challenge.Challenge; | ||||||
|  | import org.shredzone.acme4j.challenge.DnsChallenge; | ||||||
| import org.shredzone.acme4j.challenge.HttpChallenge; | import org.shredzone.acme4j.challenge.HttpChallenge; | ||||||
|  | import org.shredzone.acme4j.challenge.TlsSniChallenge; | ||||||
| import org.shredzone.acme4j.exception.AcmeConflictException; | import org.shredzone.acme4j.exception.AcmeConflictException; | ||||||
| import org.shredzone.acme4j.exception.AcmeException; | import org.shredzone.acme4j.exception.AcmeException; | ||||||
| import org.shredzone.acme4j.exception.AcmeUnauthorizedException; | import org.shredzone.acme4j.exception.AcmeUnauthorizedException; | ||||||
|  | @ -119,35 +122,12 @@ public class ClientTest { | ||||||
|             } |             } | ||||||
|             LOG.info("New authorization for domain " + domain); |             LOG.info("New authorization for domain " + domain); | ||||||
| 
 | 
 | ||||||
|             // Find a single http-01 challenge
 |             // Uncomment a challenge...
 | ||||||
|             HttpChallenge challenge = auth.findChallenge(HttpChallenge.TYPE); |             Challenge challenge = httpChallenge(auth, account, domain); | ||||||
|  | //            Challenge challenge = dnsChallenge(auth, account, domain);
 | ||||||
|  | //            Challenge challenge = tlsSniChallenge(auth, account, domain);
 | ||||||
|  | 
 | ||||||
|             if (challenge == null) { |             if (challenge == null) { | ||||||
|                 LOG.error("Found no " + HttpChallenge.TYPE + " challenge, don't know what to do..."); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             // Authorize the challenge
 |  | ||||||
|             challenge.authorize(account); |  | ||||||
| 
 |  | ||||||
|             // Output the challenge, wait for acknowledge...
 |  | ||||||
|             LOG.info("Please create a file in your web server's base directory."); |  | ||||||
|             LOG.info("It must be reachable at: http://" + domain + "/.well-known/acme-challenge/" + challenge.getToken()); |  | ||||||
|             LOG.info("File name: " + challenge.getToken()); |  | ||||||
|             LOG.info("Content: " + challenge.getAuthorization()); |  | ||||||
|             LOG.info("The file must not contain any leading or trailing whitespaces or line breaks!"); |  | ||||||
|             LOG.info("If you're ready, dismiss the dialog..."); |  | ||||||
| 
 |  | ||||||
|             StringBuilder message = new StringBuilder(); |  | ||||||
|             message.append("Please create a file in your web server's base directory.\n\n"); |  | ||||||
|             message.append("http://").append(domain).append("/.well-known/acme-challenge/").append(challenge.getToken()).append("\n\n"); |  | ||||||
|             message.append("Content:\n\n"); |  | ||||||
|             message.append(challenge.getAuthorization()); |  | ||||||
|             int option = JOptionPane.showConfirmDialog(null, |  | ||||||
|                             message.toString(), |  | ||||||
|                             "Prepare Challenge", |  | ||||||
|                             JOptionPane.OK_CANCEL_OPTION); |  | ||||||
|             if (option == JOptionPane.CANCEL_OPTION) { |  | ||||||
|                 LOG.error("User cancelled challenge"); |  | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -211,6 +191,137 @@ public class ClientTest { | ||||||
|         // client.revokeCertificate(account, cert);
 |         // client.revokeCertificate(account, cert);
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Prepares HTTP challenge. | ||||||
|  |      */ | ||||||
|  |     public Challenge httpChallenge(Authorization auth, Account account, String domain) throws AcmeException { | ||||||
|  |         // Find a single http-01 challenge
 | ||||||
|  |         HttpChallenge challenge = auth.findChallenge(HttpChallenge.TYPE); | ||||||
|  |         if (challenge == null) { | ||||||
|  |             LOG.error("Found no " + HttpChallenge.TYPE + " challenge, don't know what to do..."); | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Authorize the challenge
 | ||||||
|  |         challenge.authorize(account); | ||||||
|  | 
 | ||||||
|  |         // Output the challenge, wait for acknowledge...
 | ||||||
|  |         LOG.info("Please create a file in your web server's base directory."); | ||||||
|  |         LOG.info("It must be reachable at: http://" + domain + "/.well-known/acme-challenge/" + challenge.getToken()); | ||||||
|  |         LOG.info("File name: " + challenge.getToken()); | ||||||
|  |         LOG.info("Content: " + challenge.getAuthorization()); | ||||||
|  |         LOG.info("The file must not contain any leading or trailing whitespaces or line breaks!"); | ||||||
|  |         LOG.info("If you're ready, dismiss the dialog..."); | ||||||
|  | 
 | ||||||
|  |         StringBuilder message = new StringBuilder(); | ||||||
|  |         message.append("Please create a file in your web server's base directory.\n\n"); | ||||||
|  |         message.append("http://").append(domain).append("/.well-known/acme-challenge/").append(challenge.getToken()).append("\n\n"); | ||||||
|  |         message.append("Content:\n\n"); | ||||||
|  |         message.append(challenge.getAuthorization()); | ||||||
|  |         int option = JOptionPane.showConfirmDialog(null, | ||||||
|  |                         message.toString(), | ||||||
|  |                         "Prepare Challenge", | ||||||
|  |                         JOptionPane.OK_CANCEL_OPTION); | ||||||
|  |         if (option == JOptionPane.CANCEL_OPTION) { | ||||||
|  |             LOG.error("User cancelled challenge"); | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return challenge; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Prepares DNS challenge. | ||||||
|  |      */ | ||||||
|  |     public Challenge dnsChallenge(Authorization auth, Account account, String domain) throws AcmeException { | ||||||
|  |         // Find a single dns-01 challenge
 | ||||||
|  |         DnsChallenge challenge = auth.findChallenge(DnsChallenge.TYPE); | ||||||
|  |         if (challenge == null) { | ||||||
|  |             LOG.error("Found no " + DnsChallenge.TYPE + " challenge, don't know what to do..."); | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Authorize the challenge
 | ||||||
|  |         challenge.authorize(account); | ||||||
|  | 
 | ||||||
|  |         // Output the challenge, wait for acknowledge...
 | ||||||
|  |         LOG.info("Please create a TXT record:"); | ||||||
|  |         LOG.info("_acme-challenge." + domain + ". IN TXT " + challenge.getDigest()); | ||||||
|  |         LOG.info("If you're ready, dismiss the dialog..."); | ||||||
|  | 
 | ||||||
|  |         StringBuilder message = new StringBuilder(); | ||||||
|  |         message.append("Please create a TXT record:\n\n"); | ||||||
|  |         message.append("_acme-challenge." + domain + ". IN TXT " + challenge.getDigest()); | ||||||
|  |         int option = JOptionPane.showConfirmDialog(null, | ||||||
|  |                         message.toString(), | ||||||
|  |                         "Prepare Challenge", | ||||||
|  |                         JOptionPane.OK_CANCEL_OPTION); | ||||||
|  |         if (option == JOptionPane.CANCEL_OPTION) { | ||||||
|  |             LOG.error("User cancelled challenge"); | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return challenge; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Prepares TLS-SNI challenge. | ||||||
|  |      */ | ||||||
|  |     public Challenge tlsSniChallenge(Authorization auth, Account account, String domain) throws AcmeException { | ||||||
|  |         // Find a single tls-sni-01 challenge
 | ||||||
|  |         TlsSniChallenge challenge = auth.findChallenge(TlsSniChallenge.TYPE); | ||||||
|  |         if (challenge == null) { | ||||||
|  |             LOG.error("Found no " + TlsSniChallenge.TYPE + " challenge, don't know what to do..."); | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Authorize the challenge
 | ||||||
|  |         challenge.authorize(account); | ||||||
|  | 
 | ||||||
|  |         // Get the Subject
 | ||||||
|  |         String subject = challenge.getSubject(); | ||||||
|  | 
 | ||||||
|  |         // Create a keypair
 | ||||||
|  |         KeyPair domainKeyPair; | ||||||
|  |         try (FileWriter fw = new FileWriter("tlssni.key")) { | ||||||
|  |             domainKeyPair = KeyPairUtils.createKeyPair(2048); | ||||||
|  |             KeyPairUtils.writeKeyPair(domainKeyPair, fw); | ||||||
|  |         } catch (IOException ex) { | ||||||
|  |             LOG.error("Could not create keypair", ex); | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Create a certificate
 | ||||||
|  |         try (FileWriter fw = new FileWriter("tlssni.crt")) { | ||||||
|  |             X509Certificate cert = CertificateUtils.createTlsSniCertificate(domainKeyPair, subject); | ||||||
|  |             CertificateUtils.writeX509Certificate(cert, fw); | ||||||
|  |         } catch (IOException ex) { | ||||||
|  |             LOG.error("Could not create certificate", ex); | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Output the challenge, wait for acknowledge...
 | ||||||
|  |         LOG.info("Please configure your web server."); | ||||||
|  |         LOG.info("It must return the certificate 'tlssni.crt' on a SNI request to:"); | ||||||
|  |         LOG.info(subject); | ||||||
|  |         LOG.info("The matching keypair is available at 'tlssni.key'."); | ||||||
|  |         LOG.info("If you're ready, dismiss the dialog..."); | ||||||
|  | 
 | ||||||
|  |         StringBuilder message = new StringBuilder(); | ||||||
|  |         message.append("Please use 'tlssni.key' and 'tlssni.crt' cert for SNI requests to:\n\n"); | ||||||
|  |         message.append("https://").append(subject).append("\n\n"); | ||||||
|  |         int option = JOptionPane.showConfirmDialog(null, | ||||||
|  |                         message.toString(), | ||||||
|  |                         "Prepare Challenge", | ||||||
|  |                         JOptionPane.OK_CANCEL_OPTION); | ||||||
|  |         if (option == JOptionPane.CANCEL_OPTION) { | ||||||
|  |             LOG.error("User cancelled challenge"); | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return challenge; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Presents the user a link to the Terms of Service, and asks for confirmation. |      * Presents the user a link to the Terms of Service, and asks for confirmation. | ||||||
|      * |      * | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Richard Körber
						Richard Körber