Handle IDE domain names

pull/30/head
Richard Körber 2016-11-18 21:22:36 +01:00
parent dc74a54116
commit ef42e04793
4 changed files with 115 additions and 3 deletions

View File

@ -39,6 +39,7 @@ import org.shredzone.acme4j.exception.AcmeNetworkException;
import org.shredzone.acme4j.exception.AcmeProtocolException; import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.exception.AcmeRetryAfterException; import org.shredzone.acme4j.exception.AcmeRetryAfterException;
import org.shredzone.acme4j.util.ClaimBuilder; import org.shredzone.acme4j.util.ClaimBuilder;
import org.shredzone.acme4j.util.DomainUtils;
import org.shredzone.acme4j.util.SignatureUtils; import org.shredzone.acme4j.util.SignatureUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -173,6 +174,8 @@ public class Registration extends AcmeResource {
/** /**
* Authorizes a domain. The domain is associated with this registration. * Authorizes a domain. The domain is associated with this registration.
* <p>
* IDN domain names will be ACE encoded automatically.
* *
* @param domain * @param domain
* Domain name to be authorized * Domain name to be authorized
@ -189,7 +192,7 @@ public class Registration extends AcmeResource {
claims.putResource(Resource.NEW_AUTHZ); claims.putResource(Resource.NEW_AUTHZ);
claims.object("identifier") claims.object("identifier")
.put("type", "dns") .put("type", "dns")
.put("value", domain); .put("value", DomainUtils.toAce(domain));
int rc = conn.sendSignedRequest(getSession().resourceUri(Resource.NEW_AUTHZ), claims, getSession()); int rc = conn.sendSignedRequest(getSession().resourceUri(Resource.NEW_AUTHZ), claims, getSession());
if (rc != HttpURLConnection.HTTP_CREATED) { if (rc != HttpURLConnection.HTTP_CREATED) {

View File

@ -0,0 +1,48 @@
/*
* acme4j - Java ACME client
*
* Copyright (C) 2016 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j.util;
import java.net.IDN;
/**
* Utility class for domains and domain names.
*/
public final class DomainUtils {
private DomainUtils() {
// Utility class without constructor
}
/**
* ASCII encodes a domain name.
* <p>
* The conversion is done as described in
* <a href="http://www.ietf.org/rfc/rfc3490.txt">RFC 3490</a>. Additionally, all
* leading and trailing white spaces are trimmed, and the result is lowercased.
* <p>
* It is safe to pass in ACE encoded domains, they will be returned unchanged.
*
* @param domain
* Domain name to encode
* @return Encoded domain name, white space trimmed and lower cased. {@code null} if
* {@code null} was passed in.
*/
public static String toAce(String domain) {
if (domain == null) {
return null;
}
return IDN.toASCII(domain.trim()).toLowerCase();
}
}

View File

@ -0,0 +1,54 @@
/*
* acme4j - Java ACME client
*
* Copyright (C) 2016 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j.util;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import org.junit.Test;
/**
* Unit tests for {@link DomainUtils}.
*/
public class DomainUtilsTest {
/**
* Test ACE conversion.
*/
@Test
public void testToAce() {
// Test ASCII domains in different notations
assertThat(DomainUtils.toAce("example.com"), is("example.com"));
assertThat(DomainUtils.toAce(" example.com "), is("example.com"));
assertThat(DomainUtils.toAce("ExAmPlE.CoM"), is("example.com"));
assertThat(DomainUtils.toAce("foo.example.com"), is("foo.example.com"));
assertThat(DomainUtils.toAce("bar.foo.example.com"), is("bar.foo.example.com"));
// Test IDN domains
assertThat(DomainUtils.toAce("ExÄmþle.¢öM"), is("xn--exmle-hra7p.xn--m-7ba6w"));
// Test alternate separators
assertThat(DomainUtils.toAce("example\u3002com"), is("example.com"));
assertThat(DomainUtils.toAce("example\uff0ecom"), is("example.com"));
assertThat(DomainUtils.toAce("example\uff61com"), is("example.com"));
// Test ACE encoded domains, they must not change
assertThat(DomainUtils.toAce("xn--exmle-hra7p.xn--m-7ba6w"),
is("xn--exmle-hra7p.xn--m-7ba6w"));
// Test null
assertThat(DomainUtils.toAce(null), is(nullValue()));
}
}

View File

@ -59,17 +59,22 @@ public class CSRBuilder {
* <em>Common Name</em>. All domain names will be added as <em>Subject * <em>Common Name</em>. All domain names will be added as <em>Subject
* Alternative Name</em>. * Alternative Name</em>.
* <p> * <p>
* IDN domain names are ACE encoded automatically.
* <p>
* Note that ACME servers may not accept wildcard domains! * Note that ACME servers may not accept wildcard domains!
*/ */
public void addDomain(String domain) { public void addDomain(String domain) {
String ace = DomainUtils.toAce(domain);
if (namelist.isEmpty()) { if (namelist.isEmpty()) {
namebuilder.addRDN(BCStyle.CN, domain); namebuilder.addRDN(BCStyle.CN, ace);
} }
namelist.add(domain); namelist.add(ace);
} }
/** /**
* Adds a {@link Collection} of domains. * Adds a {@link Collection} of domains.
* <p>
* IDN domain names are ACE encoded automatically.
*/ */
public void addDomains(Collection<String> domains) { public void addDomains(Collection<String> domains) {
for (String domain : domains) { for (String domain : domains) {
@ -79,6 +84,8 @@ public class CSRBuilder {
/** /**
* Adds multiple domain names. * Adds multiple domain names.
* <p>
* IDN domain names are ACE encoded automatically.
*/ */
public void addDomains(String... domains) { public void addDomains(String... domains) {
for (String domain : domains) { for (String domain : domains) {