【Java】セキュアなランダム文字列生成を考える

■ はじめに

セキュアなランダム文字列生成を考える
 => 通常、java.util.Randomを利用すると思うが、偏りや再現性があるそうなので
    セキュアなランダム文字列生成することを考える

目次

【0】実装案
【1】java.util.UUID.randomUUID()を使用する
【2】java.security.SecureRandomを使用する
【3】Commonsを使用する

【0】実装案

【1】java.util.UUID.randomUUID()を使用する
  => 一意のランダムを生成する

【2】java.security.SecureRandomを使用する
  => 安全なトークンを生成する

【3】Commons(org.apache.commons.lang.RandomStringUtils)を使用する
  => Commonsを利用できるなら、簡単に実装できる

【1】java.util.UUID.randomUUID()を使用する

サンプル

import java.util.UUID;

public class SampleUuid {

  public static void main(String[] args) {
    System.out.println("Length: 123456789|123456789|123456789|123456789|123456789|123456789|");

    String token = UUID.randomUUID().toString();
    System.out.println("Token : " + token);

    token = UUID.randomUUID().toString();
    System.out.println("Token : " + token);

    token = UUID.randomUUID().toString();
    System.out.println("Token : " + token);
  }
}

出力結果

Length: 123456789|123456789|123456789|123456789|123456789|123456789|
Token : 4fe61f65-9742-4fd3-899a-ba50c6ad0b5b
Token : 9012be62-d089-402f-8fd2-6e45d9d2743a
Token : 086fbf25-f5b6-423f-bb33-8f7dcf2aed3e

【2】java.security.SecureRandomを使用する

API仕様
https://docs.oracle.com/javase/jp/8/docs/api/java/security/SecureRandom.html

サンプル

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

public class Main {

  public static void main(String[] args) throws NoSuchAlgorithmException {
    System.out.println("Length: 123456789|123456789|123456789|123456789|123456789|123456789|");

    String token = createToken(40);
    System.out.println("Token : " + token);

    token = createToken(-1);
    System.out.println("Token : " + token);
  }

  private static int DEFAULT_TOKEN_LENGTH = 30;

  public static String createToken(int tokenLength) throws NoSuchAlgorithmException {
    int length;
    if (tokenLength <= 1) {
      length = DEFAULT_TOKEN_LENGTH / 2;
    } else {
      length = tokenLength / 2;
    }

    try {
      SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
      byte tokenBytes[] = new byte[length];
      random.nextBytes(tokenBytes);
      return DatatypeConverter.printHexBinary(tokenBytes);
    } catch (NoSuchAlgorithmException ex) {
      ex.printStackTrace();
      throw ex;
    }
  }
}

出力結果

Length: 123456789|123456789|123456789|123456789|123456789|123456789|
Token : 849aca5e34bbba488675cd16edbe228b599bb227
Token : 5bbffc6e3faf33f228ccd651224ad7

参考文献
https://www.websec-room.com/2013/03/05/438

【3】Commonsを使用する

前提条件
https://commons.apache.org/proper/commons-lang/download_lang.cgi

でダウンロードする必要がある

サンプル

import org.apache.commons.lang.RandomStringUtils;

public class SampleCommons {

  public static void main(String[] args) {
    System.out.println("Length: 123456789|123456789|123456789|123456789|123456789|123456789|");

    String token = RandomStringUtils.randomNumeric(10);
    System.out.println("Token : " + token);

    token = RandomStringUtils.randomAlphabetic(20);
    System.out.println("Token : " + token);
    
    token = RandomStringUtils.randomAlphanumeric(30);
    System.out.println("Token : " + token);
    
    token = RandomStringUtils.randomAscii(40);
    System.out.println("Token : " + token);
    
    token = RandomStringUtils.random(50, "abcdABCD123456789@#");
    System.out.println("Token : " + token);
  }
}

出力結果

Length: 123456789|123456789|123456789|123456789|123456789|123456789|
Token : 0752117248
Token : qNCJPWWKtYTSewbaasOP
Token : V2jRM8pCFfGtSXwpBUgNcjfZyuexuL
Token : gOz}_]A@qyxpR"=[o2(p9gTjVtE.I#{U  OT4TWU
Token : 1AD7dd#a52aBcC7C8C6CC2C3c78Dc@d@25117717d19C97412a

使用上の注意

 * randomAscii() は、空白(半角スペース)も含まれるので注意

 * commons-lang2系「import org.apache.commons.lang.RandomStringUtils」と
   commons-lang3系「import org.apache.commons.lang3.RandomStringUtils;」があるので注意。

※ このcommons-lang2系/commons-lang3系でトラブった。内容は以下の関連記事を参照のこと。

参考文献

http://kechanzahorumon.hatenadiary.com/entry/2013/05/30/234000
http://blog.sorausagi.org/2009/03/java.html

関連記事

 Pythonで セキュアなランダム文字列生成を考える
https://dk521123.hatenablog.com/entry/2021/11/29/110408