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 java.security.GeneralSecurityException;
022import java.security.InvalidKeyException;
023import java.util.Collections;
024import java.util.LinkedHashSet;
025import java.util.Set;
026
027import com.google.crypto.tink.subtle.Ed25519Sign;
028import com.google.crypto.tink.subtle.X25519;
029
030import com.nimbusds.jose.JOSEException;
031import com.nimbusds.jose.jwk.Curve;
032import com.nimbusds.jose.jwk.OctetKeyPair;
033import com.nimbusds.jose.util.Base64URL;
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 * @version 2023-01-02
048 */
049public class OctetKeyPairGenerator extends JWKGenerator<OctetKeyPair> {
050
051
052        /**
053         * The curve.
054         */
055        private final Curve crv;
056
057
058        /**
059         * The supported values for the "crv" property.
060         */
061        public static final Set<Curve> SUPPORTED_CURVES;
062
063
064        static {
065                Set<Curve> curves = new LinkedHashSet<>();
066                curves.add(Curve.X25519);
067                curves.add(Curve.Ed25519);
068                SUPPORTED_CURVES = Collections.unmodifiableSet(curves);
069        }
070
071
072        /**
073         * Creates a new OctetKeyPair JWK generator.
074         *
075         * @param crv The curve. Must not be {@code null}.
076         */
077        public OctetKeyPairGenerator(final Curve crv) {
078
079                if (crv == null) {
080                        throw new IllegalArgumentException("The curve must not be null");
081                }
082
083                if (! SUPPORTED_CURVES.contains(crv)) {
084                        throw new IllegalArgumentException("Curve not supported for OKP generation");
085                }
086
087                this.crv = crv;
088        }
089        
090        
091        @Override
092        public OctetKeyPair generate()
093                throws JOSEException {
094
095                final Base64URL privateKey;
096                final Base64URL publicKey;
097
098                if (this.crv.equals(Curve.X25519)) {
099
100                        final byte[] privateKeyBytes;
101                        final byte[] publicKeyBytes;
102
103                        try {
104                                // TODO Use super.secureRandom if it is set
105
106                                privateKeyBytes = X25519.generatePrivateKey();
107                                publicKeyBytes = X25519.publicFromPrivate(privateKeyBytes);
108
109                        } catch (InvalidKeyException e) {
110                                // internal Tink error, should not happen
111                                throw new JOSEException(e.getMessage(), e);
112                        }
113
114                        privateKey = Base64URL.encode(privateKeyBytes);
115                        publicKey = Base64URL.encode(publicKeyBytes);
116
117                } else if (this.crv.equals(Curve.Ed25519)) {
118
119                        final Ed25519Sign.KeyPair tinkKeyPair;
120
121                        try {
122                                // TODO Use super.secureRandom if it is set
123
124                                tinkKeyPair = Ed25519Sign.KeyPair.newKeyPair();
125
126                        } catch (GeneralSecurityException e) {
127                                // internal Tink error, should not happen
128                                throw new JOSEException(e.getMessage(), e);
129                        }
130
131                        privateKey = Base64URL.encode(tinkKeyPair.getPrivateKey());
132                        publicKey = Base64URL.encode(tinkKeyPair.getPublicKey());
133
134                } else {
135
136                        throw new JOSEException("Curve not supported");
137                }
138
139                OctetKeyPair.Builder builder = new OctetKeyPair.Builder(crv, publicKey)
140                        .d(privateKey)
141                        .keyUse(use)
142                        .keyOperations(ops)
143                        .algorithm(alg)
144                        .expirationTime(exp)
145                        .notBeforeTime(nbf)
146                        .issueTime(iat);
147
148                if (x5tKid) {
149                        builder.keyIDFromThumbprint();
150                } else {
151                        builder.keyID(kid);
152                }
153
154                return builder.build();
155        }
156}