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        /**
042         * Creates a new code challenge with the specified value.
043         *
044         * @param value The code challenge value. Must not be {@code null} or
045         *              empty string.
046         */
047        private CodeChallenge(final String value) {
048                super(value);
049        }
050
051
052        /**
053         * Computes the code challenge using the specified method and verifier.
054         *
055         * @param method       The code challenge method. Must be supported and
056         *                     not {@code null}.
057         * @param codeVerifier The code verifier. Must not be {@code null}.
058         *
059         * @return The computed code challenge.
060         */
061        public static CodeChallenge compute(final CodeChallengeMethod method, final CodeVerifier codeVerifier) {
062
063                if (CodeChallengeMethod.PLAIN.equals(method)) {
064                        return new CodeChallenge(codeVerifier.getValue());
065                }
066
067                if (CodeChallengeMethod.S256.equals(method)) {
068
069                        MessageDigest md;
070
071                        try {
072                                md = MessageDigest.getInstance("SHA-256");
073                        } catch (NoSuchAlgorithmException e) {
074                                throw new IllegalStateException(e.getMessage());
075                        }
076
077                        byte[] hash = md.digest(codeVerifier.getValueBytes());
078
079                        return new CodeChallenge(Base64URL.encode(hash).toString());
080                }
081
082                throw new IllegalArgumentException("Unsupported code challenge method: " + method);
083        }
084        
085        
086        /**
087         * Parses a code challenge from the specified string.
088         *
089         * @param value The code challenge value.
090         *
091         * @return The code challenge.
092         *
093         * @throws ParseException If parsing failed.
094         */
095        public static CodeChallenge parse(final String value)
096                throws ParseException {
097                
098                try {
099                        return new CodeChallenge(value);
100                } catch (IllegalArgumentException e) {
101                        throw new ParseException("Invalid code challenge: " + e.getMessage(), e);
102                }
103        }
104}