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); }); }
public void Register(IAppHost appHost) { if (PrivateKey == null) { PrivateKey = RsaUtils.CreatePrivateKeyParams(); } appHost.RegisterService(typeof(EncryptedMessagesService), PublicKeyPath); appHost.RequestConverters.Add((req, requestDto) => { var encRequest = requestDto as EncryptedMessage; if (encRequest == null) { return(null); } byte[] aesKey = null; byte[] iv = null; try { var rsaEncAesKeyBytes = RsaUtils.Decrypt(Convert.FromBase64String(encRequest.EncryptedSymmetricKey), PrivateKey.Value); aesKey = new byte[AesUtils.KeySize / 8]; iv = new byte[AesUtils.IvSize / 8]; Buffer.BlockCopy(rsaEncAesKeyBytes, 0, aesKey, 0, aesKey.Length); Buffer.BlockCopy(rsaEncAesKeyBytes, aesKey.Length, iv, 0, iv.Length); if (nonceCache.ContainsKey(iv)) { throw HttpError.Forbidden(ErrorNonceSeen); } var now = DateTime.UtcNow; nonceCache.TryAdd(iv, now.Add(MaxRequestAge)); var requestBodyBytes = AesUtils.Decrypt(Convert.FromBase64String(encRequest.EncryptedBody), aesKey, iv); 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[RequestItemsAesKey] = aesKey; req.Items[RequestItemsIv] = iv; return(request); } catch (Exception ex) { WriteEncryptedError(req, aesKey, iv, ex, ErrorInvalidMessage); return(null); } }); appHost.ResponseConverters.Add((req, response) => { object oAesKey; object oIv; if (!req.Items.TryGetValue(RequestItemsAesKey, out oAesKey) || !req.Items.TryGetValue(RequestItemsIv, out oIv)) { return(null); } req.Response.ClearCookies(); var ex = response as Exception; if (ex != null) { WriteEncryptedError(req, (byte[])oAesKey, (byte[])oIv, ex); return(null); } var responseBody = response.ToJson(); var encryptedBody = AesUtils.Encrypt(responseBody, (byte[])oAesKey, (byte[])oIv); return(new EncryptedMessageResponse { EncryptedBody = encryptedBody }); }); }