/* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.x509.dn;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import javax.security.auth.x500.X500Principal;
import org.cryptacular.codec.HexEncoder;
/**
* Produces a string representation of an X.500 distinguished name using the process described in section 2 of RFC 2253,
* LADPv3 Distinguished Names.
*
* @author Middleware Services
*/
public class LdapNameFormatter implements NameFormatter
{
/** Separator character between RDN components. */
public static final char RDN_SEPARATOR = ',';
/** Separator character between ATV components in the same RDN element. */
public static final char ATV_SEPARATOR = '+';
/** Escape character. */
public static final char ESCAPE_CHAR = '\\';
/** String of characters that need to be escaped. */
public static final String RESERVED_CHARS = ",+\"\\<>;";
/** Handles hex encoding. */
private static final HexEncoder ENCODER = new HexEncoder();
@Override
public String format(final X500Principal dn)
{
final StringBuilder builder = new StringBuilder();
final RDNSequence sequence = NameReader.readX500Principal(dn);
int i = 0;
for (RDN rdn : sequence.backward()) {
if (i++ > 0) {
builder.append(RDN_SEPARATOR);
}
int j = 0;
for (Attribute attr : rdn.getAttributes()) {
if (j++ > 0) {
builder.append(ATV_SEPARATOR);
}
builder.append(attr.getType()).append('=');
final AttributeType type = attr.getType();
if (type instanceof StandardAttributeType) {
escape(attr.getValue(), builder);
} else {
encode(attr.getValue(), builder);
}
}
}
return builder.toString();
}
/**
* Appends the given value to the output with proper character escaping.
*
* @param value Value to escape.
* @param output String builder where escaped value is written.
*/
private static void escape(final String value, final StringBuilder output)
{
char c = value.charAt(0);
if (c == ' ' || c == '#') {
output.append(ESCAPE_CHAR);
}
output.append(c);
final int nmax = value.length() - 1;
for (int n = 1; n < nmax; n++) {
c = value.charAt(n);
if (RESERVED_CHARS.indexOf(c) > -1) {
output.append(ESCAPE_CHAR);
}
output.append(c);
}
c = value.charAt(nmax);
if (c == ' ') {
output.append(ESCAPE_CHAR);
}
output.append(c);
}
/**
* Appends the given value to the output using the HEX encoding method described in section 2.4.
*
* @param value Value to encode.
* @param output String builder where encoded value is written.
*/
private static void encode(final String value, final StringBuilder output)
{
output.append('#');
final byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
final CharBuffer out = CharBuffer.allocate(bytes.length * 2);
ENCODER.encode(ByteBuffer.wrap(bytes), out);
output.append(out.flip());
}
}