001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2021, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.openid.connect.sdk.assurance.evidences.attachment;
019
020
021import com.nimbusds.jose.util.Base64;
022import com.nimbusds.oauth2.sdk.ParseException;
023import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
024import net.jcip.annotations.Immutable;
025import net.minidev.json.JSONObject;
026
027import java.security.MessageDigest;
028import java.security.NoSuchAlgorithmException;
029import java.util.Objects;
030
031
032/**
033 * Cryptographic digest.
034 *
035 * <p>Related specifications:
036 *
037 * <ul>
038 *     <li>OpenID Connect for Identity Assurance 1.0
039 * </ul>
040 */
041@Immutable
042public final class Digest {
043        
044        
045        /**
046         * The hash algorithm.
047         */
048        private final HashAlgorithm alg;
049        
050        
051        /**
052         * The hash value.
053         */
054        private final Base64 value;
055        
056        
057        /**
058         * Creates a new cryptographic digest.
059         *
060         * @param alg   The hash algorithm. Must not be {@code null}.
061         * @param value The hash value. Must not be {@code null}.
062         */
063        public Digest(final HashAlgorithm alg, final Base64 value) {
064                Objects.requireNonNull(alg);
065                this.alg = alg;
066                Objects.requireNonNull(value);
067                this.value = value;
068        }
069        
070        
071        /**
072         * Returns the hash algorithm.
073         *
074         * @return The hash algorithm.
075         */
076        public HashAlgorithm getHashAlgorithm() {
077                return alg;
078        }
079        
080        
081        /**
082         * Returns the hash value.
083         *
084         * @return the hash value.
085         */
086        public Base64 getValue() {
087                return value;
088        }
089        
090        
091        /**
092         * Returns {@code true} if this digest matches the computed for the
093         * specified content.
094         *
095         * @param content The content. Must not be {@code null}.
096         *
097         * @return If {@code true} if the digest matches the content, else
098         *         {@code false}.
099         *
100         * @throws NoSuchAlgorithmException If the hash algorithm isn't
101         *                                  supported.
102         */
103        public boolean matches(final Base64 content)
104                throws NoSuchAlgorithmException {
105                
106                Digest computed = Digest.compute(getHashAlgorithm(), content);
107                return this.equals(computed);
108        }
109        
110        
111        /**
112         * Returns a JSON object representation of this cryptographic digest.
113         *
114         * @return The JSON object.
115         */
116        public JSONObject toJSONObject() {
117                JSONObject jsonObject = new JSONObject();
118                jsonObject.put("alg", getHashAlgorithm().getValue());
119                jsonObject.put("value", getValue().toString());
120                return jsonObject;
121        }
122        
123        
124        @Override
125        public boolean equals(Object o) {
126                if (this == o) return true;
127                if (!(o instanceof Digest)) return false;
128                Digest digest = (Digest) o;
129                return alg.equals(digest.alg) && getValue().equals(digest.getValue());
130        }
131        
132        
133        @Override
134        public int hashCode() {
135                return Objects.hash(alg, getValue());
136        }
137        
138        
139        /**
140         * Computes the digest for the specified content.
141         *
142         * @param alg     The hash algorithm. Must not be {@code null}.
143         * @param content The content. Must not be {@code null}.
144         *
145         * @return The digest.
146         *
147         * @throws NoSuchAlgorithmException If the hash algorithm isn't
148         *                                  supported.
149         */
150        public static Digest compute(final HashAlgorithm alg, final Base64 content)
151                throws NoSuchAlgorithmException {
152                
153                return compute(alg, content.decode());
154        }
155        
156        
157        /**
158         * Computes the digest for the specified content.
159         *
160         * @param alg     The hash algorithm. Must not be {@code null}.
161         * @param content The content. Must not be {@code null}.
162         *
163         * @return The digest.
164         *
165         * @throws NoSuchAlgorithmException If the hash algorithm isn't
166         *                                  supported.
167         */
168        public static Digest compute(final HashAlgorithm alg, final byte[] content)
169                throws NoSuchAlgorithmException {
170                
171                MessageDigest md = MessageDigest.getInstance(alg.getValue().toUpperCase());
172                byte[] hash = md.digest(content);
173                return new Digest(alg, Base64.encode(hash));
174        }
175        
176        
177        /**
178         * Parses a digest from the specified JSON object.
179         *
180         * @param jsonObject The JSON object.
181         *
182         * @return The cryptographic digest.
183         *
184         * @throws ParseException If parsing failed.
185         */
186        public static Digest parse(final JSONObject jsonObject)
187                throws ParseException {
188                
189                HashAlgorithm alg = new HashAlgorithm(JSONObjectUtils.getNonBlankString(jsonObject, "alg"));
190                Base64 value = new Base64(JSONObjectUtils.getNonBlankString(jsonObject, "value"));
191                return new Digest(alg, value);
192        }
193}