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}