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 Core 1.0, section 8.1.
026 * </ul>
027 */
028public class HashingSubjectIdentifierGenerator extends PairwiseSubjectIdentifierGenerator {
029
030
031        /**
032         * The hashing algorithm.
033         */
034        public static final String HASH_ALGORITHM = "SHA-256";
035
036
037        /**
038         * UTF-8 is the charset for byte to and from string conversions.
039         */
040        private final Charset charset;
041
042
043        /**
044         * The salt.
045         */
046        private final byte[] salt;
047
048
049        /**
050         * Creates a new SHA-256 based generator of pairwise subject
051         * identifiers.
052         *
053         * @param salt The string to use for the salt. Must not be empty, blank
054         *             or {@code null}.
055         *
056         * @throws NoSuchAlgorithmException If SHA-256 isn't supported by the
057         *                                  underlying JVM.
058         */
059        public HashingSubjectIdentifierGenerator(final String salt)
060                throws NoSuchAlgorithmException {
061
062                charset = Charset.forName("UTF-8");
063
064                if (salt == null)
065                        throw new IllegalArgumentException("The salt must not be null");
066
067                if (salt.trim().isEmpty())
068                        throw new IllegalArgumentException("The salt string must not be blank or empty");
069
070                this.salt = salt.getBytes(charset);
071
072                MessageDigest.getInstance(HASH_ALGORITHM);
073        }
074
075
076        /**
077         * Returns the salt bytes.
078         *
079         * @return The salt bytes.
080         */
081        public byte[] saltBytes() {
082
083                return salt;
084        }
085
086
087        @Override
088        public Subject generate(final String sectorIdentifier, final Subject localSub) {
089
090                MessageDigest sha256;
091
092                try {
093                        sha256 = MessageDigest.getInstance(HASH_ALGORITHM);
094
095                } catch (NoSuchAlgorithmException e) {
096
097                        throw new IllegalStateException(e.getMessage(), e);
098                }
099
100                sha256.update(sectorIdentifier.getBytes(charset));
101                sha256.update(localSub.getValue().getBytes(charset));
102                byte[] hash = sha256.digest(salt);
103
104                return new Subject(Base64.encodeBase64URLSafeString(hash));
105        }
106}