Partager via


Configurer des notifications de modification Microsoft Graph avec des données de ressources

Microsoft Graph permet aux applications de s’abonner aux ressources et de recevoir des notifications concernant les modifications apportées aux ressources. Cet article explique comment configurer des notifications enrichies, qui incluent des données de ressources directement dans la charge utile de notification.

Les notifications enrichies suppriment le besoin d’appels d’API supplémentaires pour obtenir des ressources mises à jour, ce qui rend l’exécution plus rapide et plus facile de la logique métier.

Ressources prises en charge

Des notifications enrichies sont disponibles pour les ressources suivantes.

Remarque

Les notifications enrichies pour les abonnements aux points de terminaison marqués d’un astérisque (*) sont uniquement disponibles sur le point de /beta terminaison.

Resource Chemins d’accès aux ressources pris en charge Limitations
Copilot aiInteraction Interactions de Copilot AI dont un utilisateur particulier fait partie : copilot/users/{userId}/interactionHistory/getAllEnterpriseInteractions

Interactions de Copilot AI dans un organization :copilot/interactionHistory/getAllEnterpriseInteractions
Quotas maximaux d'abonnement :
  • Par combinaison d’application et de locataire (pour les abonnements qui suivent les interactions ia sur un locataire) : 1
  • Par combinaison d’applications et d’utilisateurs (pour les abonnements suivi des interactions IA dont fait partie un utilisateur particulier) : 1
  • Par utilisateur (pour les abonnements suivi des interactions IA dont fait partie un utilisateur particulier) : 10 abonnements.
  • Par organization : 10 000 abonnements au total.
  • événement Outlook Modifications apportées à tous les événements dans la boîte aux lettres d’un utilisateur : /users/{id}/events Nécessite $select de retourner uniquement un sous-ensemble de propriétés dans la notification enrichie. Pour plus d’informations, voir Notifications de modification pour les ressources Outlook.
    message Outlook Modifications apportées à tous les messages dans la boîte aux lettres d’un utilisateur : /users/{id}/messages

    Modifications apportées aux messages dans la boîte de réception d’un utilisateur : /users/{id}/mailFolders/{id}/messages
    Nécessite $select de retourner uniquement un sous-ensemble de propriétés dans la notification enrichie. Pour plus d’informations, voir Notifications de modification pour les ressources Outlook.
    contact personnel Outlook Modifications apportées à tous les contacts personnels dans la boîte aux lettres d’un utilisateur : /users/{id}/contacts

    Modifications apportées à tous les contacts personnels dans le dossier contactFolder d’un utilisateur : /users/{id}/contactFolders/{id}/contacts
    Nécessite $select de retourner uniquement un sous-ensemble de propriétés dans la notification enrichie. Pour plus d’informations, voir Notifications de modification pour les ressources Outlook.
    CallRecording Teams Tous les enregistrements d’un organization :communications/onlineMeetings/getAllRecordings

    Tous les enregistrements d’une réunion spécifique : communications/onlineMeetings/{onlineMeetingId}/recordings

    Enregistrement d’appel qui devient disponible dans une réunion organisée par un utilisateur spécifique : users/{id}/onlineMeetings/getAllRecordings

    Enregistrement d’appel qui devient disponible dans une réunion où une application Teams particulière est installée : appCatalogs/teamsApps/{id}/installedToOnlineMeetings/getAllRecordings *
    Quotas maximaux d'abonnement :
  • Par application et combinaison de réunion en ligne : 1
  • Par combinaison d’application et d’utilisateur : 1
  • Par utilisateur (pour les enregistrements de suivi des abonnements dans tous les onlineMeetings organisés par l’utilisateur) : 10 abonnements.
  • Par organization : 10 000 abonnements au total.
  • Teams callTranscript Toutes les transcriptions d’un organization :communications/onlineMeetings/getAllTranscripts

    Toutes les transcriptions d’une réunion spécifique : communications/onlineMeetings/{onlineMeetingId}/transcripts

    Transcription d’appel qui devient disponible dans une réunion organisée par un utilisateur spécifique : users/{id}/onlineMeetings/getAllTranscripts

    Transcription d’appel qui devient disponible dans une réunion où une application Teams particulière est installée : appCatalogs/teamsApps/{id}/installedToOnlineMeetings/getAllTrancripts *
    Quotas maximaux d'abonnement :
  • Par application et combinaison de réunion en ligne : 1
  • Par combinaison d’application et d’utilisateur : 1
  • Par utilisateur (pour les transcriptions de suivi des abonnements dans tous les onlineMeetings organisés par l’utilisateur) : 10 abonnements.
  • Par organization : 10 000 abonnements au total.
  • Canal Teams Modifications apportées aux canaux dans toutes les équipes : /teams/getAllChannels

    Modifications apportées au canal dans une équipe spécifique : /teams/{id}/channels
    -
    Conversation Teams Modifications apportées à n’importe quelle conversation dans le locataire : /chats

    Modifications apportées à une conversation spécifique : /chats/{id}
    -
    chatmessage Teams Modifications apportées aux messages de conversation dans tous les canaux de toutes les équipes : /teams/getAllMessages

    Modifications apportées aux messages de conversation dans un canal spécifique : /teams/{id}/channels/{id}/messages

    Modifications apportées aux messages de conversation dans toutes les conversations : /chats/getAllMessages

    Modifications apportées aux messages de conversation dans une conversation spécifique : /chats/{id}/messages

    Modifications apportées aux messages de conversation dans toutes les conversations dont un utilisateur particulier fait partie : /users/{id}/chats/getAllMessages
    Ne prend pas en charge l’utilisation $select de pour retourner uniquement les propriétés sélectionnées. La notification enrichie se compose de toutes les propriétés du instance modifié.
    conversationMember Teams Modifications apportées à l’appartenance à une équipe spécifique : /teams/{id}/members

    Modifications apportées à l’appartenance à toutes les équipes dans le locataire : /teams/getAllMembers

    Modifications apportées à l’appartenance à tous les canaux d’une équipe spécifique : /teams/{id}/channels/getAllMembers

    Modifications apportées à l’appartenance à tous les canaux sur l’ensemble du locataire : /teams/getAllChannels/getAllMembers

    Modifications apportées à l’appartenance à une conversation spécifique : /chats/{id}/members

    Modifications apportées à l’appartenance pour toutes les conversations Teams : /chats/getAllMembers
    Ne prend pas en charge l’utilisation $select de pour retourner uniquement les propriétés sélectionnées. La notification enrichie se compose de toutes les propriétés du instance modifié.
    Teams onlineMeeting * Modifications apportées à une réunion en ligne : /communications/onlineMeetings(joinWebUrl='{encodedJoinWebUrl}')/meetingCallEvents * Ne prend pas en charge l’utilisation $select de pour retourner uniquement les propriétés sélectionnées. La notification enrichie se compose de toutes les propriétés du instance modifié. Un abonnement autorisé par application et par réunion en ligne. Pour plus d’informations, consultez Obtenir des notifications de modification pour les mises à jour des événements d’appel de réunion Microsoft Teams.
    présenceTeams Modifications apportées à la présence d’un seul utilisateur : /communications/presences/{id}

    Modifications apportées à la présence de plusieurs utilisateurs : /communications/presences?$filter=id in ({id},{id}...)
    L’abonnement pour la présence de plusieurs utilisateurs est limité à 650 utilisateurs distincts. Ne prend pas en charge l’utilisation $select de pour retourner uniquement les propriétés sélectionnées. La notification enrichie se compose de toutes les propriétés du instance modifié. Un abonnement autorisé par application et par utilisateur délégué. Pour plus d’informations, consultez Obtenir des notifications de modification pour les mises à jour de présence dans Microsoft Teams.
    Équipe Teams Modifications apportées à n’importe quelle équipe dans le locataire : /teams

    Modifications apportées à une équipe spécifique : /teams/{id}
    -

    Données de ressources dans la charge utile de notification

    Les notifications enrichies incluent des données de ressources avec les détails suivants :

    • ID et type de la ressource modifiée instance, trouvés dans la propriété resourceData.
    • Toutes les valeurs de propriété de la ressource instance, chiffrées comme spécifié dans l’abonnement, se trouvent dans la propriété encryptedContent.
    • Propriétés spécifiques de la ressource, en fonction de la ressource, ou si demandé à l’aide d’un $select paramètre dans l’URL de ressource de l’abonnement.

    Création d’un abonnement

    Pour configurer des notifications enrichies, suivez les mêmes étapes que les notifications de modification de base, mais incluez les propriétés requises suivantes :

    • includeResourceData : définissez cette true valeur sur pour demander des données de ressource.
    • encryptionCertificate : fournissez la clé publique que Microsoft Graph utilise pour chiffrer les données de ressource. Pour plus d’informations, consultez Déchiffrement des données de ressources à partir de notifications de modification.
    • encryptionCertificateId : fournissez un identificateur pour que le certificat corresponde aux notifications avec la clé de déchiffrement correcte.

    Validez les deux points de terminaison comme décrit dans validation de point de terminaison de notification. Si vous utilisez la même URL pour les deux points de terminaison, vous recevez et devez répondre à deux demandes de validation.

    Exemple : demande d’abonnement

    Cet exemple crée un abonnement pour les messages de canal dans Microsoft Teams.

    POST https://graph.microsoft.com/v1.0/subscriptions
    Content-Type: application/json
    
    {
      "changeType": "created,updated",
      "notificationUrl": "https://webhook.azurewebsites.net/api/resourceNotifications",
      "resource": "/teams/{id}/channels/{id}/messages",
      "includeResourceData": true,
      "encryptionCertificate": "{base64encodedCertificate}",
      "encryptionCertificateId": "{customId}",
      "expirationDateTime": "2019-09-19T11:00:00.0000000Z",
      "clientState": "{secretClientState}"
    }
    

    Réponse de l’abonnement

    HTTP/1.1 201 Created
    Content-Type: application/json
    
    {
      "changeType": "created,updated",
      "notificationUrl": "https://webhook.azurewebsites.net/api/resourceNotifications",
      "resource": "/teams/{id}/channels/{id}/messages",
      "includeResourceData": true,
      "encryptionCertificateId": "{customId}",
      "expirationDateTime": "2019-09-19T11:00:00.0000000Z",
      "clientState": "{secretClientState}"
    }
    

    Notifications de cycle de vie d’abonnement

    Les événements peuvent perturber le flux des notifications de modification dans un abonnement. Les notifications de cycle de vie vous indiquent les actions à entreprendre pour maintenir le flux ininterrompu. Contrairement aux notifications de modification des ressources, les notifications de cycle de vie se concentrent sur l’état de l’abonnement.

    Pour plus d’informations, consultez Réduire les abonnements manquants et les notifications de modification.

    Valider l’authenticité des notifications

    Vérifiez toujours l’authenticité des notifications de modification avant de les traiter. Cela empêche votre application de déclencher une logique métier incorrecte en utilisant de fausses notifications de tiers.

    Pour les notifications de base, validez-les à l’aide de la valeur clientState , comme expliqué dans Traitement de la notification de modification. Pour les notifications enrichies, effectuez des étapes de validation supplémentaires.

    Jetons de validation dans la notification de modification

    Les notifications enrichies incluent une propriété validationTokens , qui contient un tableau de jetons web JSON (JWT). Chaque jeton est propre à la paire application-locataire. Une notification de modification peut contenir une combinaison d’éléments pour différentes applications et locataires qui se sont abonnés à l’aide du même objet notificationUrl.

    Remarque

    Microsoft Graph n’envoie pas de jetons de validation pour les notifications de modification remises via Azure Event Hubs, car le service d’abonnement n’a pas besoin de valider la notificationUrl pour Event Hubs.

    Dans l’exemple suivant, la notification de modification contient deux éléments pour la même application, et pour deux clients différents ; par conséquent, la matrice validationTokens contient deux jetons qui doivent être validés.

    {
        "value": [
            {
                "subscriptionId": "76619225-ff6b-4489-96ca-4ef547e78b22",
                "tenantId": "aaaabbbb-0000-cccc-1111-dddd2222eeee",
                "changeType": "created",
                ...
            },
            {
                "subscriptionId": "5cfe2387-163c-4006-81bb-1b5e1e060afe",
                "tenantId": "bbbbcccc-1111-dddd-2222-eeee3333ffff",
                "changeType": "created",
                ...
            }
        ],
        "validationTokens": [
            "eyJ0eXAiOiJKV1QiLCJhb...",
            "cGlkYWNyIjoiMiIsImlkc..."
        ]
    }
    

    L’objet de notification de modification se trouve dans la structure du type de ressource changeNotificationCollection.

    Comment valider

    Utilisez la bibliothèque d’authentification Microsoft (MSAL) ou une bibliothèque tierce pour valider les jetons. Procédez comme suit :

    Gardez à l’esprit les principes suivants :

    • Répondez immédiatement à la notification avec un HTTP 202 Accepted code status.
    • Répondez avant de valider la notification de modification, même si la validation échoue ultérieurement. Répondez immédiatement à la réception de la notification de modification, que vous stockiez des notifications dans des files d’attente pour les traiter ultérieurement ou que vous les traitez à la volée.
    • L’acceptation d’une notification de modification et la réponse à ces notifications empêchent les nouvelles tentatives de remise inutiles et masquent les résultats de validation des attaquants potentiels. Vous pouvez toujours ignorer une notification de modification non valide une fois que vous la recevez.

    En particulier, effectuez une validation sur chaque jeton JWT dans la collection validationTokens. En cas d’échec d’un jeton, considérez la notification de modification comme suspecte et examinez-la de manière plus approfondie.

    Procédez comme suit pour valider les jetons et les applications qui les génèrent :

    1. Vérifiez que le jeton n’a pas expiré.

    2. Vérifiez que le Plateforme d'identités Microsoft émis le jeton et qu’il n’a pas été falsifié.

      • Obtenez les clés de signature à partir du point de terminaison de configuration commun : https://login.microsoftonline.com/common/.well-known/openid-configuration. Votre application peut mettre en cache cette configuration pendant un certain temps. La configuration est fréquemment mise à jour au fur et à mesure que les clés de signature font l’objet d’une rotation quotidienne.
      • Vérifiez la signature du jeton JWT à l’aide de ces clés.

      N’acceptez pas les jetons émis par une autre autorité.

    3. Vérifiez que le jeton a été émis pour votre application.

      Les étapes suivantes font partie de la logique de validation standard dans les bibliothèques de jetons JWT et peuvent être exécutées en tant qu’appel de fonction unique.

      • Validez que l’« audience » dans le jeton corresponde à votre ID d’application.
      • Si vous avez plusieurs applications recevant des notifications de modifications, vérifiez s’il existe plusieurs ID.
    4. Vérifiez que la propriété du azp jeton correspond à la valeur attendue de , qui représente l’éditeur de 0bf30f3b-4a52-48df-9a82-234910c4a086notification de modification Microsoft Graph.

    Exemple de jeton JWT

    L’exemple suivant montre les propriétés du jeton JWT nécessaire à la validation.

    {
      // aud is your app's id
      "aud": "925bff9f-f6e2-4a69-b858-f71ea2b9b6d0",
      "iss": "https://login.microsoftonline.com/9f4ebab6-520d-49c0-85cc-7b25c78d4a93/v2.0",
      "iat": 1624649764,
      "nbf": 1624649764,
      "exp": 1624736464,
      "aio": "E2ZgYGjnuFglnX7mtjJzwR5lYaWvAA==",
      // azp represents the notification publisher and must always be the same value of 0bf30f3b-4a52-48df-9a82-234910c4a086
      "azp": "0bf30f3b-4a52-48df-9a82-234910c4a086",
      "azpacr": "2",
      "oid": "1e7d79fa-7893-4d50-bdde-164260d9c5ba",
      "rh": "0.AX0AtrpOnw1SwEmFzHslx41KkzsP8wtSSt9ImoIjSRDEoIZ9AAA.",
      "sub": "1e7d79fa-7893-4d50-bdde-164260d9c5ba",
      "tid": "9f4ebab6-520d-49c0-85cc-7b25c78d4a93",
      "uti": "mIB4QKCeZE6hK71XUHJ3AA",
      "ver": "2.0"
    }
    

    Exemple : vérification des jetons de validation

    // add Microsoft.IdentityModel.Protocols.OpenIdConnect and System.IdentityModel.Tokens.Jwt nuget packages to your project
    public async Task<bool> ValidateToken(string token, string tenantId, IEnumerable<string> appIds)
    {
        var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
            "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration",
            new OpenIdConnectConfigurationRetriever());
        var openIdConfig = await configurationManager.GetConfigurationAsync();
        var handler = new JwtSecurityTokenHandler();
        try
        {
        handler.ValidateToken(token, new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateIssuerSigningKey = true,
            ValidateLifetime = true,
            ValidIssuer = $"https://sts.windows.net/{tenantId}/",
            ValidAudiences = appIds,
            IssuerSigningKeys = openIdConfig.SigningKeys
        }, out _);
        return true;
        }
        catch (Exception ex)
        {
        Trace.TraceError($"{ex.Message}:{ex.StackTrace}");
        return false;
        }
    }
    

    Déchiffrement des données de ressource à partir de notifications de modifications

    La propriété resourceData dans une notification de modification inclut des informations de base sur l’ID et le type d’une ressource instance. La propriété encryptedData contient les données de ressource complètes, chiffrées par Microsoft Graph avec la clé publique fournie dans l’abonnement. La propriété contient également des valeurs requises pour la vérification et le déchiffrement. Ce chiffrement est effectué pour renforcer la sécurité des données client accessibles via des notifications de modification. Sécurisez la clé privée pour vous assurer qu’un tiers ne peut pas déchiffrer les données client, même s’il intercepte les notifications de modification d’origine.

    Dans cette section, vous allez découvrir les concepts suivants :

    Gestion des clés de chiffrement

    1. Obtenez un certificat avec une paire de clés asymétriques.

      • Vous pouvez utiliser un certificat auto-signé, car Microsoft Graph ne vérifie pas l’émetteur du certificat et utilise la clé publique uniquement pour le chiffrement.

      • Utilisez Azure Key Vault pour créer, faire pivoter et gérer les certificats de manière sécurisée. Assurez-vous que les clés répondent aux critères suivants :

        • La clé doit être de type RSA.
        • La taille de clé doit être comprise entre 2 048 bits et 4 096 bits.
    2. Exportez le certificat au format X.509 codé en Base64 et incluez uniquement la clé publique.

    3. Lors de la création d’un abonnement :

      • Fournissez le certificat dans la propriété encryptionCertificate , à l’aide du contenu codé en Base64 dans lequel le certificat a été exporté.

      • Fournissez votre propre identificateur dans la propriété encryptionCertificateId.

        Cet identifiant vous permet de faire correspondre vos certificats aux notifications de modifications que vous recevez et de récupérer des certificats de votre magasin de certificats. Cet identifiant peut contenir jusqu’à 128 caractères.

    4. Gérez la clé privée de façon sécurisée afin que votre code de traitement de notification de modification puisse accéder à la clé privée de façon à déchiffrer les données de ressource.

    Clés de rotation

    Modifiez régulièrement les clés asymétriques pour réduire le risque de compromission d’une clé privée. Pour introduire une nouvelle paire de clés, procédez comme suit :

    1. Obtenir un nouveau certificat à l’aide d’une nouvelle paire de clés asymétriques. Utilisez-la pour tous les nouveaux abonnements créés.

    2. Mettez à jour les abonnements existants à l’aide de la nouvelle clé de certificat.

      • Faites en sorte que cette mise à jour fasse partie du renouvellement d’abonnement standard.
      • Vous pouvez également énumérer tous les abonnements et fournir la clé. Utilisez l’opération de correctif sur l’abonnement et mettez à jour les propriétés encryptionCertificate et encryptionCertificateId.
    3. Gardez à l’esprit les principes suivants :

      • L’ancien certificat peut encore être utilisé pour le chiffrement pendant un certain temps. Votre application doit avoir accès à la fois aux certificats anciens et nouveaux afin de pouvoir déchiffrer le contenu.
      • Utilisez la propriété encryptionCertificateId dans chaque notification de modification pour identifier la clé correcte à utiliser.
      • Ignorez l’ancien certificat uniquement lorsque vous ne voyez pas de notifications de modification récentes le référençant.

    Déchiffrement des données de ressource

    Pour optimiser les performances, Microsoft Graph utilise un processus de chiffrement en deux étapes :

    • Il génère une clé symétrique à usage unique et l’utilise pour chiffrer les données de ressources.
    • Il utilise la clé asymétrique publique (que vous avez fournie lors de l’abonnement) pour chiffrer la clé symétrique et l’inclut dans chaque notification de modification de cet abonnement.

    Supposons que la clé symétrique est différente pour chaque élément de la notification de modification.

    Pour déchiffrer des données de ressources, votre application doit effectuer les étapes inverses, à l’aide des propriétés de encryptedContent dans chaque notification de modification :

    1. Identifiez le certificat correct à l’aide de la propriété encryptionCertificateId .

    2. Initialisez un composant de chiffrement RSA avec la clé privée. Un moyen simple d’initialiser un composant RSA consiste à utiliser la méthode RSACertificateExtensions.GetRSAPrivateKey(X509Certificate2) avec un instance X509Certificate2, qui contient la clé privée décrite dans Gestion des clés de chiffrement.

    3. Déchiffrez la clé symétrique dans la propriété dataKey de chaque élément de la notification de modification à l’aide de votre clé privée. Utilisez OAEP (Optimal Asymmetric Encryption Padding) comme algorithme de déchiffrement.

    4. Utilisez la clé symétrique pour calculer la signature HMAC-SHA256 pour la valeur dans les données. Comparez-le à la valeur de dataSignature. Si elles ne correspondent pas, supposons que la charge utile est falsifiée et ne la déchiffrez pas.

    5. Déchiffrez la propriété de données à l’aide de la clé symétrique avec advanced encryption Standard (AES), comme .NET Aes.

      • Utilisez les paramètres de déchiffrement suivants pour l’algorithme AES :

        • Remplissage : PKCS7.
        • Mode de chiffrement : CBC.
      • Configurez le « vecteur d’initialisation » en copiant les 16 premiers octets de la clé symétrique utilisée pour le déchiffrement.

    Les données déchiffrées sont une chaîne JSON représentant la ressource.

    Exemple : Déchiffrement des données de ressources

    L’exemple JSON suivant montre une notification de modification qui inclut des valeurs de propriété chiffrées d’un instance chatMessage dans un message de canal. La @odata.id valeur spécifie le instance.

    {
      "value": [
        {
          "subscriptionId": "76222963-cc7b-42d2-882d-8aaa69cb2ba3",
          "changeType": "created",
          // Other properties typical in a resource change notification
          "resource": "teams('d29828b8-c04d-4e2a-b2f6-07da6982f0f0')/channels('19:f127a8c55ad949d1a238464d22f0f99e@thread.skype')/messages('1565045424600')/replies('1565047490246')",
          "resourceData": {
            "id": "1565293727947",
            "@odata.type": "#Microsoft.Graph.ChatMessage",
            "@odata.id": "teams('88cbc8fc-164b-44f0-b6a6-b59b4a1559d3')/channels('19:8d9da062ec7647d4bb1976126e788b47@thread.tacv2')/messages('1565293727947')/replies('1565293727947')"
          },
          "encryptedContent": {
            "data": "{encrypted data that produces a full resource}",
            "dataSignature": "<HMAC-SHA256 hash>",
            "dataKey": "{encrypted symmetric key from Microsoft Graph}",
            "encryptionCertificateId": "MySelfSignedCert/DDC9651A-D7BC-4D74-86BC-A8923584B0AB",
            "encryptionCertificateThumbprint": "07293748CC064953A3052FB978C735FB89E61C3D"
          }
        }
      ],
      "validationTokens": [
        "eyJ0eXAiOiJKV1QiLCJhbGciOiJSU..."
      ]
    }
    

    Pour obtenir une description complète des données envoyées lors de la remise des notifications de modification, consultez type de ressource changeNotificationCollection.

    Déchiffrer la clé symétrique

    Cette section contient des extraits de code utiles qui utilisent C# et .net pour chaque étape du déchiffrement.

    // Initialize with the private key that matches the encryptionCertificateId.
    X509Certificate2 certificate = <instance of X509Certificate2 matching the encryptionCertificateId property>;
    RSA rsa = certificate.GetRSAPrivateKey();
    byte[] encryptedSymmetricKey = Convert.FromBase64String(<value from dataKey property>);
    
    // Decrypt using OAEP padding.
    byte[] decryptedSymmetricKey = rsa.Decrypt(encryptedSymmetricKey, RSAEncryptionPadding.OaepSHA1);
    
    // Can now use decryptedSymmetricKey with the AES algorithm.
    

    Comparer les signatures de données à l’aide de HMAC-SHA256

    byte[] decryptedSymmetricKey = <the aes key decrypted in the previous step>;
    byte[] encryptedPayload = <the value from the data property, still encrypted>;
    byte[] expectedSignature = <the value from the dataSignature property>;
    byte[] actualSignature;
    
    using (HMACSHA256 hmac = new HMACSHA256(decryptedSymmetricKey))
    {
        actualSignature = hmac.ComputeHash(encryptedPayload);
    }
    if (actualSignature.SequenceEqual(expectedSignature))
    {
        // Continue with decryption of the encryptedPayload.
    }
    else
    {
        // Do not attempt to decrypt encryptedPayload. Assume notification payload has been tampered with and investigate.
    }
    

    Déchiffrer le contenu des données de la ressource

    Aes aesProvider = Aes.Create();
    aesProvider.Key = decryptedSymmetricKey;
    aesProvider.Padding = PaddingMode.PKCS7;
    aesProvider.Mode = CipherMode.CBC;
    
    // Obtain the initialization vector from the symmetric key itself.
    int vectorSize = 16;
    byte[] iv = new byte[vectorSize];
    Array.Copy(decryptedSymmetricKey, iv, vectorSize);
    aesProvider.IV = iv;
    
    byte[] encryptedPayload = Convert.FromBase64String(<value from data property>);
    
    string decryptedResourceData;
    // Decrypt the resource data content.
    using (var decryptor = aesProvider.CreateDecryptor())
    {
      using (MemoryStream msDecrypt = new MemoryStream(encryptedPayload))
      {
          using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
          {
              using (StreamReader srDecrypt = new StreamReader(csDecrypt))
              {
                  decryptedResourceData = srDecrypt.ReadToEnd();
              }
          }
      }
    }
    
    // decryptedResourceData now contains a JSON string that represents the resource.