We are investigating an issue using ACME (acme4j libraries using our own Java server) with certificate generation for secondary subdomains.
No problem when generating a certificate for a domain like xxx.mydomain.com. The request failed when requesting a certificate for yyy.xxx.mydomain.com with a urn:ietf:params:acme:error:malformed error from the server.
Is there any limitation or added constrains on the CSR that need to be filled when building the CSR for a secondary subdomain?
We are building the CSR using only domain name:
CSRBuilder csrb = new CSRBuilder();
csrb.addDomain(domain);
csrb.sign(domainKeyPair);
Note that when you submit your CSR to Let's Encrypt, it should be the Base64 encoded version of the CSR in DER format, without padding. So convert the CSR to DER (bytes), then get the base64 encoded version (without padding).
The content of the request sent to the server looks like this: {"protected":"eyJ1cmwiOiJodHRwczovL2FjbWUtc3RhZ2luZy12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL2ZpbmFsaXplLzE3MDIwNzQ4LzE5OTU5MDU4MSIsImtpZCI6Imh0dHBzOi8vYWNtZS1zdGFnaW5nLXYwMi5hcGkubGV0c2VuY3J5cHQub3JnL2FjbWUvYWNjdC8xNzAyMDc0OCIsIm5vbmNlIjoiMDAwNC0teWtHcXRJVGJHekpvTGdyN3M1VFRjVGphOVdFbjNJRm5CS2dicTgtTkEiLCJhbGciOiJSUzI1NiJ9","payload":"eyJjc3IiOiJNSUlDeURDQ0FiQUNBUUF3UmpFb01DWUdBMVVFQXd3ZmFuVnNhV1Z1TG1kbGJubHRiM1JwYjI0dWMyaGhhMkZ3WldGcmN5NW1jakVhXG5NQmdHQTFVRUNnd1JSMlZ1ZVcxdmRHbHZiaUJFWlhacFkyVXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCXG5BUUNoYWpfY3hHQm90WlphZ3RUSnZDcmEzeE5MV0tHLUx6aHJYQ3JTdWFYVlNEUE53emtUTFBZUU42RHNlSnR1TU9vT0h6aVp1YnR6XG50em41aTBpMU5wM3V5TlpSWjREX0ZSZWpyNE9RWnlXM1IwcEtmRnVfUU95TmZ1cVY0XzhJNDFETUZTU1BLSkE1LTlndEJaX2l5c2lqXG41Rzl6TXE0ODBsaTd3VmFxZnVUajhJLWMyYThxV1RPWGpWSEN5Z1J2dDNhU18xbE9tMXFNZ2doNE0xN2ZpbzQwdi1nWGR6Ri01dTZOXG5GOXlLQzI2VnBheWRXTHUzX191eWFBZkFHc3AxVXl3Y2FPTmFhSjZpaVNUTDJEQmtEdDd2ME14NmJHZXlCZHByMEQ3NWJxbVZnTFNYXG5yM1AxcHR2X09DZm1mRXB2RGdQWnJaSldLbnNueGhtSGJ6anhteUNKQWdNQkFBR2dQVEE3QmdrcWhraUc5dzBCQ1E0eExqQXNNQ29HXG5BMVVkRVFRak1DR0NIMnAxYkdsbGJpNW5aVzU1Ylc5MGFXOXVMbk5vWVd0aGNHVmhhM011Wm5Jd0RRWUpLb1pJaHZjTkFRRUxCUUFEXG5nZ0VCQURHb1laTERaNWx1cmpTaXF3OUJlVW0zWmxnS2JRS1JtV1JPREtuVVZOQ3hsUTMxUTJva3JZaW5mcHoyQTB0bTJ2b3F5Nl9rXG5ZYkNsVGYzcmVLdEdCZ3RKbjlIalZWOVdpWDVtOVdxTHZjMXJjT0hxT1Z2cmRxbkc4dXZLWGZlOGNWQzlTMXZhWE1na3FTZnZIMlNPXG53RVlRQUE5YUF1YUkza3lBQUcwNFZnb0k3SGtuc0syeXRIOE1aRGRpLThWZmlfaXdFWVlPVm40NDFtWEZZdWctbUFmWFFtandTa2ZOXG40ai1uNG9KQlMyR0k2UU5kLUhGWDVHNm1DSklUS2xjSWZrV1hSbDhrYWlKbExsemZ6eFN4NHpjSHRKZTVkSURwZ1FHc0kzQkZ0NEpWXG5NZVN1azJSR3hhUEVFZVFsRUkxNzI4MXZfcDRLV3hXSlFzTHZBU2tUS3BzXG4ifQ","signature":"MTeOEM1Q5bDeDaz4iihwLqIjw8HNahiM3HQSc5iDVvkMs_ylyLT0TG5_pPgIYajdmtu37_4N7FDP3-PUmqu8eX7D3PeEXbzBPHVHR4UKyqtav9-z4-Vkm94r_183U2dPgI5JSVxJUscMdnoDvgVQGQyKX-FA3FXAMeEuoZnIM4bES4jzoqEhAYGP7APhg4AYyE2I2wgn_bphTHEic5_A2EhBPb2sAHTHo94DPG7L4Mxs-aRa8mWPidE-gu-TxDAdL_emr1tMmbzy-XdISv9kyVtB3Q1CykfYoXeG0qjiDYZnWN71cAkAeYe82aBVY5xrHd89rtm56gV6Qcug4OkJ-A"}
I think maybe you are using some weird way of generating this encoding. The right way would be to get the raw DER bytes (use a proper PEM decoder if necessary) and then maybe use acme4j's AcmeUtils.base64UrlEncode method to encode it.
Okay, that would suggest that the problem is with the data you're passing for the byte[] csr parameter. acme4j doesn't call any code which would be resulting in these newlines.
I think webprofusion is on the money and you're not providing the DER-encoded CSR.
Ok I found the issue:
We replaced the java.util.Base64 function by Android Base64 encoder for older Android versions using Java 7.
The default implementation does not remove LF and we just need to add the NO_WRAP flag.
That was really weird that the CSR was failing with only domains with secondary subdomains, but it is just a length problem. We didn't face problem with shorted domains because the first LF was just placed after the field containing the domain name on the first Base64 line ....
Thanks you for your help pointing to the \n,
Julien