001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2018, 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.jose.jwk.gen;
019
020
021import com.google.crypto.tink.subtle.Ed25519Sign;
022import com.google.crypto.tink.subtle.X25519;
023import com.nimbusds.jose.JOSEException;
024import com.nimbusds.jose.jwk.Curve;
025import com.nimbusds.jose.jwk.OctetKeyPair;
026import com.nimbusds.jose.util.Base64URL;
027
028import java.security.GeneralSecurityException;
029import java.security.InvalidKeyException;
030import java.util.Collections;
031import java.util.LinkedHashSet;
032import java.util.Objects;
033import java.util.Set;
034
035
036/**
037 * Octet Key Pair (OKP) JSON Web Key (JWK) generator.
038 *
039 * <p>Supported curves:
040 *
041 * <ul>
042 *     <li>{@link Curve#X25519 X25519}
043 *     <li>{@link Curve#Ed25519 Ed25519}
044 * </ul>
045 *
046 * @author Tim McLean
047 * @author Vladimir Dzhuvinov
048 * @version 2025-05-27
049 */
050public class OctetKeyPairGenerator extends JWKGenerator<OctetKeyPair> {
051
052
053        /**
054         * The curve.
055         */
056        private final Curve crv;
057
058
059        /**
060         * The supported values for the "crv" property.
061         */
062        public static final Set<Curve> SUPPORTED_CURVES;
063
064
065        static {
066                Set<Curve> curves = new LinkedHashSet<>();
067                curves.add(Curve.X25519);
068                curves.add(Curve.Ed25519);
069                SUPPORTED_CURVES = Collections.unmodifiableSet(curves);
070        }
071
072
073        /**
074         * Creates a new OctetKeyPair JWK generator.
075         *
076         * @param crv The curve. Must not be {@code null}.
077         */
078        public OctetKeyPairGenerator(final Curve crv) {
079
080                if (! SUPPORTED_CURVES.contains(Objects.requireNonNull(crv))) {
081                        throw new IllegalArgumentException("Curve not supported for OKP generation");
082                }
083                this.crv = crv;
084        }
085        
086        
087        @Override
088        public OctetKeyPair generate()
089                throws JOSEException {
090
091                final Base64URL privateKey;
092                final Base64URL publicKey;
093
094                if (this.crv.equals(Curve.X25519)) {
095
096                        final byte[] privateKeyBytes;
097                        final byte[] publicKeyBytes;
098
099                        try {
100                                // TODO Use super.secureRandom if it is set
101
102                                privateKeyBytes = X25519.generatePrivateKey();
103                                publicKeyBytes = X25519.publicFromPrivate(privateKeyBytes);
104
105                        } catch (InvalidKeyException e) {
106                                // internal Tink error, should not happen
107                                throw new JOSEException(e.getMessage(), e);
108                        }
109
110                        privateKey = Base64URL.encode(privateKeyBytes);
111                        publicKey = Base64URL.encode(publicKeyBytes);
112
113                } else if (this.crv.equals(Curve.Ed25519)) {
114
115                        final Ed25519Sign.KeyPair tinkKeyPair;
116
117                        try {
118                                if (secureRandom != null) {
119                                        // Use the provided JCA SecureRandom
120                                        byte[] seed = new byte[32];
121                                        secureRandom.nextBytes(seed);
122                                        tinkKeyPair = Ed25519Sign.KeyPair.newKeyPairFromSeed(seed);
123                                } else {
124                                        // Use the Tink internal secure random generation
125                                        tinkKeyPair = Ed25519Sign.KeyPair.newKeyPair();
126                                }
127
128                        } catch (GeneralSecurityException e) {
129                                // internal Tink error, should not happen
130                                throw new JOSEException(e.getMessage(), e);
131                        }
132
133                        privateKey = Base64URL.encode(tinkKeyPair.getPrivateKey());
134                        publicKey = Base64URL.encode(tinkKeyPair.getPublicKey());
135
136                } else {
137
138                        throw new JOSEException("Curve not supported");
139                }
140
141                OctetKeyPair.Builder builder = new OctetKeyPair.Builder(crv, publicKey)
142                        .d(privateKey)
143                        .keyUse(use)
144                        .keyOperations(ops)
145                        .algorithm(alg)
146                        .expirationTime(exp)
147                        .notBeforeTime(nbf)
148                        .issueTime(iat);
149
150                if (tprKid) {
151                        builder.keyIDFromThumbprint();
152                } else {
153                        builder.keyID(kid);
154                }
155
156                return builder.build();
157        }
158}