private static AsnEncodedData CreateTimestampToken(byte serial) { Oid tokenOid = new Oid(TokenAttributeOid, TokenAttributeOid); Oid policyId = new Oid("0.0", "0.0"); Oid hashAlgorithmId = new Oid(Oids.Sha256); var tokenInfo = new Rfc3161TimestampTokenInfo( policyId, hashAlgorithmId, new byte[256 / 8], new byte[] { (byte)serial }, DateTimeOffset.UtcNow); return(new AsnEncodedData(tokenOid, tokenInfo.Encode())); }
public static void CreateFromParameters(string testDataName) { TimestampTokenTestData testData = TimestampTokenTestData.GetTestData(testDataName); Oid policyId = new Oid(testData.PolicyId, testData.PolicyId); Oid hashAlgorithmOid = new Oid(testData.HashAlgorithmId); byte[] messageHash = testData.HashBytes.ToArray(); byte[] serial = testData.SerialNumberBytes.ToArray(); DateTimeOffset nonUtcTimestamp = testData.Timestamp.ToOffset(TimeSpan.FromHours(-8)); long? accuracyMicrosec = testData.AccuracyInMicroseconds; byte[] nonce = testData.NonceBytes?.ToArray(); byte[] tsaNameBytes = testData.TsaNameBytes?.ToArray(); ReadOnlyMemory <byte>?nonceMemory = null; ReadOnlyMemory <byte>?tsaMemory = null; if (nonce != null) { nonceMemory = nonce; } if (tsaNameBytes != null) { tsaMemory = tsaNameBytes; } var tokenInfo = new Rfc3161TimestampTokenInfo( policyId, hashAlgorithmOid, messageHash, serial, nonUtcTimestamp, accuracyMicrosec, testData.IsOrdering, nonceMemory, tsaMemory); // Since AssertEqual will check all the fields the remaining checks in this method are about // input/output value/reference associations. AssertEqual(testData, tokenInfo); Assert.NotSame(policyId, tokenInfo.PolicyId); Assert.NotSame(hashAlgorithmOid, tokenInfo.HashAlgorithmId); Assert.Equal(nonUtcTimestamp, tokenInfo.Timestamp); Assert.Equal(TimeSpan.Zero, tokenInfo.Timestamp.Offset); Assert.Equal(messageHash.ByteArrayToHex(), tokenInfo.GetMessageHash().ByteArrayToHex()); // Detached from the original data messageHash[0] ^= 0xFF; Assert.NotEqual(messageHash.ByteArrayToHex(), tokenInfo.GetMessageHash().ByteArrayToHex()); Assert.Equal(serial.ByteArrayToHex(), tokenInfo.GetSerialNumber().ByteArrayToHex()); // Detached from the original data serial[1] ^= 0xFF; Assert.NotEqual(serial.ByteArrayToHex(), tokenInfo.GetSerialNumber().ByteArrayToHex()); if (nonce != null) { ReadOnlyMemory <byte>?tokenNonce = tokenInfo.GetNonce(); Assert.True(tokenNonce.HasValue, "tokenInfo.GetNonce().HasValue"); Assert.Equal(nonce.ByteArrayToHex(), tokenNonce.Value.ByteArrayToHex()); // Detached from the original data nonce[0] ^= 0xFF; Assert.NotEqual(nonce.ByteArrayToHex(), tokenNonce.Value.ByteArrayToHex()); } ReadOnlyMemory <byte>?nameFromToken = tokenInfo.GetTimestampAuthorityName(); if (tsaNameBytes != null) { Assert.True(nameFromToken.HasValue, "nameFromToken.HasValue"); Assert.Equal(tsaNameBytes.ByteArrayToHex(), nameFromToken.Value.ByteArrayToHex()); // Detached from the original data tsaNameBytes[5] ^= 0xFF; Assert.NotEqual(tsaNameBytes.ByteArrayToHex(), nameFromToken.Value.ByteArrayToHex()); } if (testData.ExtensionsBytes == null) { Assert.False(tokenInfo.HasExtensions, "tokenInfo.HasExtensions"); Assert.NotNull(tokenInfo.GetExtensions()); Assert.Equal(0, tokenInfo.GetExtensions().Count); // GetExtensions always returns a new collection. Assert.NotSame(tokenInfo.GetExtensions(), tokenInfo.GetExtensions()); } else { Assert.True(tokenInfo.HasExtensions, "tokenInfo.HasExtensions"); Assert.NotNull(tokenInfo.GetExtensions()); Assert.True(false, "A test handler has been written for extensions..."); // GetExtensions always returns a new collection. Assert.NotSame(tokenInfo.GetExtensions(), tokenInfo.GetExtensions()); } // Because the token is DER encoded, we should produce byte-for-byte the same value. Assert.Equal(testData.TokenInfoBytes.ByteArrayToHex(), tokenInfo.Encode().ByteArrayToHex()); }
private static byte[] BuildCustomToken( CertLoader cert, DateTimeOffset timestamp, SigningCertificateOption v1Option, SigningCertificateOption v2Option, HashAlgorithmName v2DigestAlg = default, X509IncludeOption includeOption = X509IncludeOption.ExcludeRoot, SubjectIdentifierType identifierType = SubjectIdentifierType.IssuerAndSerialNumber) { long accuracyMicroSeconds = (long)(TimeSpan.FromMinutes(1).TotalMilliseconds * 1000); byte[] serialNumber = BitConverter.GetBytes(DateTimeOffset.UtcNow.Ticks); Array.Reverse(serialNumber); Rfc3161TimestampTokenInfo info = new Rfc3161TimestampTokenInfo( new Oid("0.0", "0.0"), new Oid(Oids.Sha384), new byte[384 / 8], serialNumber, timestamp, accuracyMicroSeconds, isOrdering: true); ContentInfo contentInfo = new ContentInfo(new Oid(Oids.TstInfo, Oids.TstInfo), info.Encode()); SignedCms cms = new SignedCms(contentInfo); using (X509Certificate2 tsaCert = cert.TryGetCertificateWithPrivateKey()) { CmsSigner signer = new CmsSigner(identifierType, tsaCert) { IncludeOption = includeOption }; if (v1Option != SigningCertificateOption.Omit) { ExpandOption(v1Option, out bool validHash, out bool skipIssuerSerial, out bool validName, out bool validSerial); // simple SigningCertificate byte[] signingCertificateV1Bytes = "301A3018301604140000000000000000000000000000000000000000".HexToByteArray(); if (validHash) { byte[] hash = SHA1.HashData(tsaCert.RawData); Buffer.BlockCopy( hash, 0, signingCertificateV1Bytes, signingCertificateV1Bytes.Length - hash.Length, hash.Length); } if (!skipIssuerSerial) { byte[] footer = BuildIssuerAndSerialNumber(tsaCert, validName, validSerial); signingCertificateV1Bytes[1] += (byte)footer.Length; signingCertificateV1Bytes[3] += (byte)footer.Length; signingCertificateV1Bytes[5] += (byte)footer.Length; Assert.InRange(signingCertificateV1Bytes[1], 0, 127); signingCertificateV1Bytes = signingCertificateV1Bytes.Concat(footer).ToArray(); } signer.SignedAttributes.Add( new AsnEncodedData("1.2.840.113549.1.9.16.2.12", signingCertificateV1Bytes)); } if (v2Option != SigningCertificateOption.Omit) { byte[] attrBytes; byte[] algBytes = Array.Empty <byte>(); byte[] hashBytes; byte[] issuerNameBytes = Array.Empty <byte>(); if (v2DigestAlg != default) { switch (v2DigestAlg.Name) { case "MD5": algBytes = "300C06082A864886F70D02050500".HexToByteArray(); break; case "SHA1": algBytes = "300906052B0E03021A0500".HexToByteArray(); break; case "SHA256": // Invalid under DER, because it's the default. algBytes = "300D06096086480165030402010500".HexToByteArray(); break; case "SHA384": algBytes = "300D06096086480165030402020500".HexToByteArray(); break; case "SHA512": algBytes = "300D06096086480165030402030500".HexToByteArray(); break; default: throw new NotSupportedException(v2DigestAlg.Name); } } else { v2DigestAlg = HashAlgorithmName.SHA256; } hashBytes = tsaCert.GetCertHash(v2DigestAlg); ExpandOption(v2Option, out bool validHash, out bool skipIssuerSerial, out bool validName, out bool validSerial); if (!validHash) { hashBytes[0] ^= 0xFF; } if (!skipIssuerSerial) { issuerNameBytes = BuildIssuerAndSerialNumber(tsaCert, validName, validSerial); } // hashBytes hasn't been wrapped in an OCTET STRING yet, so add 2 more. int payloadSize = algBytes.Length + hashBytes.Length + issuerNameBytes.Length + 2; Assert.InRange(payloadSize, 0, 123); attrBytes = new byte[payloadSize + 6]; int index = 0; // SEQUENCE (SigningCertificateV2) attrBytes[index++] = 0x30; attrBytes[index++] = (byte)(payloadSize + 4); // SEQUENCE OF => certs attrBytes[index++] = 0x30; attrBytes[index++] = (byte)(payloadSize + 2); // SEQUENCE (ESSCertIdV2) attrBytes[index++] = 0x30; attrBytes[index++] = (byte)payloadSize; Buffer.BlockCopy(algBytes, 0, attrBytes, index, algBytes.Length); index += algBytes.Length; // OCTET STRING (Hash) attrBytes[index++] = 0x04; attrBytes[index++] = (byte)hashBytes.Length; Buffer.BlockCopy(hashBytes, 0, attrBytes, index, hashBytes.Length); index += hashBytes.Length; Buffer.BlockCopy(issuerNameBytes, 0, attrBytes, index, issuerNameBytes.Length); signer.SignedAttributes.Add( new AsnEncodedData("1.2.840.113549.1.9.16.2.47", attrBytes)); } cms.ComputeSignature(signer); } return(cms.Encode()); }