私は、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でエンコードされていますか?たとえば、逆を行うことができるようにこれを知る必要があります。
これをどのように使用したか分かりますか?私はAuthenticationTicketを解読しようとしており、あなたが上記で提供したものを使用しています。しかし、私のBuffersAreEqualは偽になってしまっています。元のものが保護されている正しい "specificPurposes"を指定していないためです。 – dmarlow
私は例を掲載しました。私たちはこれを社内のツールの1つで運用していますので、これが100%動作することがわかります。問題がある場合は、私に知らせてください。 –
私はそれを働かせてくれました。ウマルに感謝します。私の問題は、以前私のweb.config/machineKeyで設定されていたHMACSHA1を使用していたことでした。一度それを使用すると、期待どおりに動作するようになりました。私はNuGetパッケージを作っています。他の人が使っているものを使いたい場合は、https://github.com/dmarlow/BearerTokenBridge – dmarlow