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 java.security.MessageDigest;
022import java.security.NoSuchAlgorithmException;
023import java.util.Objects;
024
025import net.jcip.annotations.Immutable;
026import net.minidev.json.JSONObject;
027
028import com.nimbusds.jose.util.Base64;
029import com.nimbusds.oauth2.sdk.ParseException;
030import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
031
032
033/**
034 * Cryptographic digest.
035 *
036 * <p>Related specifications:
037 *
038 * <ul>
039 *     <li>OpenID Connect for Identity Assurance 1.0, section 5.1.2.2.
040 * </ul>
041 */
042@Immutable
043public final class Digest {
044        
045        
046        /**
047         * The hash algorithm.
048         */
049        private final HashAlgorithm alg;
050        
051        
052        /**
053         * The hash value.
054         */
055        private final Base64 value;
056        
057        
058        /**
059         * Creates a new cryptographic digest.
060         *
061         * @param alg   The hash algorithm. Must not be {@code null}.
062         * @param value The hash value. Must not be {@code null}.
063         */
064        public Digest(final HashAlgorithm alg, final Base64 value) {
065                Objects.requireNonNull(alg);
066                this.alg = alg;
067                Objects.requireNonNull(value);
068                this.value = value;
069        }
070        
071        
072        /**
073         * Returns the hash algorithm.
074         *
075         * @return The hash algorithm.
076         */
077        public HashAlgorithm getHashAlgorithm() {
078                return alg;
079        }
080        
081        
082        /**
083         * Returns the hash value.
084         *
085         * @return the hash value.
086         */
087        public Base64 getValue() {
088                return value;
089        }
090        
091        
092        /**
093         * Returns {@code true} if this digest matches the computed for the
094         * specified content.
095         *
096         * @param content The content. Must not be {@code null}.
097         *
098         * @return If {@code true} if the digest matches the content, else
099         *         {@code false}.
100         *
101         * @throws NoSuchAlgorithmException If the hash algorithm isn't
102         *                                  supported.
103         */
104        public boolean matches(final Base64 content)
105                throws NoSuchAlgorithmException {
106                
107                Digest computed = Digest.compute(getHashAlgorithm(), content);
108                return this.equals(computed);
109        }
110        
111        
112        /**
113         * Returns a JSON object representation of this cryptographic digest.
114         *
115         * @return The JSON object.
116         */
117        public JSONObject toJSONObject() {
118                JSONObject jsonObject = new JSONObject();
119                jsonObject.put("alg", getHashAlgorithm().getValue());
120                jsonObject.put("value", getValue().toString());
121                return jsonObject;
122        }
123        
124        
125        @Override
126        public boolean equals(Object o) {
127                if (this == o) return true;
128                if (!(o instanceof Digest)) return false;
129                Digest digest = (Digest) o;
130                return alg.equals(digest.alg) && getValue().equals(digest.getValue());
131        }
132        
133        
134        @Override
135        public int hashCode() {
136                return Objects.hash(alg, getValue());
137        }
138        
139        
140        /**
141         * Computes the digest for the specified content.
142         *
143         * @param alg     The hash algorithm. Must not be {@code null}.
144         * @param content The content. Must not be {@code null}.
145         *
146         * @return The digest.
147         *
148         * @throws NoSuchAlgorithmException If the hash algorithm isn't
149         *                                  supported.
150         */
151        public static Digest compute(final HashAlgorithm alg, final Base64 content)
152                throws NoSuchAlgorithmException {
153                
154                return compute(alg, content.decode());
155        }
156        
157        
158        /**
159         * Computes the digest for the specified content.
160         *
161         * @param alg     The hash algorithm. Must not be {@code null}.
162         * @param content The content. Must not be {@code null}.
163         *
164         * @return The digest.
165         *
166         * @throws NoSuchAlgorithmException If the hash algorithm isn't
167         *                                  supported.
168         */
169        public static Digest compute(final HashAlgorithm alg, final byte[] content)
170                throws NoSuchAlgorithmException {
171                
172                MessageDigest md = MessageDigest.getInstance(alg.getValue().toUpperCase());
173                byte[] hash = md.digest(content);
174                return new Digest(alg, Base64.encode(hash));
175        }
176        
177        
178        /**
179         * Parses a digest from the specified JSON object.
180         *
181         * @param jsonObject The JSON object.
182         *
183         * @return The cryptographic digest.
184         *
185         * @throws ParseException If parsing failed.
186         */
187        public static Digest parse(final JSONObject jsonObject)
188                throws ParseException {
189                
190                HashAlgorithm alg = new HashAlgorithm(JSONObjectUtils.getString(jsonObject, "alg"));
191                Base64 value = new Base64(JSONObjectUtils.getString(jsonObject, "value"));
192                return new Digest(alg, value);
193        }
194}