Using Oauth tickets across several services?

前端 未结 4 1157
执笔经年
执笔经年 2020-12-02 21:27

I currently have a pair of OWIN-based services that each use OAuth authentication against the same set of users. I intend to isolate the authorisation server (i.e. The token

4条回答
  •  生来不讨喜
    2020-12-02 21:56

    After talking with Brock Allen in the comments to the original post, I can't really guarantee this is a good/safe solution, but this is the code I ended up using. (Note: a version of this code is available as a nuget package.)

    I created a IDataProtector implementation that uses AES:

    internal class AesDataProtectorProvider : IDataProtector
    {
        // Fields
        private byte[] key;
    
        // Constructors
        public AesDataProtectorProvider(string key)
        {
            using (var sha1 = new SHA256Managed())
            {
                this.key = sha1.ComputeHash(Encoding.UTF8.GetBytes(key));
            }
        }
    
        // IDataProtector Methods
        public byte[] Protect(byte[] data)
        {
            byte[] dataHash;
            using (var sha = new SHA256Managed())
            {
                dataHash = sha.ComputeHash(data);
            }
    
            using (AesManaged aesAlg = new AesManaged())
            {
                aesAlg.Key = this.key;
                aesAlg.GenerateIV();
    
                using (var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV))
                using (var msEncrypt = new MemoryStream())
                {
                    msEncrypt.Write(aesAlg.IV, 0, 16);
    
                    using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    using (var bwEncrypt = new BinaryWriter(csEncrypt))
                    {
                        bwEncrypt.Write(dataHash);
                        bwEncrypt.Write(data.Length);
                        bwEncrypt.Write(data);
                    }
                    var protectedData = msEncrypt.ToArray();
                    return protectedData;
                }
            }
        }
    
        public byte[] Unprotect(byte[] protectedData)
        {
            using (AesManaged aesAlg = new AesManaged())
            {
                aesAlg.Key = this.key;
    
                using (var msDecrypt = new MemoryStream(protectedData))
                {
                    byte[] iv = new byte[16];
                    msDecrypt.Read(iv, 0, 16);
    
                    aesAlg.IV = iv;
    
                    using (var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV))
                    using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    using (var brDecrypt = new BinaryReader(csDecrypt))
                    {
                        var signature = brDecrypt.ReadBytes(32);
                        var len = brDecrypt.ReadInt32();
                        var data = brDecrypt.ReadBytes(len);
    
                        byte[] dataHash;
                        using (var sha = new SHA256Managed())
                        {
                            dataHash = sha.ComputeHash(data);
                        }
    
                        if (!dataHash.SequenceEqual(signature))
                            throw new SecurityException("Signature does not match the computed hash");
    
                        return data;
                    }
                }
            }
        }
    }
    

    And then used this in an ISecureDataFormat implementation like so:

    public class SecureTokenFormatter : ISecureDataFormat
    {
        // Fields
        private TicketSerializer serializer;
        private IDataProtector protector;
        private ITextEncoder encoder;
    
        // Constructors
        public SecureTokenFormatter(string key)
        {
            this.serializer = new TicketSerializer();
            this.protector = new AesDataProtectorProvider(key);
            this.encoder = TextEncodings.Base64Url;
        }
    
        // ISecureDataFormat Members
        public string Protect(AuthenticationTicket ticket)
        {
            var ticketData = this.serializer.Serialize(ticket);
            var protectedData = this.protector.Protect(ticketData);
            var protectedString = this.encoder.Encode(protectedData);
            return protectedString;
        }
    
        public AuthenticationTicket Unprotect(string text)
        {
            var protectedData = this.encoder.Decode(text);
            var ticketData = this.protector.Unprotect(protectedData);
            var ticket = this.serializer.Deserialize(ticketData);
            return ticket;
        }
    }
    

    The 'key' parameter on the constructor can then set to the same value on a number of services and they will all be able to decrypt ('unprotect') and use the ticket.

提交回复
热议问题