You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@ -0,0 +1,186 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="NetFXCryptoService.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Security.Cryptography {
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
/******************************************************************
|
||||
* !! WARNING !! *
|
||||
* This class contains cryptographic code. If you make changes to *
|
||||
* this class, please have it reviewed by the appropriate people. *
|
||||
******************************************************************/
|
||||
|
||||
// Uses .NET Framework classes to encrypt (SymmetricAlgorithm) and sign (KeyedHashAlgorithm) data.
|
||||
//
|
||||
// [PROTECT]
|
||||
// INPUT: clearData
|
||||
// OUTPUT: protectedData
|
||||
// ALGORITHM:
|
||||
// protectedData := IV || Enc(Kenc, IV, clearData) || Sign(Kval, IV || Enc(Kenc, IV, clearData))
|
||||
//
|
||||
// [UNPROTECT]
|
||||
// INPUT: protectedData
|
||||
// OUTPUT: clearData
|
||||
// ALGORITHM:
|
||||
// 1) Assume protectedData := IV || Enc(Kenc, IV, clearData) || Sign(Kval, IV || Enc(Kenc, IV, clearData))
|
||||
// 2) Validate the signature over the payload and strip it from the end
|
||||
// 3) Strip off the IV from the beginning of the payload
|
||||
// 4) Decrypt what remains of the payload, and return it as clearData
|
||||
|
||||
internal sealed class NetFXCryptoService : ICryptoService {
|
||||
|
||||
private readonly ICryptoAlgorithmFactory _cryptoAlgorithmFactory;
|
||||
private readonly CryptographicKey _encryptionKey;
|
||||
private readonly bool _predictableIV;
|
||||
private readonly CryptographicKey _validationKey;
|
||||
|
||||
public NetFXCryptoService(ICryptoAlgorithmFactory cryptoAlgorithmFactory, CryptographicKey encryptionKey, CryptographicKey validationKey, bool predictableIV = false) {
|
||||
_cryptoAlgorithmFactory = cryptoAlgorithmFactory;
|
||||
_encryptionKey = encryptionKey;
|
||||
_validationKey = validationKey;
|
||||
_predictableIV = predictableIV;
|
||||
}
|
||||
|
||||
public byte[] Protect(byte[] clearData) {
|
||||
// The entire operation is wrapped in a 'checked' block because any overflows should be treated as failures.
|
||||
checked {
|
||||
|
||||
// These SymmetricAlgorithm instances are single-use; we wrap it in a 'using' block.
|
||||
using (SymmetricAlgorithm encryptionAlgorithm = _cryptoAlgorithmFactory.GetEncryptionAlgorithm()) {
|
||||
// Initialize the algorithm with the specified key and an appropriate IV
|
||||
encryptionAlgorithm.Key = _encryptionKey.GetKeyMaterial();
|
||||
|
||||
if (_predictableIV) {
|
||||
// The caller wanted the output to be predictable (e.g. for caching), so we'll create an
|
||||
// appropriate IV directly from the input buffer. The IV length is equal to the block size.
|
||||
encryptionAlgorithm.IV = CryptoUtil.CreatePredictableIV(clearData, encryptionAlgorithm.BlockSize);
|
||||
}
|
||||
else {
|
||||
// If the caller didn't ask for a predictable IV, just let the algorithm itself choose one.
|
||||
encryptionAlgorithm.GenerateIV();
|
||||
}
|
||||
byte[] iv = encryptionAlgorithm.IV;
|
||||
|
||||
using (MemoryStream memStream = new MemoryStream()) {
|
||||
memStream.Write(iv, 0, iv.Length);
|
||||
|
||||
// At this point:
|
||||
// memStream := IV
|
||||
|
||||
// Write the encrypted payload to the memory stream.
|
||||
using (ICryptoTransform encryptor = encryptionAlgorithm.CreateEncryptor()) {
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memStream, encryptor, CryptoStreamMode.Write)) {
|
||||
cryptoStream.Write(clearData, 0, clearData.Length);
|
||||
cryptoStream.FlushFinalBlock();
|
||||
|
||||
// At this point:
|
||||
// memStream := IV || Enc(Kenc, IV, clearData)
|
||||
|
||||
// These KeyedHashAlgorithm instances are single-use; we wrap it in a 'using' block.
|
||||
using (KeyedHashAlgorithm signingAlgorithm = _cryptoAlgorithmFactory.GetValidationAlgorithm()) {
|
||||
// Initialize the algorithm with the specified key
|
||||
signingAlgorithm.Key = _validationKey.GetKeyMaterial();
|
||||
|
||||
// Compute the signature
|
||||
byte[] signature = signingAlgorithm.ComputeHash(memStream.GetBuffer(), 0, (int)memStream.Length);
|
||||
|
||||
// At this point:
|
||||
// memStream := IV || Enc(Kenc, IV, clearData)
|
||||
// signature := Sign(Kval, IV || Enc(Kenc, IV, clearData))
|
||||
|
||||
// Append the signature to the encrypted payload
|
||||
memStream.Write(signature, 0, signature.Length);
|
||||
|
||||
// At this point:
|
||||
// memStream := IV || Enc(Kenc, IV, clearData) || Sign(Kval, IV || Enc(Kenc, IV, clearData))
|
||||
|
||||
// Algorithm complete
|
||||
byte[] protectedData = memStream.ToArray();
|
||||
return protectedData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Unprotect(byte[] protectedData) {
|
||||
// The entire operation is wrapped in a 'checked' block because any overflows should be treated as failures.
|
||||
checked {
|
||||
|
||||
// We want to check that the input is in the form:
|
||||
// protectedData := IV || Enc(Kenc, IV, clearData) || Sign(Kval, IV || Enc(Kenc, IV, clearData))
|
||||
|
||||
// Definitions used in this method:
|
||||
// encryptedPayload := Enc(Kenc, IV, clearData)
|
||||
// signature := Sign(Kval, IV || encryptedPayload)
|
||||
|
||||
// These SymmetricAlgorithm instances are single-use; we wrap it in a 'using' block.
|
||||
using (SymmetricAlgorithm decryptionAlgorithm = _cryptoAlgorithmFactory.GetEncryptionAlgorithm()) {
|
||||
decryptionAlgorithm.Key = _encryptionKey.GetKeyMaterial();
|
||||
|
||||
// These KeyedHashAlgorithm instances are single-use; we wrap it in a 'using' block.
|
||||
using (KeyedHashAlgorithm validationAlgorithm = _cryptoAlgorithmFactory.GetValidationAlgorithm()) {
|
||||
validationAlgorithm.Key = _validationKey.GetKeyMaterial();
|
||||
|
||||
// First, we need to verify that protectedData is even long enough to contain
|
||||
// the required components (IV, encryptedPayload, signature).
|
||||
|
||||
int ivByteCount = decryptionAlgorithm.BlockSize / 8; // IV length is equal to the block size
|
||||
int signatureByteCount = validationAlgorithm.HashSize / 8;
|
||||
int encryptedPayloadByteCount = protectedData.Length - ivByteCount - signatureByteCount;
|
||||
if (encryptedPayloadByteCount <= 0) {
|
||||
// protectedData doesn't meet minimum length requirements
|
||||
return null;
|
||||
}
|
||||
|
||||
// If that check passes, we need to detect payload tampering.
|
||||
|
||||
// Compute the signature over the IV and encrypted payload
|
||||
// computedSignature := Sign(Kval, IV || encryptedPayload)
|
||||
byte[] computedSignature = validationAlgorithm.ComputeHash(protectedData, 0, ivByteCount + encryptedPayloadByteCount);
|
||||
|
||||
if (!CryptoUtil.BuffersAreEqual(
|
||||
buffer1: protectedData, buffer1Offset: ivByteCount + encryptedPayloadByteCount, buffer1Count: signatureByteCount,
|
||||
buffer2: computedSignature, buffer2Offset: 0, buffer2Count: computedSignature.Length)) {
|
||||
|
||||
// the computed signature didn't match the incoming signature, which is a sign of payload tampering
|
||||
return null;
|
||||
}
|
||||
|
||||
// At this point, we're certain that we generated the signature over this payload,
|
||||
// so we can go ahead with decryption.
|
||||
|
||||
// Populate the IV from the incoming stream
|
||||
byte[] iv = new byte[ivByteCount];
|
||||
Buffer.BlockCopy(protectedData, 0, iv, 0, iv.Length);
|
||||
decryptionAlgorithm.IV = iv;
|
||||
|
||||
// Write the decrypted payload to the memory stream.
|
||||
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();
|
||||
|
||||
// At this point
|
||||
// memStream := clearData
|
||||
|
||||
byte[] clearData = memStream.ToArray();
|
||||
return clearData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user