208 lines
6.6 KiB
C#
208 lines
6.6 KiB
C#
//
|
|
// ASN1Convert.cs: Abstract Syntax Notation 1 convertion routines
|
|
//
|
|
// Authors:
|
|
// Sebastien Pouliot <sebastien@ximian.com>
|
|
// Jesper Pedersen <jep@itplus.dk>
|
|
//
|
|
// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
|
|
// (C) 2004 IT+ A/S (http://www.itplus.dk)
|
|
// Copyright (C) 2004-2007 Novell, Inc (http://www.novell.com)
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
|
|
using System;
|
|
using System.Globalization;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
|
|
namespace Emby.Common.Implementations.Security
|
|
{
|
|
|
|
// References:
|
|
// a. ITU ASN.1 standards (free download)
|
|
// http://www.itu.int/ITU-T/studygroups/com17/languages/
|
|
|
|
public static class ASN1Convert {
|
|
// RFC3280, section 4.2.1.5
|
|
// CAs conforming to this profile MUST always encode certificate
|
|
// validity dates through the year 2049 as UTCTime; certificate validity
|
|
// dates in 2050 or later MUST be encoded as GeneralizedTime.
|
|
|
|
// Under 1.x this API requires a Local datetime to be provided
|
|
// Under 2.0 it will also accept a Utc datetime
|
|
static public ASN1 FromDateTime (DateTime dt)
|
|
{
|
|
if (dt.Year < 2050) {
|
|
// UTCTIME
|
|
return new ASN1 (0x17, Encoding.ASCII.GetBytes (
|
|
dt.ToUniversalTime ().ToString ("yyMMddHHmmss",
|
|
CultureInfo.InvariantCulture) + "Z"));
|
|
}
|
|
else {
|
|
// GENERALIZEDTIME
|
|
return new ASN1 (0x18, Encoding.ASCII.GetBytes (
|
|
dt.ToUniversalTime ().ToString ("yyyyMMddHHmmss",
|
|
CultureInfo.InvariantCulture) + "Z"));
|
|
}
|
|
}
|
|
|
|
static public ASN1 FromInt32 (Int32 value)
|
|
{
|
|
byte[] integer = BitConverterLE.GetBytes (value);
|
|
Array.Reverse (integer);
|
|
int x = 0;
|
|
while ((x < integer.Length) && (integer [x] == 0x00))
|
|
x++;
|
|
ASN1 asn1 = new ASN1 (0x02);
|
|
switch (x) {
|
|
case 0:
|
|
asn1.Value = integer;
|
|
break;
|
|
case 4:
|
|
asn1.Value = new byte [1];
|
|
break;
|
|
default:
|
|
byte[] smallerInt = new byte [4 - x];
|
|
Buffer.BlockCopy (integer, x, smallerInt, 0, smallerInt.Length);
|
|
asn1.Value = smallerInt;
|
|
break;
|
|
}
|
|
return asn1;
|
|
}
|
|
|
|
static public ASN1 FromOid (string oid)
|
|
{
|
|
if (oid == null)
|
|
throw new ArgumentNullException ("oid");
|
|
|
|
return new ASN1 (CryptoConfig.EncodeOID (oid));
|
|
}
|
|
|
|
static public ASN1 FromUnsignedBigInteger (byte[] big)
|
|
{
|
|
if (big == null)
|
|
throw new ArgumentNullException ("big");
|
|
|
|
// check for numbers that could be interpreted as negative (first bit)
|
|
if (big [0] >= 0x80) {
|
|
// in thie cas we add a new, empty, byte (position 0) so we're
|
|
// sure this will always be interpreted an unsigned integer.
|
|
// However we can't feed it into RSAParameters or DSAParameters
|
|
int length = big.Length + 1;
|
|
byte[] uinteger = new byte [length];
|
|
Buffer.BlockCopy (big, 0, uinteger, 1, length - 1);
|
|
big = uinteger;
|
|
}
|
|
return new ASN1 (0x02, big);
|
|
}
|
|
|
|
static public int ToInt32 (ASN1 asn1)
|
|
{
|
|
if (asn1 == null)
|
|
throw new ArgumentNullException ("asn1");
|
|
if (asn1.Tag != 0x02)
|
|
throw new FormatException ("Only integer can be converted");
|
|
|
|
int x = 0;
|
|
for (int i=0; i < asn1.Value.Length; i++)
|
|
x = (x << 8) + asn1.Value [i];
|
|
return x;
|
|
}
|
|
|
|
// Convert a binary encoded OID to human readable string representation of
|
|
// an OID (IETF style). Based on DUMPASN1.C from Peter Gutmann.
|
|
static public string ToOid (ASN1 asn1)
|
|
{
|
|
if (asn1 == null)
|
|
throw new ArgumentNullException ("asn1");
|
|
|
|
byte[] aOID = asn1.Value;
|
|
StringBuilder sb = new StringBuilder ();
|
|
// Pick apart the OID
|
|
byte x = (byte) (aOID[0] / 40);
|
|
byte y = (byte) (aOID[0] % 40);
|
|
if (x > 2) {
|
|
// Handle special case for large y if x = 2
|
|
y += (byte) ((x - 2) * 40);
|
|
x = 2;
|
|
}
|
|
sb.Append (x.ToString (CultureInfo.InvariantCulture));
|
|
sb.Append (".");
|
|
sb.Append (y.ToString (CultureInfo.InvariantCulture));
|
|
ulong val = 0;
|
|
for (x = 1; x < aOID.Length; x++) {
|
|
val = ((val << 7) | ((byte) (aOID [x] & 0x7F)));
|
|
if ( !((aOID [x] & 0x80) == 0x80)) {
|
|
sb.Append (".");
|
|
sb.Append (val.ToString (CultureInfo.InvariantCulture));
|
|
val = 0;
|
|
}
|
|
}
|
|
return sb.ToString ();
|
|
}
|
|
|
|
static public DateTime ToDateTime (ASN1 time)
|
|
{
|
|
if (time == null)
|
|
throw new ArgumentNullException ("time");
|
|
|
|
string t = Encoding.ASCII.GetString (time.Value);
|
|
// to support both UTCTime and GeneralizedTime (and not so common format)
|
|
string mask = null;
|
|
int year;
|
|
switch (t.Length) {
|
|
case 11:
|
|
// illegal format, still it's supported for compatibility
|
|
mask = "yyMMddHHmmZ";
|
|
break;
|
|
case 13:
|
|
// RFC3280: 4.1.2.5.1 UTCTime
|
|
year = Convert.ToInt16 (t.Substring (0, 2), CultureInfo.InvariantCulture);
|
|
// Where YY is greater than or equal to 50, the
|
|
// year SHALL be interpreted as 19YY; and
|
|
// Where YY is less than 50, the year SHALL be
|
|
// interpreted as 20YY.
|
|
if (year >= 50)
|
|
t = "19" + t;
|
|
else
|
|
t = "20" + t;
|
|
mask = "yyyyMMddHHmmssZ";
|
|
break;
|
|
case 15:
|
|
mask = "yyyyMMddHHmmssZ"; // GeneralizedTime
|
|
break;
|
|
case 17:
|
|
// another illegal format (990630000000+1000), again supported for compatibility
|
|
year = Convert.ToInt16 (t.Substring (0, 2), CultureInfo.InvariantCulture);
|
|
string century = (year >= 50) ? "19" : "20";
|
|
// ASN.1 (see ITU X.680 section 43.3) deals with offset differently than .NET
|
|
char sign = (t[12] == '+') ? '-' : '+';
|
|
t = String.Format ("{0}{1}{2}{3}{4}:{5}{6}", century, t.Substring (0, 12), sign,
|
|
t[13], t[14], t[15], t[16]);
|
|
mask = "yyyyMMddHHmmsszzz";
|
|
break;
|
|
}
|
|
return DateTime.ParseExact (t, mask, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
|
|
}
|
|
}
|
|
}
|