原创

SecureRandom 的 strong 工厂选了什么:先核对提供者再安排初始化

把登录凭证生成代码改成 SecureRandom.getInstanceStrong(),并不能单凭方法名证明系统更安全,也不能推导它在每台机器上的等待行为。这个工厂按照安全属性选择算法;属性、提供者与运行环境,才决定实际拿到的对象。

若只是要生成不可预测的随机字节,new SecureRandom() 已经属于密码学随机数接口。把 strong 工厂放进每次请求里创建对象,会把选择与初始化都塞进请求路径,却没有表达出业务真正需要的输出长度、有效期和校验规则。

先看当前进程究竟选了谁

下面的 Java 17 程序只输出算法信息和一个临时令牌的形状,不输出令牌本身。保存为 RandomToken.java,执行 java RandomToken.java。输出来自本文使用的 Corretto 17.0.15 环境,其他系统不必与这里一致。

import java.security.*;
import java.util.Base64;

public class RandomToken {
    private static final SecureRandom RANDOM = new SecureRandom();

    static String token(int bytes) {
        if (bytes < 16 || bytes > 64) {
            throw new IllegalArgumentException("token bytes out of policy");
        }
        byte[] raw = new byte[bytes];
        RANDOM.nextBytes(raw);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(raw);
    }

    public static void main(String[] args) throws Exception {
        SecureRandom strong = SecureRandom.getInstanceStrong();
        System.out.println("default=" + RANDOM.getAlgorithm() + "/"
                + RANDOM.getProvider().getName());
        System.out.println("strong=" + strong.getAlgorithm() + "/"
                + strong.getProvider().getName());
        System.out.println("policy=" + Security.getProperty("securerandom.strongAlgorithms"));
        String value = token(32);
        System.out.println("length=" + value.length());
        System.out.println("url-safe=" + value.matches("[A-Za-z0-9_-]{43}"));
        try { token(0); }
        catch (IllegalArgumentException e) { System.out.println(e.getMessage()); }
    }
}
default=NativePRNG/SUN
strong=NativePRNGBlocking/SUN
policy=NativePRNGBlocking:SUN,DRBG:SUN
length=43
url-safe=true
token bytes out of policy

securerandom.strongAlgorithms 不是业务配置里随意编造的算法名列表。strong 工厂读取它,尝试构造合适的实例;没有可用实现时可能抛出 NoSuchAlgorithmException。需要指定算法与提供者的系统,应在部署前核验可用性,并把选择失败当作明确的启动结果处理。

这里没有测量“哪个更快”。部分实现会延迟初始化,某些获取随机材料的操作还可能等待。只包住构造函数计时,会遗漏第一次真正调用 nextBytes 的工作;一台开发机上的数字,也不能替代目标操作系统与提供者的行为。若请求链路有延迟疑问,应记录实际算法、提供者和等待位置,再在同一环境里定位。

把随机对象放在合适的位置

示例将实例作为服务生命周期内共享的对象。SecureRandom 的 API 允许多线程安全使用,实现是否自行提供线程安全能力会影响内部同步方式。共享避免了无意义的重复构造;是否出现竞争,则要结合实际吞吐与具体提供者观察,不能仅从 static final 推导性能结论。

不要为“初始化得更随机”主动喂入当前时间、用户编号或一个常量。新建实例通常会在第一次使用时从实现定义的熵源自行播种;提前调用 setSeed 可能改变这条路径。可预测的应用字段不应冒充随机材料。代码也没有为了复现测试而给生产随机数固定种子,测试只验证长度、字符集合和输入策略。

32 个随机字节在这里经过 URL 安全 Base64 编码,去掉填充后是 43 个字符。编码改变的是表示方法,不会额外创造随机性。业务若要求固定字符数,应先选定字节数和编码方式,再验证格式,不要生成很长字符串后随意截断到一个未经评估的长度。

token 的 16 至 64 字节限制只是本例的接口策略,不是 Java 对 SecureRandom 的统一要求。调用方不能通过请求参数随意申请一个超大数组;服务通常应把令牌规格固定在业务配置中。对于验证码这样的短空间,还必须另行限制尝试次数,不能用密码学随机源掩盖过小的可猜测范围。

随机生成完成以后,协议才走了一半

令牌是否过期、是否一次性、绑定哪个用途、失败如何计数,属于验证端的规则。两个长度相同的字符串,也不代表它们可以跨用途混用。用于下载的临时凭证和用于恢复账户的凭证,应由各自的验证流程约束,不应只靠随机字符串看起来复杂来区分。

如果需要诊断一次生成失败,可以保留算法名、提供者、异常类型和关联请求标识。日志没有必要写入完整令牌;示例只打印长度与字符集合,正是为了让格式验证不依赖公开凭证内容。至于 strong 是否必须使用,应由系统明确的密码学或合规要求决定,并落实到目标环境的算法配置上。

正文到此结束
评论插件初始化中...
Loading...
本文目录