mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-28 22:55:49 +08:00
Encode and decode CRL time values as UTC or GeneralizedTime depending on the value of the year. (#1959)
- Follow RFC to encode time/date >=2050 as generalized time - Fix bug that encoded generalized time was not properly decoded
This commit is contained in:
@@ -327,13 +327,13 @@ namespace Opc.Ua.Security.Certificates
|
||||
crlWriter.WriteEncodedValue((ReadOnlySpan<byte>)IssuerName.RawData);
|
||||
|
||||
// this update
|
||||
crlWriter.WriteUtcTime(this.ThisUpdate);
|
||||
WriteTime(crlWriter, ThisUpdate);
|
||||
|
||||
if (NextUpdate != DateTime.MinValue &&
|
||||
NextUpdate > ThisUpdate)
|
||||
{
|
||||
// next update
|
||||
crlWriter.WriteUtcTime(NextUpdate);
|
||||
WriteTime(crlWriter, NextUpdate);
|
||||
}
|
||||
|
||||
// sequence to start the revoked certificates.
|
||||
@@ -384,6 +384,23 @@ namespace Opc.Ua.Security.Certificates
|
||||
return crlWriter.Encode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write either a UTC time or a Generalized time depending if DataTime is before or after 2050.
|
||||
/// </summary>
|
||||
/// <param name="writer">The writer to write to.</param>
|
||||
/// <param name="dateTime">The date time to write.</param>
|
||||
private static void WriteTime(AsnWriter writer, DateTime dateTime)
|
||||
{
|
||||
if (dateTime.Year < 2050)
|
||||
{
|
||||
writer.WriteUtcTime(dateTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteGeneralizedTime(dateTime);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Fields
|
||||
|
||||
@@ -220,7 +220,7 @@ namespace Opc.Ua.Security.Certificates
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decode the Tbs of the CRL.
|
||||
/// Decode the Tbs of the CRL.
|
||||
/// </summary>
|
||||
/// <param name="tbs">The raw TbsCertList of the CRL.</param>
|
||||
internal void DecodeCrl(byte[] tbs)
|
||||
@@ -262,15 +262,11 @@ namespace Opc.Ua.Security.Certificates
|
||||
m_issuerName = new X500DistinguishedName(seqReader.ReadEncodedValue().ToArray());
|
||||
|
||||
// thisUpdate
|
||||
m_thisUpdate = seqReader.ReadUtcTime().UtcDateTime;
|
||||
m_thisUpdate = ReadTime(seqReader, optional: false);
|
||||
|
||||
// nextUpdate is OPTIONAL
|
||||
var utcTag = new Asn1Tag(UniversalTagNumber.UtcTime);
|
||||
peekTag = seqReader.PeekTag();
|
||||
if (peekTag == utcTag)
|
||||
{
|
||||
m_nextUpdate = seqReader.ReadUtcTime().UtcDateTime;
|
||||
}
|
||||
m_nextUpdate = ReadTime(seqReader, optional: true);
|
||||
|
||||
|
||||
var seqTag = new Asn1Tag(UniversalTagNumber.Sequence, true);
|
||||
peekTag = seqReader.PeekTag();
|
||||
@@ -331,6 +327,34 @@ namespace Opc.Ua.Security.Certificates
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the time, UTC or local time
|
||||
/// </summary>
|
||||
/// <param name="asnReader"></param>
|
||||
/// <param name="optional"></param>
|
||||
/// <returns>The DateTime representing the tag</returns>
|
||||
private DateTime ReadTime(AsnReader asnReader, bool optional)
|
||||
{
|
||||
// determine if the time is UTC or GeneralizedTime time
|
||||
var timeTag = asnReader.PeekTag();
|
||||
if (timeTag.TagValue == Asn1Tag.UtcTime.TagValue)
|
||||
{
|
||||
return asnReader.ReadUtcTime().UtcDateTime;
|
||||
}
|
||||
else if (timeTag.TagValue == Asn1Tag.GeneralizedTime.TagValue)
|
||||
{
|
||||
return asnReader.ReadGeneralizedTime().LocalDateTime;
|
||||
}
|
||||
else if (optional)
|
||||
{
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new AsnContentException("The CRL contains an invalid time tag.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decode if RawData is yet undecoded.
|
||||
/// </summary>
|
||||
|
||||
@@ -211,6 +211,68 @@ namespace Opc.Ua.Security.Certificates.Tests
|
||||
Assert.True(x509Crl.VerifySignature(issuerPubKey, true));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate a CRL Builder and decoder pass on using utc and generalized times.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CrlUtcAndGeneralizedTimeTest()
|
||||
{
|
||||
// Generate a CRL with dates over 2050
|
||||
var dname = new X500DistinguishedName("CN=Test, O=OPC Foundation");
|
||||
var hash = HashAlgorithmName.SHA256;
|
||||
var crlBuilder = CrlBuilder.Create(dname, hash)
|
||||
.SetThisUpdate(DateTime.Today.AddYears(30))
|
||||
.SetNextUpdate(DateTime.Today.AddYears(30).AddDays(1));
|
||||
byte[] serial = new byte[] { 4, 5, 6, 7 };
|
||||
var revokedarray = new RevokedCertificate(serial);
|
||||
crlBuilder.RevokedCertificates.Add(revokedarray);
|
||||
string serstring = "45678910";
|
||||
var revokedstring = new RevokedCertificate(serstring);
|
||||
crlBuilder.RevokedCertificates.Add(revokedstring);
|
||||
crlBuilder.CrlExtensions.Add(X509Extensions.BuildCRLNumber(123));
|
||||
var crlEncoded = crlBuilder.Encode();
|
||||
Assert.NotNull(crlEncoded);
|
||||
var x509Crl = new X509CRL();
|
||||
x509Crl.DecodeCrl(crlEncoded);
|
||||
Assert.NotNull(x509Crl);
|
||||
Assert.NotNull(x509Crl.CrlExtensions);
|
||||
Assert.NotNull(x509Crl.RevokedCertificates);
|
||||
Assert.AreEqual(dname.RawData, x509Crl.IssuerName.RawData);
|
||||
Assert.AreEqual(crlBuilder.ThisUpdate, x509Crl.ThisUpdate);
|
||||
Assert.AreEqual(crlBuilder.NextUpdate, x509Crl.NextUpdate);
|
||||
Assert.AreEqual(2, x509Crl.RevokedCertificates.Count);
|
||||
Assert.AreEqual(serial, x509Crl.RevokedCertificates[0].UserCertificate);
|
||||
Assert.AreEqual(serstring, x509Crl.RevokedCertificates[1].SerialNumber);
|
||||
Assert.AreEqual(1, x509Crl.CrlExtensions.Count);
|
||||
Assert.AreEqual(hash, x509Crl.HashAlgorithmName);
|
||||
|
||||
// Generate a CRL with dates up-to 2050
|
||||
DateTime dateToEncode = DateTime.UtcNow.Date;
|
||||
crlBuilder = CrlBuilder.Create(dname, hash)
|
||||
.SetThisUpdate(dateToEncode)
|
||||
.SetNextUpdate(dateToEncode.AddDays(1));
|
||||
revokedarray = new RevokedCertificate(serial);
|
||||
crlBuilder.RevokedCertificates.Add(revokedarray);
|
||||
revokedstring = new RevokedCertificate(serstring);
|
||||
crlBuilder.RevokedCertificates.Add(revokedstring);
|
||||
crlBuilder.CrlExtensions.Add(X509Extensions.BuildCRLNumber(123));
|
||||
crlEncoded = crlBuilder.Encode();
|
||||
Assert.NotNull(crlEncoded);
|
||||
x509Crl = new X509CRL();
|
||||
x509Crl.DecodeCrl(crlEncoded);
|
||||
Assert.NotNull(x509Crl);
|
||||
Assert.NotNull(x509Crl.CrlExtensions);
|
||||
Assert.NotNull(x509Crl.RevokedCertificates);
|
||||
Assert.AreEqual(dname.RawData, x509Crl.IssuerName.RawData);
|
||||
Assert.AreEqual(crlBuilder.ThisUpdate, x509Crl.ThisUpdate);
|
||||
Assert.AreEqual(crlBuilder.NextUpdate, x509Crl.NextUpdate);
|
||||
Assert.AreEqual(2, x509Crl.RevokedCertificates.Count);
|
||||
Assert.AreEqual(serial, x509Crl.RevokedCertificates[0].UserCertificate);
|
||||
Assert.AreEqual(serstring, x509Crl.RevokedCertificates[1].SerialNumber);
|
||||
Assert.AreEqual(1, x509Crl.CrlExtensions.Count);
|
||||
Assert.AreEqual(hash, x509Crl.HashAlgorithmName);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
Reference in New Issue
Block a user