private static SctVerificationResult VerifySctSignatureOverBytes(this SignedCertificateTimestamp sct, Log logServer, byte[] toVerify) { var(oid, sigAlg) = GetKeyAlgorithm(logServer.KeyBytes); //var signer = sigAlg switch //{ // CtSignatureAlgorithm.Ecdsa => SignerUtilities.GetSigner(Constants.Sha256WithEcdsa), // CtSignatureAlgorithm.Rsa => SignerUtilities.GetSigner(Constants.Sha256WithRsa), // _ => throw new NotImplementedException($"Signature algothrim '{sigAlg}' not supported, with OID '{oid}'"), //}; ISigner signer; if (sigAlg == CtSignatureAlgorithm.Ecdsa) { signer = SignerUtilities.GetSigner(Constants.Sha256WithEcdsa); } else if (sigAlg == CtSignatureAlgorithm.Rsa) { signer = SignerUtilities.GetSigner(Constants.Sha256WithRsa); } else { throw new NotImplementedException($"Signature algothrim '{sigAlg}' not supported, with OID '{oid}'"); } var pubKey = PublicKeyFactory.CreateKey(logServer.KeyBytes); signer.Init(false, pubKey); signer.BlockUpdate(toVerify, 0, toVerify.Length); var isValid = signer.VerifySignature(sct.Signature.SignatureData); return(isValid ? SctVerificationResult.Valid(sct.TimestampUtc, logServer.LogId, logServer.Description) : SctVerificationResult.FailedVerification(sct.TimestampUtc, logServer.LogId, logServer.Description, "Invalid Signature")); }
internal static SctVerificationResult VerifySctOverPreCertificate(this SignedCertificateTimestamp sct, Log logServer, X509Certificate2 certificate, IssuerInformation issuerInfo) { var preCertificateTbs = CreateTbsForVerification(certificate, issuerInfo); var toVerify = sct.SerialiseSignedSctDataForPreCertificate(preCertificateTbs.GetEncoded(), issuerInfo.KeyHash); return(sct.VerifySctSignatureOverBytes(logServer, toVerify)); }
private static void SerialiseCommonFields(BinaryWriter bw, SignedCertificateTimestamp sct) { if (sct.SctVersion != SctVersion.V1) { throw new InvalidOperationException("Can only serialise SCT v1!"); } bw.WriteLong((long)sct.SctVersion, Constants.VersionLength); bw.WriteLong(0, 1); // Certificate Timestamp bw.WriteLong(sct.TimestampMs, Constants.TimestampLength); }
private static byte[] SerialiseSignedSctData(this SignedCertificateTimestamp sct, X509Certificate2 certificate) { using var ms = new MemoryStream(); using var bw = new BinaryWriter(ms); SerialiseCommonFields(bw, sct); bw.WriteLong(0, Constants.LogEntryTypeLength); // X509 Entry bw.WriteVariableLength(certificate.RawData, Constants.CertificateMaxLength); bw.WriteVariableLength(sct.Extensions, Constants.ExtensionsMaxLength); return(ms.ToArray()); }
private static byte[] SerialiseSignedSctDataForPreCertificate(this SignedCertificateTimestamp sct, byte[] preCert, byte[] issuerKeyHash) { using var ms = new MemoryStream(); using var bw = new BinaryWriter(ms); SerialiseCommonFields(bw, sct); bw.WriteLong(1, Constants.LogEntryTypeLength); // PerCert Entry bw.Write(issuerKeyHash); bw.WriteVariableLength(preCert, Constants.CertificateMaxLength); bw.WriteVariableLength(sct.Extensions, Constants.ExtensionsMaxLength); return(ms.ToArray()); }
internal static SctVerificationResult VerifySignature(this SignedCertificateTimestamp sct, Log logServer, IList <X509Certificate2> chain) { if (logServer == null || sct == null || chain?.Any() != true || logServer.LogId != sct.LogIdBase64) { return(SctVerificationResult.FailedVerification(sct.TimestampUtc, logServer?.LogId, "Invalid verification arguments")); } var nowUtc = DateTime.UtcNow; if (sct.TimestampUtc > nowUtc) { return(SctVerificationResult.FutureTimestamp(sct.TimestampUtc, logServer.LogId)); } if (logServer.ValidUntilUtc.HasValue && sct.TimestampUtc > logServer.ValidUntilUtc) { return(SctVerificationResult.LogServerUntrusted(sct.TimestampUtc, logServer.LogId)); } try { var leafCert = chain.First(); var notPreCert = !leafCert.IsPreCertificate(); var noEmbeddedSct = !leafCert.HasEmbeddedSct(); if (notPreCert && noEmbeddedSct) { // When verifying final cert without embedded SCTs, we don't need the issuer but can verify directly var toVerify = sct.SerialiseSignedSctData(leafCert); return(sct.VerifySctSignatureOverBytes(logServer, toVerify)); } if (chain.Count < 2) { return(SctVerificationResult.FailedVerification(sct.TimestampUtc, logServer.LogId, "Chain with PreCertificate or Certificate must contain issuer")); } // PreCertificate or final certificate with embedded SCTs, we want the issuerInformation var issuerCert = chain[1]; var isPreCertificateSigningCert = issuerCert.IsPreCertificateSigningCert(); var issuerInformation = default(IssuerInformation); if (!isPreCertificateSigningCert) { issuerInformation = issuerCert.IssuerInformation(); } else if (chain.Count < 3) { return(SctVerificationResult.FailedVerification(sct.TimestampUtc, logServer.LogId, "Chain with PreCertificate signed by PreCertificate Signing Cert must contain issuer")); } else { issuerInformation = issuerCert.IssuerInformationFromPreCertificate(chain[2]); } return(VerifySctOverPreCertificate(sct, logServer, leafCert, issuerInformation)); } catch (Exception ex) { return(SctVerificationResult.FailedWithException(sct.TimestampUtc, logServer.LogId, ex)); } }
internal static List <SignedCertificateTimestamp> GetSignedCertificateTimestamps(this X509Certificate2 certificate) { // https://letsencrypt.org/2018/04/04/sct-encoding.html var result = new List <SignedCertificateTimestamp>(); #if DEBUG var sctExtension = certificate is MoqX509Certificate2 moqCert ? moqCert.Extensions .OfType <X509Extension>() .Where(i => i.Oid.Value.Equals(Constants.SctCertificateOid)) .FirstOrDefault() : certificate.GetExtension(Constants.SctCertificateOid); #else var sctExtension = certificate.GetExtension(Constants.SctCertificateOid); #endif if (sctExtension?.RawData?.Any() == true) { //var octets = Asn1OctetString.GetInstance(sctExtension.RawData).GetOctets(); //var asn1 = Asn1Object.FromByteArray(sctExtension.RawData); //var octets = Asn1OctetString.GetInstance(asn1).GetOctets(); var octets = sctExtension.RawData; // could be a nested OCTET string, check leading byte var derOctetString = octets[0] == 0x04 ? Asn1Object.FromByteArray(octets) as DerOctetString : Asn1Object.FromByteArray(sctExtension.RawData) as DerOctetString; using (var inputStream = derOctetString.GetOctetStream()) { TlsUtilities.ReadUint16(inputStream); while (inputStream.Length - inputStream.Position > 2) { var sctBytes = TlsUtilities.ReadOpaque16(inputStream); using (var sctStream = new MemoryStream(sctBytes)) { var version = (SctVersion)sctStream.ReadByte(); if (version != SctVersion.V1) { throw new NotSupportedException(UnknowError(nameof(SctVersion), version)); } var keyId = new byte[Constants.KeyIdLength]; sctStream.Read(keyId, 0, keyId.Length); var timestamp = sctStream.ReadLong(Constants.TimestampLength); var extensions = sctStream.ReadVariableLength(Constants.ExtensionsMaxLength); var hashAlgo = (CtHashAlgorithm)sctStream.ReadByte(); if (!Enum.IsDefined(typeof(CtHashAlgorithm), hashAlgo)) { throw new NotSupportedException(UnknowError(nameof(CtHashAlgorithm), hashAlgo)); } var signatureAlgo = (CtSignatureAlgorithm)sctStream.ReadByte(); if (!Enum.IsDefined(typeof(CtSignatureAlgorithm), signatureAlgo)) { throw new NotSupportedException(UnknowError(nameof(CtSignatureAlgorithm), signatureAlgo)); } var signature = sctStream.ReadVariableLength(Constants.SignatureMaxLength); var digitallySigned = new DigitallySigned() { Hash = hashAlgo, Signature = signatureAlgo, SignatureData = signature }; var sct = new SignedCertificateTimestamp() { SctVersion = version, LogId = keyId, TimestampMs = timestamp, Extensions = extensions, Signature = digitallySigned }; result.Add(sct); } } } } return(result); }