如何在搭载Scandium的Leshan服务器中实现RPK的TOFU机制?
实现TOFU机制的核心思路
Leshan本身没有内置TOFU支持,但你可以通过自定义RPK信任存储逻辑结合持久化存储来实现,核心逻辑:
- 首次连接的新客户端,记录它的端点名称(Endpoint Name)和对应公钥,直接信任
- 后续连接时,校验客户端提供的公钥是否和已记录的一致
- 公钥不匹配就直接拒绝连接
具体实现步骤
1. 自定义RpkTrustStore
Scandium的RpkTrustStore接口负责验证客户端RPK,你需要实现这个接口,把TOFU逻辑加进去:
- 实现
isTrusted方法处理信任判断 - 用持久化存储(比如本地文件、轻量数据库)保存端点和公钥的映射关系
示例代码大概是这样:
public class TofuRpkTrustStore implements RpkTrustStore { // 先用ConcurrentHashMap做内存存储示例,实际要换成持久化存储 private final Map<String, PublicKey> endpointKeyMap = new ConcurrentHashMap<>(); @Override public boolean isTrusted(PublicKey clientPublicKey, SocketAddress peerAddr, String endpointName) { if (endpointName == null) { return false; // 没有端点名称的连接直接拒掉 } synchronized (endpointKeyMap) { PublicKey storedKey = endpointKeyMap.get(endpointName); if (storedKey == null) { // 首次连接,存下公钥并信任 endpointKeyMap.put(endpointName, clientPublicKey); // 这里加持久化操作,比如写入本地文件或数据库 saveToStorage(); return true; } else { // 非首次连接,校验公钥是否一致 return storedKey.equals(clientPublicKey); } } } // 把映射关系持久化到存储的方法,自己实现 private void saveToStorage() { // 比如序列化endpointKeyMap到JSON文件,或者写入SQLite } // 服务器启动时加载已存储的映射 public void loadFromStorage() { // 从存储读取数据,初始化endpointKeyMap } }
2. 把自定义信任存储集成到Leshan服务器
构建Leshan服务器的时候,把默认的RPK信任存储换成你写的TofuRpkTrustStore:
// 初始化TOFU信任存储,启动时加载已保存的密钥 TofuRpkTrustStore tofuTrustStore = new TofuRpkTrustStore(); tofuTrustStore.loadFromStorage(); // 配置Scandium的DTLS连接器 DtlsConnectorConfig dtlsConfig = new DtlsConnectorConfig.Builder() .setAddress(new InetSocketAddress(5684)) .setRpkTrustStore(tofuTrustStore) // 替换默认的信任存储 // 这里还要加服务器自身的RPK配置,比如自己的公钥私钥 .build(); // 构建并启动Leshan服务器 LeshanServer server = new LeshanServerBuilder() .setDtlsConfig(dtlsConfig) // 其他服务器配置,比如资源、端点等 .build(); server.start();
3. 几个关键注意点
- 端点名称要唯一:必须依赖客户端的Endpoint Name作为唯一标识,要确保客户端用固定且唯一的名称
- 持久化不能省:内存存储重启就没了,一定要实现持久化(比如JSON文件、SQLite这类轻量存储)
- 并发安全要考虑:多线程环境下,存储操作要加锁或者用线程安全的集合,避免数据混乱
- 公钥序列化要正确:存储公钥时,要把它转成可序列化的格式(比如Base64编码的字节数组)
- TOFU的安全风险:首次连接可能被中间人攻击,如果你的场景安全性要求极高,最好加个后续人工确认公钥的环节
标准与最佳实践
目前LwM2M标准没有专门定义TOFU机制,但TOFU是设备认证里常见的零配置信任模式,适合没法预先配置密钥的场景。几个最佳实践:
- 把首次连接的设备公钥记录日志,方便后续审计
- 定期备份公钥存储,防止数据丢失
- 加个手动管理公钥的接口,比如允许删除、更新设备公钥,应对设备更换的情况
内容的提问来源于stack exchange,提问作者Juan Redondo
相关产品推荐
相关产品推荐

