001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2016, 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.oauth2.sdk.pkce;
019
020
021import java.security.MessageDigest;
022import java.security.NoSuchAlgorithmException;
023
024import com.nimbusds.jose.util.Base64URL;
025import com.nimbusds.oauth2.sdk.ParseException;
026import com.nimbusds.oauth2.sdk.id.Identifier;
027
028
029/**
030 * Authorisation code challenge.
031 *
032 * <p>Related specifications:
033 *
034 * <ul>
035 *     <li>Proof Key for Code Exchange by OAuth Public Clients (RFC 7636).
036 * </ul>
037 */
038public class CodeChallenge extends Identifier {
039        
040        
041        private static final long serialVersionUID = 1353706942392517197L;
042        
043        
044        /**
045         * Creates a new code challenge with the specified value.
046         *
047         * @param value The code challenge value. Must not be {@code null} or
048         *              empty string.
049         */
050        private CodeChallenge(final String value) {
051                super(value);
052        }
053
054
055        /**
056         * Computes the code challenge using the specified method and verifier.
057         *
058         * @param method       The code challenge method. Must be supported and
059         *                     not {@code null}.
060         * @param codeVerifier The code verifier. Must not be {@code null}.
061         *
062         * @return The computed code challenge.
063         */
064        public static CodeChallenge compute(final CodeChallengeMethod method, final CodeVerifier codeVerifier) {
065
066                if (CodeChallengeMethod.PLAIN.equals(method)) {
067                        return new CodeChallenge(codeVerifier.getValue());
068                }
069
070                if (CodeChallengeMethod.S256.equals(method)) {
071
072                        MessageDigest md;
073
074                        try {
075                                md = MessageDigest.getInstance("SHA-256");
076                        } catch (NoSuchAlgorithmException e) {
077                                throw new IllegalStateException(e.getMessage());
078                        }
079
080                        byte[] hash = md.digest(codeVerifier.getValueBytes());
081
082                        return new CodeChallenge(Base64URL.encode(hash).toString());
083                }
084
085                throw new IllegalArgumentException("Unsupported code challenge method: " + method);
086        }
087        
088        
089        /**
090         * Parses a code challenge from the specified string.
091         *
092         * @param value The code challenge value.
093         *
094         * @return The code challenge.
095         *
096         * @throws ParseException If parsing failed.
097         */
098        public static CodeChallenge parse(final String value)
099                throws ParseException {
100                
101                try {
102                        return new CodeChallenge(value);
103                } catch (IllegalArgumentException e) {
104                        throw new ParseException("Invalid code challenge: " + e.getMessage(), e);
105                }
106        }
107}