/// <summary> /// GetBLOB method implmentation /// </summary> public override Task <MetadataBLOBPayload> GetBLOB() { bool needrawblob = false; MetadataBLOBPayload result = null; BLOBPayloadInformations infos = new BLOBPayloadInformations(); if (HasBLOBPayloadCache()) { infos = GetBLOBPayloadCache(); if (string.IsNullOrEmpty(infos.BLOB)) { return(null); } int oldnumber = infos.Number; DateTime oldnextupdate = infos.NextUpdate; result = DeserializeAndValidateBlob(infos); if ((infos.Number > oldnumber) || (Convert.ToDateTime(infos.NextUpdate) > Convert.ToDateTime(oldnextupdate))) { needrawblob = true; } } else { return(null); } if (needrawblob) { if (string.IsNullOrEmpty(infos.BLOB)) { return(null); } SetBLOBPayloadCache(infos); } return(Task.FromResult(result)); }
/// <summary> /// RetreiveBlobProperties method implementation /// </summary> protected virtual BLOBPayloadInformations RetreiveBlobProperties(BLOBPayloadInformations infos) { Dictionary <string, string> dic = DecodeNewPayload(infos.BLOB); var xnumber = dic["Number"]; var xnextupdate = dic["NextUpdate"]; infos.Number = Convert.ToInt32(xnumber); infos.NextUpdate = Convert.ToDateTime(xnextupdate); return(infos); }
/// <summary> /// SetBLOBPayload method implmentation /// </summary> public void SetBLOBPayloadCache(BLOBPayloadInformations infos) { try { _manager.SetBLOBPayloadCache(infos); } catch (Exception e) { _log.WriteEntry(string.Format("Error on WebAdminService Service SetBLOBPayload method : {0}.", e.Message), EventLogEntryType.Error, 2010); } }
/// <summary> /// SetBLOBPayloadCache method implmentation /// </summary> public void SetBLOBPayloadCache(BLOBPayloadInformations infos) { RegistryKey ek = Registry.LocalMachine.OpenSubKey("Software\\MFA", true); ek.SetValue("BlobNumber", Convert.ToString(infos.Number), RegistryValueKind.String); ek.SetValue("BlobNextUpdate", infos.NextUpdate.ToString("yyyy-MM-dd"), RegistryValueKind.String); ek.SetValue("BlobDownload", Convert.ToInt32(infos.CanDownload), RegistryValueKind.DWord); File.WriteAllText(SystemUtilities.PayloadCacheFile, infos.BLOB); using (MailSlotClient mailslot = new MailSlotClient("BDC")) { mailslot.Text = Environment.MachineName; mailslot.SendNotification(NotificationsKind.ConfigurationReload); } }
/// <summary> /// Backgroud RefreshBLOBMethod method /// </summary> private void RefreshBLOBMethod() { using (_blobcanceller.Token.Register(Thread.CurrentThread.Abort)) { while (!_blobmustexit) { try { lock (_bloblock) { BLOBPayloadInformations infos = GetBLOBPayloadCache(); if (infos.CanDownload && ((DateTime.Now > Convert.ToDateTime(infos.NextUpdate)) || (!File.Exists(SystemUtilities.PayloadCacheFile)))) { try { infos.BLOB = GetRawBlob().Result; } catch { infos.BLOB = null; } if (!string.IsNullOrEmpty(infos.BLOB)) { int oldnumber = infos.Number; DateTime oldnextupdate = infos.NextUpdate; RetreiveBlobProperties(infos); if ((infos.Number > oldnumber) || (Convert.ToDateTime(infos.NextUpdate) > Convert.ToDateTime(oldnextupdate)) || (!File.Exists(SystemUtilities.PayloadCacheFile))) { SetBLOBPayloadCache(infos); } } } } Thread.Sleep(new TimeSpan(0, 12, 0, 0)); // every 12 hours } catch (ThreadAbortException ex) { _blobmustexit = true; _log.WriteEntry(string.Format("error on refresh BLOB method : {0}.", ex.Message), EventLogEntryType.Error, 1014); } catch (Exception ex) { _log.WriteEntry(string.Format("error on refresh BLOB method : {0}.", ex.Message), EventLogEntryType.Error, 1014); Thread.Sleep(new TimeSpan(0, 12, 0, 0)); // every 12 hours } } } }
/// <summary> /// GetBLOBPayloadCache method implementation /// </summary> protected virtual BLOBPayloadInformations GetBLOBPayloadCache() { BLOBPayloadInformations infos = new BLOBPayloadInformations(); RegistryKey ek = Registry.LocalMachine.OpenSubKey("Software\\MFA", false); infos.Number = Convert.ToInt32(ek.GetValue("BlobNumber", 0, RegistryValueOptions.None)); infos.NextUpdate = Convert.ToDateTime(ek.GetValue("BlobNextUpdate", "1970-01-01", RegistryValueOptions.None)); infos.CanDownload = Convert.ToBoolean(ek.GetValue("BlobDownload", 1, RegistryValueOptions.None)); if (File.Exists(SystemUtilities.PayloadCacheFile)) { infos.BLOB = File.ReadAllText(SystemUtilities.PayloadCacheFile); } else { infos.BLOB = null; } return(infos); }
/// <summary> /// SetBLOBPayloadCache method implmentation /// </summary> public static void SetBLOBPayloadCache(BLOBPayloadInformations infos) { WebAdminClient manager = new WebAdminClient(); manager.Initialize(); try { IWebAdminServices client = manager.Open(); try { client.SetBLOBPayloadCache(infos); } finally { manager.Close(client); } } finally { manager.UnInitialize(); } }
protected abstract MetadataBLOBPayload DeserializeAndValidateBlob(BLOBPayloadInformations infos);
/// <summary> /// SetBLOBPayloadCache method implementation /// </summary> protected virtual void SetBLOBPayloadCache(BLOBPayloadInformations infos) { WebAdminManagerClient.SetBLOBPayloadCache(infos); }
/// <summary> /// DeserializeAndValidateBlob method implementation /// </summary> protected override MetadataBLOBPayload DeserializeAndValidateBlob(BLOBPayloadInformations infos) { if (string.IsNullOrWhiteSpace(infos.BLOB)) { throw new ArgumentNullException(nameof(infos.BLOB)); } var jwtParts = infos.BLOB.Split('.'); if (jwtParts.Length != 3) { throw new ArgumentException("The JWT does not have the 3 expected components"); } var blobHeaderString = jwtParts.First(); var blobHeader = JObject.Parse(Encoding.UTF8.GetString(Base64Url.Decode(blobHeaderString))); var blobAlg = blobHeader["alg"]?.Value <string>(); if (blobAlg == null) { throw new ArgumentNullException("No alg value was present in the BLOB header."); } JArray x5cArray = blobHeader["x5c"] as JArray; if (x5cArray == null) { throw new Exception("No x5c array was present in the BLOB header."); } var keyStrings = x5cArray.Values <string>().ToList(); if (keyStrings.Count == 0) { throw new ArgumentException("No keys were present in the BLOB header."); } var rootCert = GetX509Certificate(ROOT_CERT); var blobCerts = keyStrings.Select(o => GetX509Certificate(o)).ToArray(); var keys = new List <SecurityKey>(); foreach (var certString in keyStrings) { var cert = GetX509Certificate(certString); var ecdsaPublicKey = cert.GetECDsaPublicKey(); if (ecdsaPublicKey != null) { keys.Add(new ECDsaSecurityKey(ecdsaPublicKey)); continue; } var rsaPublicKey = cert.GetRSAPublicKey(); if (rsaPublicKey != null) { keys.Add(new RsaSecurityKey(rsaPublicKey)); continue; } throw new MetadataException("Unknown certificate algorithm"); } var blobPublicKeys = keys.ToArray(); var certChain = new X509Chain(); certChain.ChainPolicy.ExtraStore.Add(rootCert); certChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; var validationParameters = new TokenValidationParameters { ValidateIssuer = false, ValidateAudience = false, ValidateLifetime = false, ValidateIssuerSigningKey = true, IssuerSigningKeys = blobPublicKeys, }; var tokenHandler = new JwtSecurityTokenHandler() { // 250k isn't enough bytes for conformance test tool // https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/1097 MaximumTokenSizeInBytes = infos.BLOB.Length }; tokenHandler.ValidateToken( infos.BLOB, validationParameters, out var validatedToken); if (blobCerts.Length > 1) { certChain.ChainPolicy.ExtraStore.AddRange(blobCerts.Skip(1).ToArray()); } var certChainIsValid = certChain.Build(blobCerts.First()); // if the root is trusted in the context we are running in, valid should be true here if (!certChainIsValid) { // otherwise we have to manually validate that the root in the chain we are testing is the root we downloaded if (rootCert.Thumbprint == certChain.ChainElements[certChain.ChainElements.Count - 1].Certificate.Thumbprint && // and that the number of elements in the chain accounts for what was in x5c plus the root we added certChain.ChainElements.Count == (keyStrings.Count + 1) && // and that the root cert has exactly one status listed against it certChain.ChainElements[certChain.ChainElements.Count - 1].ChainElementStatus.Length == 1 && // and that that status is a status of exactly UntrustedRoot certChain.ChainElements[certChain.ChainElements.Count - 1].ChainElementStatus[0].Status == X509ChainStatusFlags.UntrustedRoot) { // if we are good so far, that is a good sign certChainIsValid = true; for (var i = 0; i < certChain.ChainElements.Count - 1; i++) { // check each non-root cert to verify zero status listed against it, otherwise, invalidate chain if (0 != certChain.ChainElements[i].ChainElementStatus.Length) { certChainIsValid = false; } } } } if (!certChainIsValid) { throw new VerificationException("Failed to validate cert chain while parsing BLOB"); } var blobPayload = ((JwtSecurityToken)validatedToken).Payload.SerializeToJson(); var blob = JsonConvert.DeserializeObject <MetadataBLOBPayload>(blobPayload); infos.Number = blob.Number; infos.NextUpdate = Convert.ToDateTime(blob.NextUpdate); infos.CanDownload = false; // Constrained Repository blob.JwtAlg = blobAlg; return(blob); }