343 lines
10 KiB
C#
343 lines
10 KiB
C#
|
//------------------------------------------------------------
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//------------------------------------------------------------
|
||
|
|
||
|
namespace System.IdentityModel.Tokens
|
||
|
{
|
||
|
|
||
|
// ASN.1 DER. DER refers to Distinguished Encoding Rules.
|
||
|
internal static class DEREncoding
|
||
|
{
|
||
|
// OID=1.2.840.113554.1.2.2 (Kerberos V5) { 0x06, 0x09, 0x2a, 0x86,
|
||
|
// 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02 }
|
||
|
|
||
|
// 1. 0x60 -- Tag for [APPLICATION 0] SEQUENCE; indicates that
|
||
|
// -- constructed form, definite length encoding follows.
|
||
|
// 2. Token length octets, specifying length of subsequent data
|
||
|
// (i.e., the summed lengths of elements 3-5 in this list, and of the
|
||
|
// mechanism-defined token object following the tag). This element
|
||
|
// comprises a variable number of octets:
|
||
|
// 2a. If the indicated value is less than 128, it shall be
|
||
|
// represented in a single octet with bit 8 (high order) set to
|
||
|
// "0" and the remaining bits representing the value.
|
||
|
// 2b. If the indicated value is 128 or more, it shall be
|
||
|
// represented in two or more octets, with bit 8 of the first
|
||
|
// octet set to "1" and the remaining bits of the first octet
|
||
|
// specifying the number of additional octets. The subsequent
|
||
|
// octets carry the value, 8 bits per octet, most significant
|
||
|
// digit first. The minimum number of octets shall be used to
|
||
|
// encode the length (i.e., no octets representing leading zeros
|
||
|
// shall be included within the length encoding).
|
||
|
|
||
|
// 3. 0x06 -- Tag for OBJECT IDENTIFIER
|
||
|
|
||
|
// 4. Object identifier length -- length (number of octets) of
|
||
|
// -- the encoded object identifier contained in element 5,
|
||
|
// -- encoded per rules as described in 2a. and 2b. above.
|
||
|
|
||
|
// 5. Object identifier octets -- variable number of octets,
|
||
|
// -- encoded per ASN.1 BER rules:
|
||
|
|
||
|
// 5a. The first octet contains the sum of two values: (1) the
|
||
|
// top-level object identifier component, multiplied by 40
|
||
|
// (decimal), and (2) the second-level object identifier
|
||
|
// component. This special case is the only point within an
|
||
|
// object identifier encoding where a single octet represents
|
||
|
// contents of more than one component.
|
||
|
|
||
|
// 5b. Subsequent octets, if required, encode successively-lower
|
||
|
// components in the represented object identifier. A component's
|
||
|
// encoding may span multiple octets, encoding 7 bits per octet
|
||
|
// (most significant bits first) and with bit 8 set to "1" on all
|
||
|
// but the final octet in the component's encoding. The minimum
|
||
|
// number of octets shall be used to encode each component (i.e.,
|
||
|
// no octets representing leading zeros shall be included within a
|
||
|
// component's encoding).
|
||
|
|
||
|
// (Note: In many implementations, elements 3-5 may be stored and
|
||
|
// referenced as a contiguous string constant.)
|
||
|
|
||
|
|
||
|
static byte[] mech = new byte[] { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, 0x02 };
|
||
|
static byte[] type = new byte[] { 0x01, 0x00 };
|
||
|
|
||
|
private static bool BufferIsEqual(byte[] arrayOne, int offsetOne, byte[] arrayTwo, int offsetTwo, int length)
|
||
|
{
|
||
|
if (length > arrayOne.Length - offsetOne)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (length > arrayTwo.Length - offsetTwo)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < length; i++)
|
||
|
{
|
||
|
if (arrayOne[offsetOne + i] != arrayTwo[offsetTwo + i])
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// length is assumed to be non negative
|
||
|
//
|
||
|
|
||
|
public static int LengthSize(int length)
|
||
|
{
|
||
|
|
||
|
if (length < (1 << 7))
|
||
|
{
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
else if (length < (1 << 8))
|
||
|
{
|
||
|
|
||
|
return 2;
|
||
|
}
|
||
|
else if (length < (1 << 16))
|
||
|
{
|
||
|
|
||
|
return 3;
|
||
|
}
|
||
|
else if (length < (1 << 24))
|
||
|
{
|
||
|
|
||
|
return 4;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
|
||
|
return 5;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// fills in a buffer with the token header. The buffer is assumed
|
||
|
// to be the right size. buffer is advanced past the token header
|
||
|
//
|
||
|
// bodySize includes TokenId
|
||
|
//
|
||
|
|
||
|
public static void MakeTokenHeader(int bodySize, byte[] buffer, ref int offset, ref int len)
|
||
|
{
|
||
|
|
||
|
buffer[offset++] = 0x60;
|
||
|
len--;
|
||
|
|
||
|
WriteLength(buffer, ref offset, ref len, 1 + LengthSize(mech.Length) + mech.Length + type.Length + bodySize);
|
||
|
|
||
|
buffer[offset++] = 0x06; // OID
|
||
|
len--;
|
||
|
|
||
|
WriteLength(buffer, ref offset, ref len, mech.Length);
|
||
|
|
||
|
System.Buffer.BlockCopy(mech, 0, buffer, offset, mech.Length);
|
||
|
offset += mech.Length;
|
||
|
len -= mech.Length;
|
||
|
|
||
|
System.Buffer.BlockCopy(type, 0, buffer, offset, type.Length);
|
||
|
offset += type.Length;
|
||
|
len -= type.Length;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// returns decoded length, or < 0 on failure. Advances buffer and
|
||
|
// decrements length
|
||
|
//
|
||
|
|
||
|
public static int ReadLength(byte[] buffer, ref int offset, ref int length)
|
||
|
{
|
||
|
|
||
|
int tmp;
|
||
|
int ret = 0;
|
||
|
|
||
|
if (length < 1)
|
||
|
{
|
||
|
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SystemException());
|
||
|
}
|
||
|
|
||
|
tmp = buffer[offset++];
|
||
|
|
||
|
length--;
|
||
|
|
||
|
if ((tmp & 0x80) != 0)
|
||
|
{
|
||
|
|
||
|
if ((tmp &= 0x7f) > (length - 1))
|
||
|
{
|
||
|
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SystemException());
|
||
|
}
|
||
|
|
||
|
if (tmp > 4)
|
||
|
{ // 4 == sizeof(int)
|
||
|
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SystemException());
|
||
|
}
|
||
|
|
||
|
for (; tmp != 0; tmp--)
|
||
|
{
|
||
|
|
||
|
ret = (ret << 8) + buffer[offset++];
|
||
|
length--;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
|
||
|
ret = tmp;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// returns the length of a token, given the mech oid and the body
|
||
|
// size
|
||
|
//
|
||
|
|
||
|
public static int TokenSize(int bodySize)
|
||
|
{
|
||
|
|
||
|
// set body size to sequence contents size, 2 for token id
|
||
|
bodySize += 2 + mech.Length + LengthSize(mech.Length) + 1;
|
||
|
|
||
|
return (1 + LengthSize(bodySize) + bodySize);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// given a buffer containing a token, reads and verifies the token,
|
||
|
// leaving buffer advanced past the token header, and setting body_size
|
||
|
// to the number of remaining bytes
|
||
|
//
|
||
|
|
||
|
public static void VerifyTokenHeader(byte[] buffer, ref int offset, ref int len)
|
||
|
{
|
||
|
|
||
|
if ((len -= 1) < 0)
|
||
|
{
|
||
|
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SystemException());
|
||
|
}
|
||
|
|
||
|
if (buffer[offset++] != 0x60)
|
||
|
{
|
||
|
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SystemException());
|
||
|
}
|
||
|
|
||
|
int seqSize = ReadLength(buffer, ref offset, ref len);
|
||
|
|
||
|
if (seqSize != len)
|
||
|
{
|
||
|
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SystemException());
|
||
|
}
|
||
|
|
||
|
if ((len -= 1) < 0)
|
||
|
{
|
||
|
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SystemException());
|
||
|
}
|
||
|
|
||
|
if (buffer[offset++] != 0x06)
|
||
|
{
|
||
|
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SystemException());
|
||
|
}
|
||
|
|
||
|
int oidLength = ReadLength(buffer, ref offset, ref len); // (byte) buffer[offset++];
|
||
|
|
||
|
if ((oidLength & 0x7fffffff) != mech.Length)
|
||
|
{ // Overflow???
|
||
|
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SystemException());
|
||
|
}
|
||
|
|
||
|
if ((len -= oidLength) < 0)
|
||
|
{
|
||
|
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SystemException());
|
||
|
}
|
||
|
|
||
|
if (!BufferIsEqual(mech, 0, buffer, offset, mech.Length))
|
||
|
{
|
||
|
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SystemException());
|
||
|
}
|
||
|
|
||
|
offset += oidLength;
|
||
|
|
||
|
if ((len -= type.Length) < 0)
|
||
|
{
|
||
|
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SystemException());
|
||
|
}
|
||
|
|
||
|
if (!BufferIsEqual(type, 0, buffer, offset, type.Length))
|
||
|
{
|
||
|
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SystemException());
|
||
|
}
|
||
|
|
||
|
offset += type.Length;
|
||
|
}
|
||
|
|
||
|
public static void WriteLength(byte[] buffer, ref int offset, ref int bufferLength, int length)
|
||
|
{
|
||
|
|
||
|
if (length < (1 << 7))
|
||
|
{ // one byte
|
||
|
|
||
|
// *(*buffer)++ = (unsigned char) length;
|
||
|
buffer[offset++] = (byte)length;
|
||
|
bufferLength--;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
|
||
|
// *(*buffer)++ = (unsigned char) (der_length_size(length) + 127);
|
||
|
buffer[offset++] = (byte)(LengthSize(length) + 127);
|
||
|
|
||
|
if (length >= (1 << 24))
|
||
|
{
|
||
|
|
||
|
// *(*buffer)++ = (unsigned char) (length >> 24);
|
||
|
buffer[offset++] = (byte)(length >> 24);
|
||
|
bufferLength--;
|
||
|
}
|
||
|
|
||
|
if (length >= (1 << 16))
|
||
|
{
|
||
|
|
||
|
// *(*buffer)++ = (unsigned char) ((length >> 16) & 0xff);
|
||
|
buffer[offset++] = (byte)((length >> 16) & 0xFF);
|
||
|
bufferLength--;
|
||
|
}
|
||
|
|
||
|
if (length >= (1 << 8))
|
||
|
{
|
||
|
|
||
|
// *(*buffer)++ = (unsigned char) ((length >> 8) & 0xff);
|
||
|
buffer[offset++] = (byte)((length >> 8) & 0xFF);
|
||
|
bufferLength--;
|
||
|
}
|
||
|
|
||
|
// *(*buffer)++ = (unsigned char) (length & 0xff);
|
||
|
buffer[offset++] = (byte)(length & 0xFF);
|
||
|
bufferLength--;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|