/* See LICENSE for licensing and NOTICE for copyright. */ package org.ldaptive.ad; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; /** * Class to represent an active directory SID. Provides conversion from binary to string and vice versa. * * @author Middleware Services */ public final class SecurityIdentifier { /** Default constructor. */ private SecurityIdentifier() {} /** * Converts the supplied SID to it's string format. * * @param sid to convert * * @return string format of the SID */ public static String toString(final byte[] sid) { // CheckStyle:MagicNumber OFF // format of SID: S-R-X-Y1-Y2...-Yn // S: static 'S', indicating string // R: revision // X: authority // Yn: sub-authority // create a byte buffer for reading the sid final ByteBuffer sidBuffer = ByteBuffer.wrap(sid); // string identifier final StringBuilder sb = new StringBuilder("S"); // byte[0] is the revision sb.append("-").append(sidBuffer.get() & 0xFF); // byte[1] is the count of sub-authorities final int countSubAuth = sidBuffer.get() & 0xFF; // byte[2] - byte[7] is the authority (48 bits) sidBuffer.limit(8); sb.append("-").append(getLong(sidBuffer, true)); // byte[8] - ? is the sub-authorities, // (32 bits per authority, little endian) for (int i = 0; i < countSubAuth; i++) { // values are unsigned, so get 4 bytes as a long sidBuffer.limit(sidBuffer.position() + 4); sb.append("-").append(getLong(sidBuffer, false)); } return sb.toString(); // CheckStyle:MagicNumber ON } /** * Converts the supplied SID to it's binary format. * * @param sid to convert * * @return binary format of the SID */ public static byte[] toBytes(final String sid) { // CheckStyle:MagicNumber OFF // format of SID: S-R-X-Y1-Y2...-Yn // S: static 'S', indicating string // R: revision // X: authority // Yn: sub-authority final StringTokenizer st = new StringTokenizer(sid, "-"); // first token is the 'S' st.nextToken(); // second token is the revision final int revision = Integer.valueOf(st.nextToken()); // third token is the authority final long authority = Long.valueOf(st.nextToken()); // remaining token are the sub authorities final List<String> subAuthorities = new ArrayList<>(); while (st.hasMoreTokens()) { subAuthorities.add(st.nextToken()); } // revision is 1 byte // sub-authorities count is 1 byte // authority is 6 bytes // 4 bytes for each sub-authority final int size = 8 + (4 * subAuthorities.size()); final ByteBuffer sidBuffer = ByteBuffer.allocate(size); sidBuffer.put((byte) (revision & 0xFF)); sidBuffer.put((byte) (subAuthorities.size() & 0xFF)); sidBuffer.limit(8); putLong(sidBuffer, authority, true); for (String subAuthority : subAuthorities) { sidBuffer.limit(sidBuffer.position() + 4); putLong(sidBuffer, Long.valueOf(subAuthority), false); } return sidBuffer.array(); // CheckStyle:MagicNumber ON } /** * Reads a long from the supplied byte buffer. The byte buffer limit must be set appropriately by the caller. * * @param buffer to read long from * @param bigEndian whether to read the bytes as big endian * * @return long value */ private static long getLong(final ByteBuffer buffer, final boolean bigEndian) { // CheckStyle:MagicNumber OFF long value = buffer.get() & 0xFF; if (bigEndian) { // shift the value to the right, or with the next byte while (buffer.hasRemaining()) { value <<= Byte.SIZE; value |= buffer.get() & 0xFF; } } else { // shift the next byte to the right, or with the value int offset = Byte.SIZE; while (buffer.hasRemaining()) { value |= (buffer.get() & 0xFF) << offset; offset += Byte.SIZE; } } return value & 0xFFFFFFFFL; // CheckStyle:MagicNumber ON } /** * Writes a long into the supplied byte buffer. The byte buffer limit must be set appropriately by the caller. * * @param buffer to write long to * @param value to write * @param bigEndian whether to write the bytes as big endian */ private static void putLong(final ByteBuffer buffer, final long value, final boolean bigEndian) { // CheckStyle:MagicNumber OFF if (bigEndian) { int offset = Byte.SIZE * (buffer.limit() - buffer.position() - 1); while (buffer.hasRemaining()) { // get the high bits and decrement down buffer.put((byte) ((value >> offset) & 0xFF)); offset -= Byte.SIZE; } } else { int offset = 0; while (buffer.hasRemaining()) { // get the low bits and increment up buffer.put((byte) ((value >> offset) & 0xFF)); offset += Byte.SIZE; } } // CheckStyle:MagicNumber ON } }