lib_unity_purchase/Runtime/Security/Certificate.cs

261 lines
9.1 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using LipingShare.LCLib.Asn1Processor;
using System.Security.Cryptography;
namespace UnityEngine.Purchasing.Security
{
internal class DistinguishedName
{
public string Country { get; set; }
public string Organization { get; set; }
public string OrganizationalUnit { get; set; }
public string Dnq { get; set; }
public string State { get; set; }
public string CommonName { get; set; }
public string SerialNumber { get; set; }
public DistinguishedName(Asn1Node n)
{
/* Name:
* SET
* SEQ (attr)
* Object Identifier
* Printable String || UTF8String
*/
if (n.MaskedTag == Asn1Tag.SEQUENCE)
{
for (int i = 0; i < n.ChildNodeCount; i++)
{
Asn1Node tt = n.GetChildNode(i);
if (tt.MaskedTag != Asn1Tag.SET || tt.ChildNodeCount != 1)
throw new InvalidX509Data();
tt = tt.GetChildNode(0);
if (tt.MaskedTag != Asn1Tag.SEQUENCE || tt.ChildNodeCount != 2)
throw new InvalidX509Data();
Asn1Node oi = tt.GetChildNode(0);
Asn1Node txt = tt.GetChildNode(1);
if (oi.MaskedTag != Asn1Tag.OBJECT_IDENTIFIER ||
!(
(txt.MaskedTag == Asn1Tag.PRINTABLE_STRING) ||
(txt.MaskedTag == Asn1Tag.UTF8_STRING) ||
(txt.MaskedTag == Asn1Tag.IA5_STRING)))
{
throw new InvalidX509Data();
}
var xoid = new LipingShare.LCLib.Asn1Processor.Oid();
string oiName = xoid.Decode(oi.Data);
var enc = new System.Text.UTF8Encoding();
switch (oiName)
{
case "2.5.4.6": // countryName
Country = enc.GetString(txt.Data);
break;
case "2.5.4.10": // organizationName
Organization = enc.GetString(txt.Data);
break;
case "2.5.4.11": // organizationalUnit
OrganizationalUnit = enc.GetString(txt.Data);
break;
case "2.5.4.3": // commonName
CommonName = enc.GetString(txt.Data);
break;
case "2.5.4.5": // serial number
SerialNumber = Asn1Util.ToHexString(txt.Data);
break;
case "2.5.4.46": // dnq
Dnq = enc.GetString(txt.Data);
break;
case "2.5.4.8": // state
State = enc.GetString(txt.Data);
break;
}
}
}
}
public bool Equals(DistinguishedName n2)
{
return this.Organization == n2.Organization &&
this.OrganizationalUnit == n2.OrganizationalUnit &&
this.Dnq == n2.Dnq &&
this.Country == n2.Country &&
this.State == n2.State &&
this.CommonName == n2.CommonName;
}
public override string ToString()
{
return "CN: " + CommonName + "\n" +
"ON: " + Organization + "\n" +
"Unit Name: " + OrganizationalUnit + "\n" +
"Country: " + Country;
}
}
internal class X509Cert
{
public string SerialNumber { get; private set; }
public DateTime ValidAfter { get; private set; }
public DateTime ValidBefore { get; private set; }
public RSAKey PubKey { get; private set; }
public bool SelfSigned { get; private set; }
public DistinguishedName Subject { get; private set; }
public DistinguishedName Issuer { get; private set; }
private Asn1Node TbsCertificate;
public Asn1Node Signature { get; private set; }
public byte[] rawTBSCertificate;
public X509Cert(Asn1Node n)
{
ParseNode(n);
}
public X509Cert(byte[] data)
{
using (var stm = new System.IO.MemoryStream(data))
{
Asn1Parser parser = new Asn1Parser();
parser.LoadData(stm);
ParseNode(parser.RootNode);
}
}
public bool CheckCertTime(DateTime time)
{
return time.CompareTo(ValidAfter) >= 0 && time.CompareTo(ValidBefore) <= 0;
}
public bool CheckSignature(X509Cert signer)
{
if (Issuer.Equals(signer.Subject))
{
return VerifySignatureWithSha256OrSha1(signer);
}
return false;
}
bool VerifySignatureWithSha256OrSha1(X509Cert signer)
{
if (signer.PubKey.VerifySha256(rawTBSCertificate, Signature.Data))
{
return true;
}
return signer.PubKey.VerifySha1(rawTBSCertificate, Signature.Data);
}
public bool CheckSignatureSha256(X509Cert signer)
{
if (Issuer.Equals(signer.Subject))
{
return signer.PubKey.VerifySha256(rawTBSCertificate, Signature.Data);
}
return false;
}
private void ParseNode(Asn1Node root)
{
if ((root.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SEQUENCE || root.ChildNodeCount != 3)
throw new InvalidX509Data();
// TBS cert
TbsCertificate = root.GetChildNode(0);
if (TbsCertificate.ChildNodeCount < 7)
throw new InvalidX509Data();
rawTBSCertificate = new byte[TbsCertificate.DataLength + 4];
Array.Copy(root.Data, 0, rawTBSCertificate, 0, rawTBSCertificate.Length);
// get the serial number
Asn1Node sn = TbsCertificate.GetChildNode(1);
if ((sn.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.INTEGER)
throw new InvalidX509Data();
SerialNumber = Asn1Util.ToHexString(sn.Data);
// get the issuer
Issuer = new DistinguishedName(TbsCertificate.GetChildNode(3));
// get the subject
Subject = new DistinguishedName(TbsCertificate.GetChildNode(5));
// get the dates
Asn1Node validTimes = TbsCertificate.GetChildNode(4);
if ((validTimes.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SEQUENCE || validTimes.ChildNodeCount != 2)
throw new InvalidX509Data();
ValidAfter = ParseTime(validTimes.GetChildNode(0));
ValidBefore = ParseTime(validTimes.GetChildNode(1));
// is this self signed?
SelfSigned = Subject.Equals(Issuer);
// get the pub key
PubKey = new RSAKey(TbsCertificate.GetChildNode(6));
// set the tbs cert & signature data for signature verification
Signature = root.GetChildNode(2);
}
/**
* According to rfc5280, time should be specified in GMT:
* https://tools.ietf.org/html/rfc5280#section-4.1.2.5
*/
private DateTime ParseTime(Asn1Node n)
{
string time = (new System.Text.UTF8Encoding()).GetString(n.Data);
if (!(time.Length == 13 || time.Length == 15))
throw new InvalidTimeFormat();
// only accept Zulu time
if (time[time.Length - 1] != 'Z')
throw new InvalidTimeFormat();
int curIdx = 0;
int year = 0;
if (time.Length == 13)
{
year = Int32.Parse(time.Substring(0, 2));
if (year >= 50)
year += 1900;
else if (year < 50)
year += 2000;
curIdx += 2;
}
else
{
year = Int32.Parse(time.Substring(0, 4));
curIdx += 4;
}
int month = Int32.Parse(time.Substring(curIdx, 2)); curIdx += 2;
int dom = Int32.Parse(time.Substring(curIdx, 2)); curIdx += 2;
int hour = Int32.Parse(time.Substring(curIdx, 2)); curIdx += 2;
int min = Int32.Parse(time.Substring(curIdx, 2)); curIdx += 2;
int secs = Int32.Parse(time.Substring(curIdx, 2)); curIdx += 2;
return new DateTime(year, month, dom, hour, min, secs, DateTimeKind.Utc);
}
}
/// <summary>
/// An IAP Security exception indicating some invalid time format.
/// </summary>
public class InvalidTimeFormat : IAPSecurityException { }
/// <summary>
/// An IAP Security exception indicating some invalid data for X509 certification checks.
/// </summary>
public class InvalidX509Data : IAPSecurityException { }
}