public static void WriteEncryptedError(IRequest req, byte[] cryptKey, byte[] authKey, byte[] iv, Exception ex, string description = null) { var error = new ErrorResponse { ResponseStatus = ex.ToResponseStatus() }; var responseBodyBytes = error.ToJson().ToUtf8Bytes(); var encryptedBytes = AesUtils.Encrypt(responseBodyBytes, cryptKey, iv); var authEncryptedBytes = HmacUtils.Authenticate(encryptedBytes, authKey, iv); var httpError = ex as IHttpError; req.Response.StatusCode = (int)HttpStatusCode.BadRequest; req.Response.StatusDescription = description ?? (httpError != null ? httpError.ErrorCode : ex.GetType().Name); var errorResponse = new EncryptedMessageResponse { EncryptedBody = Convert.ToBase64String(authEncryptedBytes) }; req.Response.ContentType = MimeTypes.Json; req.Response.Write(errorResponse.ToJson()); req.Response.EndRequest(); }
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 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 privateKey = GetPrivateKey(encRequest.KeyId); if (Equals(privateKey, default(RSAParameters))) { WriteUnencryptedError(req, HttpError.NotFound(ErrorKeyNotFound.Fmt(encRequest.KeyId, PublicKeyPath)), "KeyNotFoundException"); return null; } 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 static void WriteEncryptedError(IRequest req, byte[] aesKey, byte[] iv, Exception ex, string description = null) { var error = new ErrorResponse { ResponseStatus = ex.ToResponseStatus() }; var responseBody = error.ToJson(); var encryptedBody = AesUtils.Encrypt(responseBody, aesKey, iv); var httpError = ex as IHttpError; req.Response.StatusCode = (int) HttpStatusCode.BadRequest; req.Response.StatusDescription = description ?? (httpError != null ? httpError.ErrorCode : ex.GetType().Name); var errorResponse = new EncryptedMessageResponse { EncryptedBody = encryptedBody }; req.Response.ContentType = MimeTypes.Json; req.Response.Write(errorResponse.ToJson()); req.Response.EndRequest(); }