hc
2023-11-07 f45e756958099c35d6afb746df1d40a1c6302cfc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/** 
 * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Amazon Software License (the "License"). You may not use this file 
 * except in compliance with the License. A copy of the License is located at
 *
 *   http://aws.amazon.com/asl/
 *
 * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the 
 * specific language governing permissions and limitations under the License.
 */
package com.rockchip.alexa.jacky.app;
 
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
 
import org.apache.commons.codec.binary.ABase64;
 
@SuppressWarnings("javadoc")
public class CodeChallengeWorkflow {
    private static final String SHA_256 = "S256";
    private static final String ALORITHM_SHA_256 = "SHA-256";
 
    private String codeVerifier;
    private String codeChallengeMethod;
    private String codeChallenge;
    private static CodeChallengeWorkflow instance = new CodeChallengeWorkflow();
 
    private CodeChallengeWorkflow() {
    }
 
    /**
     * @return the {@link CodeChallengeWorkflow} instance
     */
    public static CodeChallengeWorkflow getInstance() {
        return instance;
    }
 
    /**
     * CodeChallenge parameter generation logic goes here. We are implementing version 10 of the specification.
     * Design doc: https://w.amazon.com/index.php/IdentityServices/LWA/Projects/LWA_3P_SSO_Launch
     * SPOP Protocol specification version 10: https://tools.ietf.org/html/draft-ietf-oauth-spop-02
     */
    public void generateProofKeyParameters() {
        try {
            codeVerifier = generateCodeVerifier();
            codeChallengeMethod = SHA_256;
            codeChallenge = generateCodeChallenge(codeVerifier, codeChallengeMethod);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("Your JRE does not support the required "
                    + CodeChallengeWorkflow.ALORITHM_SHA_256 + " algorithm.", e);
        }
    }
 
    /**
     * @return the codeVerifier generated.
     */
    public String getCodeVerifier() {
        return this.codeVerifier;
    }
 
    /**
     * @return the codeChallenge generated.
     */
    public String getCodeChallenge() {
        return this.codeChallenge;
    }
 
    /**
     * @return the codeChallengeMethod used. Defaults to {@value #SHA_256}
     */
    public String getCodeChallengeMethod() {
        return this.codeChallengeMethod;
    }
 
    private String generateCodeChallenge(String codeVerifier, String codeChallengeMethod)
            throws NoSuchAlgorithmException {
        String codeChallenge =
                base64UrlEncode(MessageDigest.getInstance(ALORITHM_SHA_256).digest(codeVerifier.getBytes()));
        return codeChallenge;
    }
 
    private String generateCodeVerifier() {
        byte[] randomOctetSequence = generateRandomOctetSequence();
        String codeVerifier = base64UrlEncode(randomOctetSequence);
        return codeVerifier;
    }
 
    /**
     * As per Proof Key/SPOP protocol Version 10
     * @return a random 32 sized octet sequence from allowed range
     */
    private byte[] generateRandomOctetSequence() {
        SecureRandom random = new SecureRandom();
        byte[] octetSequence = new byte[32];
        random.nextBytes(octetSequence);
 
        return octetSequence;
    }
 
    /**
     * This method is borrowed from the SPOP protocol spec version 10 here : http://datatracker.ietf.org/doc/draft-ietf-oauth-spop/?include_text=1
     * @param arg the string to convert
     * @return base64 URL encoded string value as specified by spec.
     */
    private String base64UrlEncode(byte[] arg) {
        return ABase64.encodeBase64URLSafeString(arg);
    }
}