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}