lib_unity_purchase/Runtime/Security/PKCS7.cs

241 lines
8.2 KiB
C#
Raw Permalink Normal View History

2024-01-29 18:49:33 +08:00
using System;
using System.Collections;
using System.Collections.Generic;
using LipingShare.LCLib.Asn1Processor;
using System.Security.Cryptography;
namespace UnityEngine.Purchasing.Security
{
internal class PKCS7
{
private Asn1Node root;
public Asn1Node data { get; private set; }
public List<SignerInfo> sinfos { get; private set; }
public List<X509Cert> certChain { get; private set; }
private bool validStructure;
public static PKCS7 Load(byte[] data)
{
using (var stm = new System.IO.MemoryStream(data))
{
Asn1Parser parser = new Asn1Parser();
parser.LoadData(stm);
return new PKCS7(parser.RootNode);
}
}
public PKCS7(Asn1Node node)
{
this.root = node;
CheckStructure();
}
public bool Verify(X509Cert cert, DateTime certificateCreationTime)
{
if (validStructure)
{
bool ok = true;
foreach (var sinfo in sinfos)
{
X509Cert signCert = FindSignCert(sinfo);
if (signCert != null && signCert.PubKey != null)
{
ok = ok && signCert.CheckCertTime(certificateCreationTime);
if (IsStoreKitSimulatorData())
{
ok = ok && signCert.PubKey.VerifySha256(data.GetChildNode(0).Data, sinfo.EncryptedDigest);
ok = ok && ValidateStoreKitSimulatorCertRoot(cert, signCert);
}
else
{
ok = ok && VerifyPublicKeyWithSha256OrSha1(signCert, sinfo);
ok = ok && ValidateChain(cert, signCert, certificateCreationTime);
}
}
}
return ok && sinfos.Count > 0;
}
return false;
}
X509Cert FindSignCert(SignerInfo sinfo)
{
foreach (var cert in certChain)
{
if (cert.SerialNumber == sinfo.IssuerSerialNumber)
{
return cert;
}
}
return null;
}
bool IsStoreKitSimulatorData()
{
return data.IsIndefiniteLength && data.ChildNodeCount == 1;
}
bool VerifyPublicKeyWithSha256OrSha1(X509Cert signCert, SignerInfo sinfo)
{
if (signCert.PubKey.VerifySha256(data.Data, sinfo.EncryptedDigest))
{
return true;
}
return signCert.PubKey.VerifySha1(data.Data, sinfo.EncryptedDigest);
}
static bool ValidateStoreKitSimulatorCertRoot(X509Cert root, X509Cert cert)
{
return cert.CheckSignatureSha256(root);
}
private bool ValidateChain(X509Cert root, X509Cert cert, DateTime certificateCreationTime)
{
if (cert.Issuer.Equals(root.Subject))
{
return cert.CheckSignature(root);
}
/**
* TODO: improve this logic
*/
foreach (var c in certChain)
{
if (c != cert && c.Subject.Equals(cert.Issuer) && c.CheckCertTime(certificateCreationTime))
{
if (c.Issuer.Equals(root.Subject) && c.SerialNumber == root.SerialNumber)
{
return c.CheckSignature(root);
}
else
{
// cert was issued by c
if (cert.CheckSignature(c))
{
return ValidateChain(root, c, certificateCreationTime);
}
}
}
}
return false;
}
private void CheckStructure()
{
validStructure = false;
if ((root.Tag & Asn1Tag.TAG_MASK) == Asn1Tag.SEQUENCE &&
root.ChildNodeCount == 2)
{
Asn1Node tt = root.GetChildNode(0);
if ((tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.OBJECT_IDENTIFIER ||
tt.GetDataStr(false) != "1.2.840.113549.1.7.2")
{
throw new InvalidPKCS7Data();
}
tt = root.GetChildNode(1); // [0]
if (tt.ChildNodeCount != 1)
throw new InvalidPKCS7Data();
int curChild = 0;
tt = tt.GetChildNode(curChild++); // Seq
if (tt.ChildNodeCount < 4 || (tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SEQUENCE)
throw new InvalidPKCS7Data();
Asn1Node tt2 = tt.GetChildNode(0); // version
if ((tt2.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.INTEGER)
throw new InvalidPKCS7Data();
tt2 = tt.GetChildNode(curChild++); // digest algo
// TODO: check algo
if ((tt2.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SET)
throw new InvalidPKCS7Data();
tt2 = tt.GetChildNode(curChild++); // pkcs7 data
if ((tt2.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SEQUENCE && tt2.ChildNodeCount != 2)
throw new InvalidPKCS7Data();
data = tt2.GetChildNode(1).GetChildNode(0);
if (tt.ChildNodeCount == 5)
{
// cert chain, this is optional
certChain = new List<X509Cert>();
tt2 = tt.GetChildNode(curChild++);
if (tt2.ChildNodeCount == 0)
throw new InvalidPKCS7Data();
for (int i = 0; i < tt2.ChildNodeCount; i++)
{
certChain.Add(new X509Cert(tt2.GetChildNode(i)));
}
}
tt2 = tt.GetChildNode(curChild++); // signer's info
if ((tt2.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SET || tt2.ChildNodeCount == 0)
throw new InvalidPKCS7Data();
sinfos = new List<SignerInfo>();
for (int i = 0; i < tt2.ChildNodeCount; i++)
{
sinfos.Add(new SignerInfo(tt2.GetChildNode(i)));
}
validStructure = true;
}
}
}
internal class SignerInfo
{
public int Version { get; private set; }
public string IssuerSerialNumber { get; private set; }
public byte[] EncryptedDigest { get; private set; }
public SignerInfo(Asn1Node n)
{
if (n.ChildNodeCount != 5)
throw new InvalidPKCS7Data();
Asn1Node tt;
// version
tt = n.GetChildNode(0);
if ((tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.INTEGER)
throw new InvalidPKCS7Data();
Version = tt.Data[0];
if (Version != 1 || tt.Data.Length != 1)
throw new UnsupportedSignerInfoVersion();
// get the issuer SN
tt = n.GetChildNode(1);
if ((tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.SEQUENCE || tt.ChildNodeCount != 2)
throw new InvalidPKCS7Data();
tt = tt.GetChildNode(1);
if ((tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.INTEGER)
throw new InvalidPKCS7Data();
IssuerSerialNumber = Asn1Util.ToHexString(tt.Data);
// get the data
tt = n.GetChildNode(4);
if ((tt.Tag & Asn1Tag.TAG_MASK) != Asn1Tag.OCTET_STRING)
throw new InvalidPKCS7Data();
EncryptedDigest = tt.Data;
}
}
/// <summary>
/// An IAP Security exception indicating some invalid data for PKCS7 checks.
/// </summary>
public class InvalidPKCS7Data : IAPSecurityException { }
/// <summary>
/// An IAP Security exception indicating unsupported signer information.
/// </summary>
public class UnsupportedSignerInfoVersion : IAPSecurityException { }
}