Для этого используется TLS. Это спец-протокол, который решает все вопросы, которые вы хотите решить. Там есть возможность шифрования, взаимной аутентификации как клиента, так и сервера и всё такое. Минусы -- вычислительно сложно. Т. е. на слабых микроконтроллерах процесс установления TLS-сессии может занимать время, плюс расход батарей. Плюсы -- уже есть готовые либы под множество платформ/архитектур, которые можно использовать, чтобы не изобретать велосипеды и не писать своё.
Гарантия от replay attack достигается тем, что для передачи команды сторонам нужно аутентифицироваться, т. е. это всегда двусторонний обмен. Технически сначала стороны обмениваются сертификатами, проверяют их, затем на основании алгоритма Диффи-Хэллмана формируют симметричный ключ шифрования, который будет использоваться для шифрования потока данных. В общем случае, в каждой сессии этот ключ будет разным. Есть механизмы кэширования и сохранения стейта, для ускорения установления TLS-сессии (допустим, на случай если клиент недавно уже подключался) -- это нужно, чтобы увеличить скорость согласования TLS-соединения), но это всё опционально и отключабельно.
Внутрь TLS можно засунуть что угодно -- кастомный бинарный протокол на базе TCP/IP или REST API на базе HTTP -- без разницы.