2017-10-03 14 views
2

には、別個の暗号化と検証キーを指定できる方法があります。現在、検証と暗号化の両方を行うマスターキーは1つだけです。ただし、Webファームには複数のアプリケーションがあり、そのうちの1つのみがASP.NET COREで実行され、これはIIS上でホストされます。アプリケーションの残りの部分(ASP.NET * Not coreで実行)は同じマシンキーを使用します。もちろん、マシン鍵には復号鍵と検証鍵があり、他のすべてのアプリケーションはこの同じマシン鍵を使用して、それらの間でデータを同期します。また、COREアプリケーションを同じキーと同期させたいと思っています。現在、コアアプリにはこれがあります。 IDataProtectorは、マスタを使用して検証と暗号化/復号化を行います。IDataProtectorのマシンキーを使用する - ASP.NET CORE

<?xml version="1.0" encoding="utf-8"?> 
    <key id="6015093e-8571-4244-8824-17157f248d13" version="1"> 
     <creationDate>2017-10-03T12:13:26.6902857Z</creationDate> 
     <activationDate>2017-10-03T13:13:26.6897307+01:00</activationDate> 
     <expirationDate>2017-11-03T13:13:26.6898152+01:00</expirationDate> 
     <descriptor> 
     <descriptor> 
      <encryption algorithm="AES_256_CBC" /> 
      <validation algorithm="HMACSHA256" /> 
      <masterKey p4:requiresEncryption="true" xmlns:p4="http://schemas.asp.net/2015/03/dataProtection"> 
      <value>**This is the key**</value> 
      </masterKey> 
     </descriptor> 
     </descriptor> 
    </key> 

私は独立した検証と暗号化キーを指定するこの

<descriptor> 
     <encryption algorithm="AES_256_CBC" /> 
     <validation algorithm="HMACSHA256" /> 
     <encryptionKey p4:requiresEncryption="true" xmlns:p4="http://schemas.asp.net/2015/03/dataProtection"> 
     <!-- Warning: the key below is in an unencrypted form. --> 
     <value>encrypt key</value> 
     </encryptionKey> 
     <decryptionKey p4:requiresEncryption="true" xmlns:p4="http://schemas.asp.net/2015/03/dataProtection"> 
     <!-- Warning: the key below is in an unencrypted form. --> 
     <value>validation key</value> 
     </decryptionKey> 
    </descriptor> 
    </descriptor> 

のようなものを持っていると思います。このようなことは可能でしょうか?

答えて

1

私は、MachineKey.UnProtect関数が必要でした。私はASP.NET COREのAPIを使って作業することはできませんでした。だから、.NET Frameworkからソースコードを読み込むしかありませんでした。次のコードは私に何かを保護解除するために働いてしまった。

public static class MachineKey 
    { 
     private static readonly UTF8Encoding SecureUTF8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); 
     public static byte[] Unprotect(byte[] protectedData, string validationKey, string encKey, params string[] specificPurposes) 
     { 
      // The entire operation is wrapped in a 'checked' block because any overflows should be treated as failures. 
      checked 
      { 
       using (SymmetricAlgorithm decryptionAlgorithm = new AesCryptoServiceProvider()) 
       { 

        decryptionAlgorithm.Key = SP800_108.DeriveKey(HexToBinary(encKey), "User.MachineKey.Protect", specificPurposes); 

        // These KeyedHashAlgorithm instances are single-use; we wrap it in a 'using' block. 
        using (KeyedHashAlgorithm validationAlgorithm = new HMACSHA256()) 
        { 
         validationAlgorithm.Key = SP800_108.DeriveKey(HexToBinary(validationKey), "User.MachineKey.Protect", specificPurposes); 

         int ivByteCount = decryptionAlgorithm.BlockSize/8; 
         int signatureByteCount = validationAlgorithm.HashSize/8; 
         int encryptedPayloadByteCount = protectedData.Length - ivByteCount - signatureByteCount; 
         if (encryptedPayloadByteCount <= 0) 
         { 
          return null; 
         } 

         byte[] computedSignature = validationAlgorithm.ComputeHash(protectedData, 0, ivByteCount + encryptedPayloadByteCount); 

         if (!BuffersAreEqual(
          buffer1: protectedData, buffer1Offset: ivByteCount + encryptedPayloadByteCount, buffer1Count: signatureByteCount, 
          buffer2: computedSignature, buffer2Offset: 0, buffer2Count: computedSignature.Length)) 
         { 

          return null; 
         } 

         byte[] iv = new byte[ivByteCount]; 
         Buffer.BlockCopy(protectedData, 0, iv, 0, iv.Length); 
         decryptionAlgorithm.IV = iv; 

         using (MemoryStream memStream = new MemoryStream()) 
         { 
          using (ICryptoTransform decryptor = decryptionAlgorithm.CreateDecryptor()) 
          { 
           using (CryptoStream cryptoStream = new CryptoStream(memStream, decryptor, CryptoStreamMode.Write)) 
           { 
            cryptoStream.Write(protectedData, ivByteCount, encryptedPayloadByteCount); 
            cryptoStream.FlushFinalBlock(); 

            byte[] clearData = memStream.ToArray(); 

            return clearData; 
           } 
          } 
         } 
        } 
       } 
      } 
     } 

     private static bool BuffersAreEqual(byte[] buffer1, int buffer1Offset, int buffer1Count, byte[] buffer2, int buffer2Offset, int buffer2Count) 
     { 
      bool success = (buffer1Count == buffer2Count); // can't possibly be successful if the buffers are of different lengths 
      for (int i = 0; i < buffer1Count; i++) 
      { 
       success &= (buffer1[buffer1Offset + i] == buffer2[buffer2Offset + (i % buffer2Count)]); 
      } 
      return success; 
     } 

     private static class SP800_108 
     { 

      public static byte[] DeriveKey(byte[] keyDerivationKey, string primaryPurpose, params string[] specificPurposes) 
      { 
       using (HMACSHA512 hmac = new HMACSHA512(keyDerivationKey)) 
       { 

        GetKeyDerivationParameters(out byte[] label, out byte[] context, primaryPurpose, specificPurposes); 

        byte[] derivedKey = DeriveKeyImpl(hmac, label, context, keyDerivationKey.Length * 8); 

        return derivedKey; 
       } 
      } 

      private static byte[] DeriveKeyImpl(HMAC hmac, byte[] label, byte[] context, int keyLengthInBits) 
      { 
       checked 
       { 
        int labelLength = (label != null) ? label.Length : 0; 
        int contextLength = (context != null) ? context.Length : 0; 
        byte[] buffer = new byte[4 /* [i]_2 */ + labelLength /* label */ + 1 /* 0x00 */ + contextLength /* context */ + 4 /* [L]_2 */]; 

        if (labelLength != 0) 
        { 
         Buffer.BlockCopy(label, 0, buffer, 4, labelLength); // the 4 accounts for the [i]_2 length 
        } 
        if (contextLength != 0) 
        { 
         Buffer.BlockCopy(context, 0, buffer, 5 + labelLength, contextLength); // the '5 +' accounts for the [i]_2 length, the label, and the 0x00 byte 
        } 
        WriteUInt32ToByteArrayBigEndian((uint)keyLengthInBits, buffer, 5 + labelLength + contextLength); // the '5 +' accounts for the [i]_2 length, the label, the 0x00 byte, and the context 

        int numBytesWritten = 0; 
        int numBytesRemaining = keyLengthInBits/8; 
        byte[] output = new byte[numBytesRemaining]; 

        for (uint i = 1; numBytesRemaining > 0; i++) 
        { 
         WriteUInt32ToByteArrayBigEndian(i, buffer, 0); // set the first 32 bits of the buffer to be the current iteration value 
         byte[] K_i = hmac.ComputeHash(buffer); 

         // copy the leftmost bits of K_i into the output buffer 
         int numBytesToCopy = Math.Min(numBytesRemaining, K_i.Length); 
         Buffer.BlockCopy(K_i, 0, output, numBytesWritten, numBytesToCopy); 
         numBytesWritten += numBytesToCopy; 
         numBytesRemaining -= numBytesToCopy; 
        } 

        // finished 
        return output; 
       } 
      } 

      private static void WriteUInt32ToByteArrayBigEndian(uint value, byte[] buffer, int offset) 
      { 
       buffer[offset + 0] = (byte)(value >> 24); 
       buffer[offset + 1] = (byte)(value >> 16); 
       buffer[offset + 2] = (byte)(value >> 8); 
       buffer[offset + 3] = (byte)(value); 
      } 

     } 

     private static void GetKeyDerivationParameters(out byte[] label, out byte[] context, string primaryPurpose, params string[] specificPurposes) 
     { 
      label = SecureUTF8Encoding.GetBytes(primaryPurpose); 

       using (MemoryStream stream = new MemoryStream()) 
       using (BinaryWriter writer = new BinaryWriter(stream, SecureUTF8Encoding)) 
       { 
        foreach (string specificPurpose in specificPurposes) 
        { 
         writer.Write(specificPurpose); 
        } 
        context = stream.ToArray(); 
       } 
     } 

     private static byte[] HexToBinary(string data) 
     { 
      if (data == null || data.Length % 2 != 0) 
      { 
       // input string length is not evenly divisible by 2 
       return null; 
      } 

      byte[] binary = new byte[data.Length/2]; 

      for (int i = 0; i < binary.Length; i++) 
      { 
       int highNibble = HexToInt(data[2 * i]); 
       int lowNibble = HexToInt(data[2 * i + 1]); 

       if (highNibble == -1 || lowNibble == -1) 
       { 
        return null; // bad hex data 
       } 
       binary[i] = (byte)((highNibble << 4) | lowNibble); 
      } 

      int HexToInt(char h) 
      { 
       return (h >= '0' && h <= '9') ? h - '0' : 
       (h >= 'a' && h <= 'f') ? h - 'a' + 10 : 
       (h >= 'A' && h <= 'F') ? h - 'A' + 10 : 
       -1; 
      } 
      return binary; 
     } 

    } 

[実施例]

var message = "My secret message"; 

var encodedMessage = Encoding.ASCII.GetBytes(message); 

var protectedMessage = MachineKey.Protect(encodedMessage, "My Purpose"); 

var protectedMessageAsBase64 = Convert.ToBase64String(protectedMessage); 

// Now make sure you reverse the process 

var convertFromBase64 = Convert.FromBase64String(protectedMessageAsBase64); 

var unProtectedMessage = MachineKey.Unprotect(convertFromBase64, "Your validation key", "Your encryption key", "My Purpose"); 

var decodedMessage = Encoding.ASCII.GetString(unProtectedMessage); 

これは単純な例です。まず、IISからの正しい検証と暗号化キーがあることを確認します。これは明白な点のように思えるかもしれませんが、間違ったキーを使用していたので私は怒ってしまいました。次に、メッセージがどのような目的で拒否されたのかを確認します。私の例では、目的は「私の目的」です。メッセージが目的なしに暗号化されている場合は、保護を解除したときに目的のパラメータをそのままにしておきます。最後に、暗号化されたメッセージがどのように提示されたかを知る必要があります。それはbase64でエンコードされていますか?たとえば、逆を行うことができるようにこれを知る必要があります。

+0

これをどのように使用したか分かりますか?私はAuthenticationTicketを解読しようとしており、あなたが上記で提供したものを使用しています。しかし、私のBuffersAreEqualは偽になってしまっています。元のものが保護されている正しい "specificPurposes"を指定していないためです。 – dmarlow

+0

私は例を掲載しました。私たちはこれを社内のツールの1つで運用していますので、これが100%動作することがわかります。問題がある場合は、私に知らせてください。 –

+0

私はそれを働かせてくれました。ウマルに感謝します。私の問題は、以前私のweb.config/machineKeyで設定されていたHMACSHA1を使用していたことでした。一度それを使用すると、期待どおりに動作するようになりました。私はNuGetパッケージを作っています。他の人が使っているものを使いたい場合は、https://github.com/dmarlow/BearerTokenBridge – dmarlow

関連する問題