001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2025, Connect2id Ltd. 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.crypto; 019 020 021import com.nimbusds.jose.*; 022import com.nimbusds.jose.crypto.impl.RSAKeyUtils; 023import com.nimbusds.jose.crypto.impl.RSASSA; 024import com.nimbusds.jose.crypto.impl.RSASSAProvider; 025import com.nimbusds.jose.crypto.opts.AllowWeakRSAKey; 026import com.nimbusds.jose.crypto.opts.OptionUtils; 027import com.nimbusds.jose.crypto.opts.UserAuthenticationRequired; 028import com.nimbusds.jose.jwk.RSAKey; 029import com.nimbusds.jose.util.Base64URL; 030import net.jcip.annotations.ThreadSafe; 031 032import java.security.InvalidKeyException; 033import java.security.PrivateKey; 034import java.security.Signature; 035import java.security.SignatureException; 036import java.security.interfaces.RSAPrivateKey; 037import java.util.Collections; 038import java.util.Set; 039 040 041 042/** 043 * RSA Signature-Scheme-with-Appendix (RSASSA) signer of 044 * {@link com.nimbusds.jose.JWSObject JWS objects}. Expects a private RSA key. 045 * 046 * <p>See RFC 7518, sections 047 * <a href="https://tools.ietf.org/html/rfc7518#section-3.3">3.3</a> and 048 * <a href="https://tools.ietf.org/html/rfc7518#section-3.5">3.5</a> for more 049 * information. 050 * 051 * <p>This class is thread-safe. 052 * 053 * <p>Supports the following algorithms: 054 * 055 * <ul> 056 * <li>{@link com.nimbusds.jose.JWSAlgorithm#RS256} 057 * <li>{@link com.nimbusds.jose.JWSAlgorithm#RS384} 058 * <li>{@link com.nimbusds.jose.JWSAlgorithm#RS512} 059 * <li>{@link com.nimbusds.jose.JWSAlgorithm#PS256} 060 * <li>{@link com.nimbusds.jose.JWSAlgorithm#PS384} 061 * <li>{@link com.nimbusds.jose.JWSAlgorithm#PS512} 062 * </ul> 063 * 064 * <p>Supports the following {@link JWSSignerOption options}: 065 * 066 * <ul> 067 * <li>{@link UserAuthenticationRequired} -- to prompt the user to 068 * authenticate in order to complete the signing operation. Android 069 * applications can use this option to trigger a biometric prompt that 070 * is required to unlock a private key created with 071 * {@code setUserAuthenticationRequired(true)}. 072 * <li>{@link AllowWeakRSAKey} -- to allow weak RSA keys that are shorter 073 * than {@link com.nimbusds.jose.jwk.gen.RSAKeyGenerator#MIN_KEY_SIZE_BITS 074 * 2048 bits} 075 * </ul> 076 * 077 * <p>Supports the BouncyCastle FIPS provider for the PSxxx family of JWS algorithms. 078 * 079 * @author Vladimir Dzhuvinov 080 * @author Omer Levi Hevroni 081 * @version 2025-07-17 082 */ 083@ThreadSafe 084public class RSASSASigner extends RSASSAProvider implements JWSSigner { 085 086 087 /** 088 * The private RSA key. Represented by generic private key interface to 089 * support key stores that prevent exposure of the private key 090 * parameters via the {@link java.security.interfaces.RSAPrivateKey} 091 * API. 092 * 093 * See https://bitbucket.org/connect2id/nimbus-jose-jwt/issues/169 094 */ 095 private final PrivateKey privateKey; 096 097 098 /** 099 * The configured options, empty set if none. 100 */ 101 private final Set<JWSSignerOption> opts; 102 103 104 /** 105 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 106 * This constructor can also accept a private RSA key located in a 107 * PKCS#11 store that doesn't expose the private key parameters (such 108 * as a smart card or HSM). 109 * 110 * @param privateKey The private RSA key. Its algorithm must be "RSA" 111 * and its length at least 2048 bits. Note that the 112 * length of an RSA key in a PKCS#11 store cannot be 113 * checked. Must not be {@code null}. 114 */ 115 public RSASSASigner(final PrivateKey privateKey) { 116 117 this(privateKey, false); 118 } 119 120 121 /** 122 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 123 * This constructor can also accept a private RSA key located in a 124 * PKCS#11 store that doesn't expose the private key parameters (such 125 * as a smart card or HSM). 126 * 127 * @param privateKey The private RSA key. Its algorithm must be 128 * "RSA" and its length at least 2048 bits. Note 129 * that the length of an RSA key in a PKCS#11 store 130 * cannot be checked. Must not be {@code null}. 131 * @param allowWeakKey {@code true} to allow an RSA key shorter than 132 * 2048 bits. 133 */ 134 @Deprecated 135 public RSASSASigner(final PrivateKey privateKey, final boolean allowWeakKey) { 136 137 this( 138 privateKey, 139 allowWeakKey ? Collections.<JWSSignerOption>singleton(AllowWeakRSAKey.getInstance()) : Collections.<JWSSignerOption>emptySet() 140 ); 141 } 142 143 144 /** 145 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 146 * This constructor can also accept a private RSA key located in a 147 * PKCS#11 store that doesn't expose the private key parameters (such 148 * as a smart card or HSM). 149 * 150 * @param privateKey The private RSA key. Its algorithm must be "RSA" 151 * and its length at least 2048 bits. Note that the 152 * length of an RSA key in a PKCS#11 store cannot be 153 * checked. Must not be {@code null}. 154 * @param opts The signing options, empty or {@code null} if 155 * none. 156 */ 157 public RSASSASigner(final PrivateKey privateKey, final Set<JWSSignerOption> opts) { 158 159 if (privateKey instanceof RSAPrivateKey || "RSA".equalsIgnoreCase(privateKey.getAlgorithm())) { 160 // Will also allow "RSASSA-PSS" alg RSAPrivateKey instances with MGF1ParameterSpec 161 this.privateKey = privateKey; 162 } else { 163 throw new IllegalArgumentException("The private key algorithm must be RSA"); 164 } 165 166 this.opts = opts != null ? opts : Collections.<JWSSignerOption>emptySet(); 167 OptionUtils.ensureMinRSAPrivateKeySize(privateKey, this.opts); 168 } 169 170 171 /** 172 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 173 * 174 * @param rsaJWK The RSA JSON Web Key (JWK). Must contain or reference 175 * a private part. Its length must be at least 2048 bits. 176 * Note that the length of an RSA key in a PKCS#11 store 177 * cannot be checked. Must not be {@code null}. 178 * 179 * @throws JOSEException If the RSA JWK doesn't contain a private part 180 * or its extraction failed. 181 */ 182 public RSASSASigner(final RSAKey rsaJWK) 183 throws JOSEException { 184 185 this(rsaJWK, Collections.<JWSSignerOption>emptySet()); 186 } 187 188 189 /** 190 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 191 * 192 * @param rsaJWK The RSA JSON Web Key (JWK). Must contain or 193 * reference a private part. Its length must be at 194 * least 2048 bits. Note that the length of an RSA 195 * key in a PKCS#11 store cannot be checked. Must 196 * not be {@code null}. 197 * @param allowWeakKey {@code true} to allow an RSA key shorter than 198 * 2048 bits. 199 * 200 * @throws JOSEException If the RSA JWK doesn't contain a private part 201 * or its extraction failed. 202 */ 203 @Deprecated 204 public RSASSASigner(final RSAKey rsaJWK, final boolean allowWeakKey) 205 throws JOSEException { 206 207 this(RSAKeyUtils.toRSAPrivateKey(rsaJWK), allowWeakKey); 208 } 209 210 211 /** 212 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 213 * 214 * @param rsaJWK The RSA JSON Web Key (JWK). Must contain or reference 215 * a private part. Its length must be at least 2048 bits. 216 * Note that the length of an RSA key in a PKCS#11 store 217 * cannot be checked. Must not be {@code null}. 218 * @param opts The signing options, empty or {@code null} if 219 * none. 220 * 221 * @throws JOSEException If the RSA JWK doesn't contain a private part 222 * or its extraction failed. 223 */ 224 public RSASSASigner(final RSAKey rsaJWK, final Set<JWSSignerOption> opts) 225 throws JOSEException { 226 227 this(RSAKeyUtils.toRSAPrivateKey(rsaJWK), opts); 228 } 229 230 231 /** 232 * Gets the private RSA key. 233 * 234 * @return The private RSA key. Casting to 235 * {@link java.security.interfaces.RSAPrivateKey} may not be 236 * possible if the key is located in a PKCS#11 store that 237 * doesn't expose the private key parameters. 238 */ 239 public PrivateKey getPrivateKey() { 240 241 return privateKey; 242 } 243 244 245 @Override 246 public Base64URL sign(final JWSHeader header, final byte[] signingInput) 247 throws JOSEException { 248 249 final Signature signer = getInitiatedSignature(header); 250 251 if (opts.contains(UserAuthenticationRequired.getInstance())) { 252 253 throw new ActionRequiredForJWSCompletionException( 254 "Authenticate user to complete signing", 255 UserAuthenticationRequired.getInstance(), 256 new CompletableJWSObjectSigning() { 257 258 @Override 259 public Signature getInitializedSignature() { 260 return signer; 261 } 262 263 @Override 264 public Base64URL complete() throws JOSEException { 265 return sign(signingInput, signer); 266 } 267 } 268 ); 269 } 270 271 return sign(signingInput, signer); 272 } 273 274 275 private Signature getInitiatedSignature(final JWSHeader header) 276 throws JOSEException { 277 278 Signature signer = RSASSA.getSignerAndVerifier(header.getAlgorithm(), getJCAContext().getProvider()); 279 try { 280 signer.initSign(privateKey); 281 } catch (InvalidKeyException e) { 282 throw new JOSEException("Invalid private RSA key: " + e.getMessage(), e); 283 } 284 285 return signer; 286 } 287 288 289 private Base64URL sign(final byte[] signingInput, final Signature signer) 290 throws JOSEException { 291 292 try { 293 signer.update(signingInput); 294 return Base64URL.encode(signer.sign()); 295 } catch (SignatureException e) { 296 throw new JOSEException("RSA signature exception: " + e.getMessage(), e); 297 } 298 } 299}