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