aspnet / Security

[Archived] Middleware for security and authorization of web apps. Project moved to https://github.com/aspnet/AspNetCore

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

DataProtection with ProtectKeysWithCertificate for Cookie Authentication shared by multi-apps.

kevinlo opened this issue · comments

I have App1 and App2 that need to shared cookie authentication. App1 is the first access application that will create the cookie to be shared by App2. I put the following codes in both apps and it works fine. I can see the key.xml with the encryptedSecret tag.

            services.AddDataProtection()
                .PersistKeysToFileSystem(new DirectoryInfo(myDataProtectionPath))
                .SetApplicationName("MyApp")
                .ProtectKeysWithCertificate(myCertificate);
                
            services.AddAuthentication()
                .AddCookie(CookieScheme, options =>
            {
                options.Cookie = new CookieBuilder()
                {
                    HttpOnly = true,
                    Name = "MyCookie",
                };
            };

Then , I remove the ProtectKeysWithCertificate(myCertificate) call in App2 like this

            services.AddDataProtection()
                .PersistKeysToFileSystem(new DirectoryInfo(myDataProtectionPath))
                .SetApplicationName("MyApp");

I expect the App2 cannot decrypt the cookie and the authentication will fail. But instead, the cookie authentication pass and I see a new protection key file with unencrypted master key is created.

Is it a bug or I am missing something? Could someone explain how it works?

That's expected. When App2 can't read any keys it will create its own. Is the concern that it wiped out the first one?

@Tratcher Could you explain how it works in this case? App2 read the encrypted key file, it cannot decrypt it so it creates its own unencrypted one. Is that key same as the one used by the App1 so it can use it to decrypt the cookie?

In that case, why does it need to ProtectKeysWithCertificate, any app that knows the DataProtectionPath and ApplicationName can create the same key to decrypt the protected data?

@natemcmaster to confirm.

Just find DataProtection Issue#286, it says

Decrypt is limited to search the store for a matching certificate

So, is it that App2 reads the matching certificate through the store? That's why it works without the ProtectKeysWithCertificate.

But then for the web farm case, the App2 in another Node may not find the certificate in the store and it would fail and it must call the ProtectKeysWithCertificate with the same certificate.

why does it need to ProtectKeysWithCertificate, any app that knows the DataProtectionPath and ApplicationName can create the same key to decrypt the protected data?

ProtectKeysWithCertificate is not needed on Windows machines. It's an option available to you if you want to protect keys at rest using an X.509 certificate. Data protection has a set of defaults described in https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/default-settings. These defaults are tuned for single machines scenarios and Azure Web Apps. If the app is spread across multiple machines, it may be convenient to distribute a shared X.509 certificate across the machines and configure the hosted apps to use the certificate for encryption of keys at rest. See https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/implementation/key-encryption-at-rest.

So, is it that App2 reads the matching certificate through the store? That's why it works without the ProtectKeysWithCertificate.

Yes, DataProtection will attempt to read all keys in your storage location (in your case, "myDataProtectionPath"). If it can decrypt a key, it will load and use it.

But then for the web farm case, the App2 in another Node may not find the certificate in the store and it would fail and it must call the ProtectKeysWithCertificate with the same certificate.

You must distribute your keys and certificates in a web farm case. If the key was encrypted with a certificate and that certificate is neither in the cert store nor given via UnprotectKeysWithAnyCertificate, then the key cannot be used.

@natemcmaster

Yes, DataProtection will attempt to read all keys in your storage location (in your case, "myDataProtectionPath"). If it can decrypt a key, it will load and use it.

App2 does not have the myCertificate. Why can it decrypt the key? That is the main question I have why the App2 can decrypt the cookie even it does not have the myCertificate. Does it read the certificate from the store?

Yes, DataProtection will use certificates installed in the cert store, even if you did not programmatically call .ProtectKeysWithCertificate or .UnprotectKeysWithAnyCertificate.

Can you clarify the exact behavior you are seeing, and what you're expecting? I'm not sure what the problem is. Is App2 using the exact same key as App1 and shouldn't? Is the certificate used to encrypt the key for App1 installed in the machine running App2 but shouldn't be used? Something else?

Ok. I just test it more. I create the myCertificate in my testing server. It does add the certificate to the Personal store. After I delete that certificate in the store and now App2 has

20181015 13:38:12,462 ERROR [XmlKeyManager] An exception occurred while processing the key element '<key id="9a207536-9af2-4dc6-bbff-9cb07e9b3bd1" version="1" />'. [(null) (null):3  ] 
System.Security.Cryptography.CryptographicException: Unable to retrieve the decryption key.
   at System.Security.Cryptography.Xml.EncryptedXml.GetDecryptionKey(EncryptedData encryptedData, String symmetricAlgorithmUri)
   at System.Security.Cryptography.Xml.EncryptedXml.DecryptDocument()
   at Microsoft.AspNetCore.DataProtection.XmlEncryption.EncryptedXmlDecryptor.Decrypt(XElement encryptedElement)
   at Microsoft.AspNetCore.DataProtection.XmlEncryption.XmlEncryptionExtensions.DecryptElement(XElement element, IActivator activator)
   at Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager.Microsoft.AspNetCore.DataProtection.KeyManagement.Internal.IInternalXmlKeyManager.DeserializeDescriptorFromKeyElement(XElement keyElement)

After I add ProtectKeysWithCertificate back to App2 using the same myCertificate, the exception is gone.

That is what I expect.

@natemcmaster I think you have answered my question

DataProtection will use certificates installed in the cert store, even if you did not programmatically call .ProtectKeysWithCertificate

Could you please point me to the class and function that read the certificate in the store? I would like to read how it works. Does it try every certificate in the store?

Got it. The Utils.BuildBagOfCerts in System.Security.Cryptography.Xml read the certificates from the LocalMachine and CurrentUser store.

Thanks all answering my questions. I''ll close this issue.

Using services.AddDataProtection().PersistKeysToAzureBlobStorage(Microsoft.WindowsAzure.Storage.CloudStorageAccount.Parse(connectionString), keyPersistentPath) doesn't resolves private key, results "Keyset not exists" error, can anyone shed some light on it?