2014-02-16 30 views
17

我目前有一對基於OWIN的服務,每個服務都對同一組用戶使用OAuth身份驗證。我打算隔離授權服務器(即令牌端點)並以某種方式配置我的兩個服務以接受此令牌。我認爲這將涉及我的所有服務的一些配置,以允許在所有相關服務中解密此令牌。這可能嗎?跨多個服務使用Oauth門票?

回答

9

Katana OAuth2授權服務器中間件並非真正爲此場景設計(主要是因爲它依賴機器密鑰進行令牌驗證)。

如果您希望集中生成令牌,那麼您應該查看專爲此設計的OAuth2授權服務器。 Thinktecture AuthorizationServer是一個開源的服務器,它可以做到這一點:http://thinktecture.github.io/Thinktecture.AuthorizationServer/

+0

我其實是有一些運氣與此發佈之後。在服務器配置和承載令牌配置上都有一個AccessTokenFormat屬性。我實現了ISecureDataFormat 來序列化票證並用AES加密。假設我爲我的所有服務使用相同的密碼密鑰,令牌似乎對其中的每個服務都起作用。你看到這個方法有什麼問題嗎?如果沒有,我會寫出一個正確的答案。感謝您的回覆。 – Barguast

+0

可擴展性點當然在那裏,所以這是可能的。只是不幸的是,他們把這一切留給了一般開發人員來填補這些。這就是爲什麼它不幸 - 你不需要加密(本身),但你想要一個簽名來驗證它是從你的服務器。成功的解密不提供信任。 –

+0

謝謝,儘管我很努力地想知道爲什麼我的方法可能有害。它只是在DPAPI上使用AES,並且在那裏有簽名檢查元素以確保無效但可解密的標記仍然被拒絕。 – Barguast

20

在與原帖的評論中與Brock Allen交談之後,我不能保證這是一個好的/安全的解決方案,但這是我結束的代碼使用。 (注:此代碼的版本可以a nuget package)。

我創建了一個使用AES一個IDataProtector實現:

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; 
       } 
      } 
     } 
    } 
} 

,然後在ISecureDataFormat實現像這樣用的:

public class SecureTokenFormatter : ISecureDataFormat<AuthenticationTicket> 
{ 
    // 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<AuthenticationTicket> 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; 
    } 
} 

構造函數上的'key'參數可以在多個服務上設置爲相同的值,並且它們都將能夠解密('取消保護')並使用票據。

+1

在這裏你可以找到更多的「準備好」實施AesDataProtector的:https://github.com/i4004/Owin.Security.AesDataProtectorProvider/blob/master/Owin.Security.AesDataProtectorProvider/AesDataProtector.cs – piotrwest

+0

@piotrwest你能幫助我理解是什麼使鏈接到的實現更「準備」?我做了一個快速概述,我看到的唯一區別是注入加密工廠的能力。還有別的事嗎?在什麼情況下,我們想要注入這些工廠而不是僅僅使用這個答案中編碼的具體類? – pettys

+1

@pettys,我應該鏈接到https://github.com/i4004/Owin.Security.AesDataProtectorProvider,它清楚地顯示了我的意圖,而不是類。通過準備,我並不意味着更好的實施/建模/設計,而是將其作爲NuGet包裝。我想我只是懶得複製和粘貼你的代碼... – piotrwest

0

我知道這是一個老問題,但我有一個類似的用例。根據文檔,OWIN OAuth使用機器密鑰來保護數據。由於您控制了所有實例,因此我認爲只需在Web配置中設置machinekey即可。

編號:http://msdn.microsoft.com/en-us/library/microsoft.owin.security.oauth.oauthauthorizationserveroptions(v=vs.113).aspx

+0

在這種情況下,我是在Windows服務中自託管的,除非我錯了,否則無法在app.config中設置machineKey? – Barguast

+0

對不起,我認爲你使用的是IIS。根據我發佈的鏈接,在這種情況下,它默認使用DPAPI,它與用戶帳戶(和密碼)相關聯。在這種情況下,我認爲實施自己的數據保護器可能是您最好的選擇,但請確保您使用了衆所周知的算法。我不知道你在哪裏找到你的,所以我不會保證它是相對安全的,但它似乎是一個好的開始。 – jbblanchet

+0

謝謝,它在過去的幾個月裏一直在使用,它的功能非常好。 – Barguast