我正在使用Spring-LDAP编写Java程序。我需要实现一个方法,该方法应该通过SID搜索用户。出于这个原因,我使用像"&((objectClass=User)(objectSid="+sid+"))"
这样的过滤器。搜索不能像"S-1-12-345677-5676743-223344-..."
这样的字符串格式使用sid。
使用Apache Directory Studio,我可以使用像(objectSid=\ff\01\03\04\1a\2b\...)
这样的过滤器定期查询我的AD LDAP数据库。这是十六进制格式的objectSid。
现在,如何在Java中将SID从String转换为十六进制,反之亦然?
答案 0 :(得分:5)
Advapi32Util类中有convertSidToStringSid
和convertStringSidToSid
方法。
我不确定这是否是您需要的确切格式,但希望能让您更接近。
这里有一个blog post,它描述了可能有用的SID格式。
答案 1 :(得分:2)
ho1(使用Advapi32Util)类建议的解决方案有两个限制:
因此,我编写了一个纯Java类,没有外部依赖关系,可以在所有平台上运行。此类可以将安全标识符从其二进制文件转换为文本表示形式,反之亦然。
如果您使用的是Spring-LDAP,您也可以使用LdapUtils类提供的方法,但是这些方法不会对SID格式进行任何检查,因此可能会产生错误的结果或意外的异常。
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class SidConverter {
private final static int MASK_8_BIT = 0xff;
private final static long MASK_32_BIT = 0xffffffffL;
private final static long MASK_48_BIT = 0xffffffffffffL;
public static String bytesToString(byte[] bytes) {
if (bytes.length < 8) {
throw new IllegalArgumentException(
"Binary SID representation must have at least 8 bytes but passed byte array has only "
+ bytes.length + " bytes.");
}
// The revision number is an unsigned 8-bit unsigned integer.
int revision = bytes[0] & MASK_8_BIT;
// The number of sub-authority parts is specified as an 8-bit unsigned
// integer.
int numberOfSubAuthorityParts = bytes[1] & MASK_8_BIT;
if (bytes.length != 8 + numberOfSubAuthorityParts * 4) {
throw new IllegalArgumentException(
"According to byte 1 of the SID it total length should be "
+ (8 + 4 * numberOfSubAuthorityParts)
+ " bytes, however its actual length is "
+ bytes.length + " bytes.");
}
// The authority is a 48-bit unsigned integer stored in big-endian
// format.
long authority = ByteBuffer.wrap(bytes).getLong() & MASK_48_BIT;
// The sub-authority consists of up to 255 32-bit unsigned integers in
// little-endian format. The number of integers is specified by
// numberOfSubAuthorityParts.
int[] subAuthority = new int[numberOfSubAuthorityParts];
ByteBuffer.wrap(bytes, 8, bytes.length - 8)
.order(ByteOrder.LITTLE_ENDIAN).asIntBuffer().put(subAuthority);
StringBuilder sb = new StringBuilder();
sb.append("S-");
sb.append(revision);
sb.append("-");
sb.append(authority);
for (int subAuthorityPart : subAuthority) {
sb.append("-");
sb.append(subAuthorityPart & MASK_32_BIT);
}
return sb.toString();
}
public static byte[] stringToBytes(String sid) {
if (!sid.startsWith("S-") && !sid.startsWith("s-")) {
throw new IllegalArgumentException("Invalid SID \"" + sid
+ "\": A valid SID must start with \"S-\".");
}
String[] parts = sid.split("-");
if (parts.length < 3) {
throw new IllegalArgumentException("Invalid SID \"" + sid
+ "\": A valid SID must have at least two dashes.");
}
if (parts.length > MASK_8_BIT + 3) {
throw new IllegalArgumentException("Invalid SID \"" + sid
+ "\": A valid SID must not have more than 257 dashes.");
}
int revision;
try {
revision = Integer.parseInt(parts[1]);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(
"Invalid revision part in SID \""
+ sid
+ "\": The revision must be an integer number between 0 and 255.");
}
if (revision < 0 || revision > MASK_8_BIT) {
throw new IllegalArgumentException(
"Invalid revision part in SID \""
+ sid
+ "\": The revision must be an integer number between 0 and 255.");
}
int numberOfSubAuthorityParts = parts.length - 3;
long authority;
try {
authority = Long.parseLong(parts[2]);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(
"Invalid authority part in SID \""
+ sid
+ "\": The authority must be an integer number between 0 and 281474976710655.");
}
if (authority < 0 || authority > MASK_48_BIT) {
throw new IllegalArgumentException(
"Invalid authority part in SID \""
+ sid
+ "\": The authority must be an integer number between 0 and 281474976710655.");
}
int[] subAuthority = new int[numberOfSubAuthorityParts];
for (int i = 0; i < numberOfSubAuthorityParts; i++) {
long subAuthorityPart;
try {
subAuthorityPart = Long.parseLong(parts[3 + i]);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(
"Invalid sub-authority part in SID \""
+ sid
+ "\": The sub-authority parts must be integer numbers between 0 and 4294967295.");
}
if (subAuthorityPart < 0 || subAuthorityPart > MASK_32_BIT) {
throw new IllegalArgumentException(
"Invalid sub-authority part in SID \""
+ sid
+ "\": The sub-authority parts must be integer numbers between 0 and 4294967295.");
}
subAuthority[i] = (int) subAuthorityPart;
}
byte[] bytes = new byte[8 + numberOfSubAuthorityParts * 4];
// We have to write the authority first, otherwise it would overwrite
// the revision and length bytes.
ByteBuffer.wrap(bytes).putLong(authority);
bytes[0] = (byte) revision;
bytes[1] = (byte) numberOfSubAuthorityParts;
ByteBuffer.wrap(bytes, 8, bytes.length - 8)
.order(ByteOrder.LITTLE_ENDIAN).asIntBuffer().put(subAuthority);
return bytes;
}
}
答案 2 :(得分:1)
使用InPlaceMsAdObjectSidValueEditor.java中的convertToString:
protected String convertToString( byte[] bytes )
{
/*
* The binary data structure, from http://msdn.microsoft.com/en-us/library/cc230371(PROT.10).aspx:
* byte[0] - Revision (1 byte): An 8-bit unsigned integer that specifies the revision level of the SID structure. This value MUST be set to 0x01.
* byte[1] - SubAuthorityCount (1 byte): An 8-bit unsigned integer that specifies the number of elements in the SubAuthority array. The maximum number of elements allowed is 15.
* byte[2-7] - IdentifierAuthority (6 bytes): A SID_IDENTIFIER_AUTHORITY structure that contains information, which indicates the authority under which the SID was created. It describes the entity that created the SID and manages the account.
* Six element arrays of 8-bit unsigned integers that specify the top-level authority
* big-endian!
* and then - SubAuthority (variable): A variable length array of unsigned 32-bit integers that uniquely identifies a principal relative to the IdentifierAuthority. Its length is determined by SubAuthorityCount.
* little-endian!
*/
if ( bytes == null || bytes.length < 8 )
{
return Messages.getString( "InPlaceMsAdObjectSidValueEditor.InvalidSid" ); //$NON-NLS-1$
}
char[] hex = Hex.encodeHex( bytes );
StringBuffer sb = new StringBuffer();
// start with 'S'
sb.append( 'S' );
// revision
int revision = Integer.parseInt( new String( hex, 0, 2 ), 16 );
sb.append( '-' );
sb.append( revision );
// get count
int count = Integer.parseInt( new String( hex, 2, 2 ), 16 );
// check length
if ( bytes.length != ( 8 + count * 4 ) )
{
return Messages.getString( "InPlaceMsAdObjectSidValueEditor.InvalidSid" ); //$NON-NLS-1$
}
// get authority, big-endian
long authority = Long.parseLong( new String( hex, 4, 12 ), 16 );
sb.append( '-' );
sb.append( authority );
// sub-authorities, little-endian
for ( int i = 0; i < count; i++ )
{
StringBuffer rid = new StringBuffer();
for ( int k = 3; k >= 0; k-- )
{
rid.append( hex[16 + ( i * 8 ) + ( k * 2 )] );
rid.append( hex[16 + ( i * 8 ) + ( k * 2 ) + 1] );
}
long subAuthority = Long.parseLong( rid.toString(), 16 );
sb.append( '-' );
sb.append( subAuthority );
}
return sb.toString();
}
(来自Apache Directory Studio)
答案 3 :(得分:1)
没有JNA的示例
这里有一些很好的紧凑代码,用于在不使用JNA的情况下转换SID。它甚至通过返回NULL,EMPTY或零长度数组内置了一些“错误检查”,如果某些东西不太正确的话。
对于SID到String:
/**
* Converts Windows SID to a String. NULL input returns NULL.
* Invalid byte array returns EMPTY.
* @param sid SID as byte array.
* @return SID as String.
*/
public static String convertSidToStr(byte[] sid) {
if (sid==null) return null;
if (sid.length<8 || sid.length % 4 != 0) return "";
StringBuilder sb = new StringBuilder();
sb.append("S-").append(sid[0]);
int c = sid[1]; // Init with Subauthority Count.
ByteBuffer bb = ByteBuffer.wrap(sid);
// bb.order(ByteOrder.BIG_ENDIAN); // Not needed, default big endian.
sb.append("-").append((long)bb.getLong() & 0XFFFFFFFFFFFFL);
bb.order(ByteOrder.LITTLE_ENDIAN); // Now switch.
for (int i=0; i<c; i++) { // Create Subauthorities.
sb.append("-").append((long)bb.getInt() & 0xFFFFFFFFL);
}
return sb.toString();
}
For String to SID:
/**
* Converts Windows SID String to byte array. NULL input returns NULL.
* Invalid String returns zero-length byte array.
* @param sid SID as String.
* @return SID as byte array.
*/
public static byte[] convertStrToSid(String sid) {
if (sid==null) return null;
if (!sid.matches("^[sS]-\\d-\\d{1,13})"
+ "(?:-\\d{1,10})*$")) return new byte[0];
String[] ss = sid.split("-");
int c=ss.length-3; // Init with Subauthority Count.
byte[] b=new byte[2+6+(c*4)];
ByteBuffer bb = ByteBuffer.wrap(b);
// bb.order(ByteOrder.BIG_ENDIAN); // Not needed, default big endian.
bb.putLong(Long.parseLong(ss[2]));
// Overlay bytes 0 and 1 with Revision and Identifier Authority.
b[0]=(byte)Short.parseShort(ss[1]);
b[1]=(byte)c;
bb.order(ByteOrder.LITTLE_ENDIAN); // Now switch.
for (int i=0; i<c; i++) { // Create Subauthorities.
bb.putInt((int)Long.parseLong(ss[i+3]));
}
return b;
}
供参考,请参阅TechNet的Security Identifier Structure。
答案 4 :(得分:1)
来自http://miromannino.com/blog/convert-a-sid-to-string-with-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();
}