001package com.nimbusds.openid.connect.sdk.id; 002 003 004import java.nio.charset.Charset; 005import java.security.MessageDigest; 006import java.security.NoSuchAlgorithmException; 007 008import org.apache.commons.codec.binary.Base64; 009 010import com.nimbusds.oauth2.sdk.id.Subject; 011 012 013/** 014 * SHA-256 based generator of pairwise subject identifiers. 015 * 016 * <p>Algorithm: 017 * 018 * <pre> 019 * sub = SHA-256 ( sector_identifier | local_account_id | salt ) 020 * </pre> 021 * 022 * <p>Related specifications: 023 * 024 * <ul> 025 * <li>OpenID Connect Messages, section 2.8.1. 026 * </ul> 027 * 028 * @author Vladimir Dzhuvinov 029 */ 030public final class HashingSubjectIdentifierGenerator extends PairwiseSubjectIdentifierGenerator { 031 032 033 /** 034 * The hashing algorithm. 035 */ 036 public static final String HASH_ALGORITHM = "SHA-256"; 037 038 039 /** 040 * UTF-8 is the charset for byte to and from string conversions. 041 */ 042 private final Charset charset; 043 044 045 /** 046 * The salt. 047 */ 048 private final byte[] salt; 049 050 051 /** 052 * Creates a new SHA-256 based generator of pairwise subject 053 * identifiers. 054 * 055 * @param salt The string to use for the salt. Must not be empty, blank 056 * or {@code null}. 057 * 058 * @throws NoSuchAlgorithmException If SHA-256 isn't supported by the 059 * underlying JVM. 060 */ 061 public HashingSubjectIdentifierGenerator(final String salt) 062 throws NoSuchAlgorithmException { 063 064 charset = Charset.forName("UTF-8"); 065 066 if (salt == null) 067 throw new IllegalArgumentException("The salt must not be null"); 068 069 if (salt.trim().isEmpty()) 070 throw new IllegalArgumentException("The salt string must not be blank or empty"); 071 072 this.salt = salt.getBytes(charset); 073 074 MessageDigest.getInstance(HASH_ALGORITHM); 075 } 076 077 078 /** 079 * Returns the salt bytes. 080 * 081 * @return The salt bytes. 082 */ 083 public byte[] saltBytes() { 084 085 return salt; 086 } 087 088 089 @Override 090 public Subject generate(final String sectorIdentifier, final Subject localSub) { 091 092 MessageDigest sha256; 093 094 try { 095 sha256 = MessageDigest.getInstance(HASH_ALGORITHM); 096 097 } catch (NoSuchAlgorithmException e) { 098 099 throw new IllegalStateException(e.getMessage(), e); 100 } 101 102 sha256.update(sectorIdentifier.getBytes(charset)); 103 sha256.update(localSub.getValue().getBytes(charset)); 104 byte[] hash = sha256.digest(salt); 105 106 return new Subject(Base64.encodeBase64URLSafeString(hash)); 107 } 108}