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