public void Register(IAppHost appHost)
{
if (PrivateKey == null)
{
PrivateKey = RsaUtils.CreatePrivateKeyParams();
}
appHost.RegisterService(typeof(EncryptedMessagesService), PublicKeyPath);
PrivateKeyModulusMap = new Dictionary <string, RSAParameters>
{
{ Convert.ToBase64String(PrivateKey.Value.Modulus), PrivateKey.Value },
};
foreach (var fallbackKey in FallbackPrivateKeys)
{
PrivateKeyModulusMap[Convert.ToBase64String(fallbackKey.Modulus)] = fallbackKey;
}
appHost.RequestConverters.Add((req, requestDto) =>
{
var encRequest = requestDto as EncryptedMessage;
if (encRequest == null)
{
return(null);
}
var cryptKey = new byte[AesUtils.KeySizeBytes];
var authKey = new byte[AesUtils.KeySizeBytes];
var iv = new byte[AesUtils.BlockSizeBytes];
const int tagLength = HmacUtils.KeySizeBytes;
try
{
var privateKey = GetPrivateKey(encRequest.KeyId);
if (Equals(privateKey, default(RSAParameters)))
{
WriteUnencryptedError(req, HttpError.NotFound(ErrorKeyNotFound.Fmt(encRequest.KeyId, PublicKeyPath)), "KeyNotFoundException");
return(null);
}
var authRsaEncCryptKey = Convert.FromBase64String(encRequest.EncryptedSymmetricKey);
var rsaEncCryptAuthKeys = new byte[authRsaEncCryptKey.Length - iv.Length - tagLength];
Buffer.BlockCopy(authRsaEncCryptKey, 0, iv, 0, iv.Length);
Buffer.BlockCopy(authRsaEncCryptKey, iv.Length, rsaEncCryptAuthKeys, 0, rsaEncCryptAuthKeys.Length);
var cryptAuthKeys = RsaUtils.Decrypt(rsaEncCryptAuthKeys, privateKey);
Buffer.BlockCopy(cryptAuthKeys, 0, cryptKey, 0, cryptKey.Length);
Buffer.BlockCopy(cryptAuthKeys, cryptKey.Length, authKey, 0, authKey.Length);
//Needs to be after cryptKey,authKey populated
if (nonceCache.ContainsKey(iv))
{
throw HttpError.Forbidden(ErrorNonceSeen);
}
var now = DateTime.UtcNow;
nonceCache.TryAdd(iv, now.Add(MaxRequestAge));
if (!HmacUtils.Verify(authRsaEncCryptKey, authKey))
{
throw new Exception("EncryptedSymmetricKey is Invalid");
}
var authEncryptedBytes = Convert.FromBase64String(encRequest.EncryptedBody);
if (!HmacUtils.Verify(authEncryptedBytes, authKey))
{
throw new Exception("EncryptedBody is Invalid");
}
var requestBodyBytes = HmacUtils.DecryptAuthenticated(authEncryptedBytes, cryptKey);
var requestBody = requestBodyBytes.FromUtf8Bytes();
if (string.IsNullOrEmpty(requestBody))
{
throw new ArgumentNullException("EncryptedBody");
}
var parts = requestBody.SplitOnFirst(' ');
var unixTime = int.Parse(parts[0]);
var minRequestDate = now.Subtract(MaxRequestAge);
if (unixTime.FromUnixTime() < minRequestDate)
{
throw HttpError.Forbidden(ErrorRequestTooOld);
}
DateTime expiredEntry;
nonceCache.Where(x => now > x.Value).ToList()
.Each(entry => nonceCache.TryRemove(entry.Key, out expiredEntry));
parts = parts[1].SplitOnFirst(' ');
req.Items[Keywords.InvokeVerb] = parts[0];
parts = parts[1].SplitOnFirst(' ');
var operationName = parts[0];
var requestJson = parts[1];
var requestType = appHost.Metadata.GetOperationType(operationName);
var request = JsonSerializer.DeserializeFromString(requestJson, requestType);
req.Items[RequestItemsCryptKey] = cryptKey;
req.Items[RequestItemsAuthKey] = authKey;
req.Items[RequestItemsIv] = iv;
return(request);
}
catch (Exception ex)
{
WriteEncryptedError(req, cryptKey, authKey, iv, ex, ErrorInvalidMessage);
return(null);
}
});
appHost.ResponseConverters.Add((req, response) =>
{
object oCryptKey, oAuthKey, oIv;
if (!req.Items.TryGetValue(RequestItemsCryptKey, out oCryptKey) ||
!req.Items.TryGetValue(RequestItemsAuthKey, out oAuthKey) ||
!req.Items.TryGetValue(RequestItemsIv, out oIv))
{
return(null);
}
req.Response.ClearCookies();
var ex = response as Exception;
if (ex != null)
{
WriteEncryptedError(req, (byte[])oCryptKey, (byte[])oAuthKey, (byte[])oIv, ex);
return(null);
}
var responseBodyBytes = response.ToJson().ToUtf8Bytes();
var encryptedBytes = AesUtils.Encrypt(responseBodyBytes, (byte[])oCryptKey, (byte[])oIv);
var authEncryptedBytes = HmacUtils.Authenticate(encryptedBytes, (byte[])oAuthKey, (byte[])oIv);
var encResponse = new EncryptedMessageResponse
{
EncryptedBody = Convert.ToBase64String(authEncryptedBytes)
};
return(encResponse);
});
}