Convert a SID to String with Java

A security identifier (SID) is an unique identifier, commonly used in Microsoft’s systems. For example, it’s used to identify users within Windows, or, more generally, within an Active Directory.

The SID is a binary value, with a variable length, that can be also be represented as a string. This conversion is implemented by the function called ConvertSidToStringSid, provided by the library Advapi32.dll, available only in Windows.

Hence, if you need to perform this conversion, you can procede with one of these two paths:

  • Use Advapi32 (only in Windows)
  • Rewrite the conversion
Thus, it’s very useful to have that implementation in a particular language (in our case Java), to be used everywhere. I noticed that, after a research in Internet, all the implementations are wrong, despite they work in general. Therefore, I decided to write my own implementation, thoroughly tested to prove you its correctness.

A string SID has this format:

S-1-IdentifierAuthority-SubAuthority1-SubAuthority2-...-SubAuthorityn
This syntax is defined formally, using the ABNF in this way:
SID= "S-1-" IdentifierAuthority 1*SubAuthority
IdentifierAuthority= IdentifierAuthorityDec / IdentifierAuthorityHex
  ; If the identifier authority is < 2^32, the
  ; identifier authority is represented as a decimal
  ; number
  ; If the identifier authority is >= 2^32,
  ; the identifier authority is represented in
  ; hexadecimal
IdentifierAuthorityDec =  1*10DIGIT
  ; IdentifierAuthorityDec, top level authority of a
  ; security identifier is represented as a decimal number
IdentifierAuthorityHex = "0x" 12HEXDIG
  ; IdentifierAuthorityHex, the top-level authority of a
  ; security identifier is represented as a hexadecimal number
SubAuthority= "-" 1*10DIGIT
  ; Sub-Authority is always represented as a decimal number
  ; No leading "0" characters are allowed when IdentifierAuthority
  ; or SubAuthority is represented as a decimal number
  ; All hexadecimal digits must be output in string format,
  ; pre-pended by "0x"
Each field corresponds to a particular group of bytes of the binary SID. This correspondence has also been defined formally. With these information, this is the final algorithm that I implemented in Java:
public static String convertSidToStringSid(byte[] sid) {
	int offset, size;

	// sid[0] is the Revision, we allow only version 1, because it's the
	// only that exists right now.
	if (sid[0] != 1)
		throw new IllegalArgumentException("SID revision must be 1");

	StringBuilder stringSidBuilder = new StringBuilder("S-1-");

	// The next byte specifies the numbers of sub authorities (number of
	// dashes minus two)
	int subAuthorityCount = sid[1] & 0xFF;

	// IdentifierAuthority (6 bytes starting from the second) (big endian)
	long identifierAuthority = 0;
	offset = 2;
	size = 6;
	for (int i = 0; i < size; i++) {
		identifierAuthority |= (long) (sid[offset + i] & 0xFF) << (8 * (size - 1 - i));
		// The & 0xFF is necessary because byte is signed in Java
	}
	if (identifierAuthority < Math.pow(2, 32)) {
		stringSidBuilder.append(Long.toString(identifierAuthority));
	} else {
		stringSidBuilder.append("0x").append(
				Long.toHexString(identifierAuthority).toUpperCase());
	}

	// Iterate all the SubAuthority (little-endian)
	offset = 8;
	size = 4; // 32-bits (4 bytes) for each SubAuthority
	for (int i = 0; i < subAuthorityCount; i++, offset += size) {
		long subAuthority = 0;
		for (int j = 0; j < size; j++) {
			subAuthority |= (long) (sid[offset + j] & 0xFF) << (8 * j);
			// The & 0xFF is necessary because byte is signed in Java
		}
		stringSidBuilder.append("-").append(subAuthority);
	}

	return stringSidBuilder.toString();
}
This algorithm has been tested generating a lot of SIDs combinations, and comparing the results with the ones returned by the original conversion in Advapi32.dll. Just to have an idea, the following code is a particular function used for the tests.
private static void generateCombinations(byte[] sid, int offset) throws Exception {
	String convertedSid, convertedSid2;
	if (offset >= sid.length) {
		convertedSid = Advapi32Util.convertSidToStringSid(new PSID(sid));
		convertedSid2 = convertSidToStringSid(sid);
		if (!convertedSid.equals(convertedSid2)) {
			throw new Exception("Conversion Error: "
				+ convertedSid2 + " instead of " + convertedSid);
		}
		return;
	}
	for (int i = 0; i <= 255; i += 255/3) {
		sid[offset] = (byte)(i & 0xFF);
		generateCombinations(sid, offset+1);
	}
}

#
 
Complete project

Download the complete algorithm, with also the tests.

Leave a Reply

Your email address will not be published. Required fields are marked *