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 081 * {@link com.nimbusds.jose.crypto.bc.BouncyCastleFIPSProviderSingleton 082 * BouncyCastle FIPS provider} for the PSxxx family of JWS algorithms. 083 * 084 * @author Vladimir Dzhuvinov 085 * @author Omer Levi Hevroni 086 * @version 2023-01-31 087 */ 088@ThreadSafe 089public class RSASSASigner extends RSASSAProvider implements JWSSigner { 090 091 092 /** 093 * The private RSA key. Represented by generic private key interface to 094 * support key stores that prevent exposure of the private key 095 * parameters via the {@link java.security.interfaces.RSAPrivateKey} 096 * API. 097 * 098 * See https://bitbucket.org/connect2id/nimbus-jose-jwt/issues/169 099 */ 100 private final PrivateKey privateKey; 101 102 103 /** 104 * The configured options, empty set if none. 105 */ 106 private final Set<JWSSignerOption> opts; 107 108 109 /** 110 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 111 * This constructor can also accept a private RSA key located in a 112 * PKCS#11 store that doesn't expose the private key parameters (such 113 * as a smart card or HSM). 114 * 115 * @param privateKey The private RSA key. Its algorithm must be "RSA" 116 * and its length at least 2048 bits. Note that the 117 * length of an RSA key in a PKCS#11 store cannot be 118 * checked. Must not be {@code null}. 119 */ 120 public RSASSASigner(final PrivateKey privateKey) { 121 122 this(privateKey, false); 123 } 124 125 126 /** 127 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 128 * This constructor can also accept a private RSA key located in a 129 * PKCS#11 store that doesn't expose the private key parameters (such 130 * as a smart card or HSM). 131 * 132 * @param privateKey The private RSA key. Its algorithm must be 133 * "RSA" and its length at least 2048 bits. Note 134 * that the length of an RSA key in a PKCS#11 store 135 * cannot be checked. Must not be {@code null}. 136 * @param allowWeakKey {@code true} to allow an RSA key shorter than 137 * 2048 bits. 138 */ 139 @Deprecated 140 public RSASSASigner(final PrivateKey privateKey, final boolean allowWeakKey) { 141 142 this(privateKey, allowWeakKey ? Collections.singleton((JWSSignerOption) AllowWeakRSAKey.getInstance()) : Collections.<JWSSignerOption>emptySet()); 143 } 144 145 146 /** 147 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 148 * This constructor can also accept a private RSA key located in a 149 * PKCS#11 store that doesn't expose the private key parameters (such 150 * as a smart card or HSM). 151 * 152 * @param privateKey The private RSA key. Its algorithm must be "RSA" 153 * and its length at least 2048 bits. Note that the 154 * length of an RSA key in a PKCS#11 store cannot be 155 * checked. Must not be {@code null}. 156 * @param opts The signing options, empty or {@code null} if 157 * none. 158 */ 159 public RSASSASigner(final PrivateKey privateKey, final Set<JWSSignerOption> opts) { 160 161 if (privateKey instanceof RSAPrivateKey || "RSA".equalsIgnoreCase(privateKey.getAlgorithm())) { 162 // Will also allow "RSASSA-PSS" alg RSAPrivateKey instances with MGF1ParameterSpec 163 this.privateKey = privateKey; 164 } else { 165 throw new IllegalArgumentException("The private key algorithm must be RSA"); 166 } 167 168 this.opts = opts != null ? opts : Collections.<JWSSignerOption>emptySet(); 169 170 if (! OptionUtils.optionIsPresent(this.opts, AllowWeakRSAKey.class)) { 171 int keyBitLength = RSAKeyUtils.keyBitLength(privateKey); 172 173 if (keyBitLength > 0 && keyBitLength < MIN_KEY_SIZE_BITS) { 174 throw new IllegalArgumentException("The RSA key size must be at least " + MIN_KEY_SIZE_BITS + " bits"); 175 } 176 } 177 } 178 179 180 /** 181 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 182 * 183 * @param rsaJWK The RSA JSON Web Key (JWK). Must contain or reference 184 * a private part. Its length must be at least 2048 bits. 185 * Note that the length of an RSA key in a PKCS#11 store 186 * cannot be checked. Must not be {@code null}. 187 * 188 * @throws JOSEException If the RSA JWK doesn't contain a private part 189 * or its extraction failed. 190 */ 191 public RSASSASigner(final RSAKey rsaJWK) 192 throws JOSEException { 193 194 this(rsaJWK, null); 195 } 196 197 198 /** 199 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 200 * 201 * @param rsaJWK The RSA JSON Web Key (JWK). Must contain or 202 * reference a private part. Its length must be at 203 * least 2048 bits. Note that the length of an RSA 204 * key in a PKCS#11 store cannot be checked. Must 205 * not be {@code null}. 206 * @param allowWeakKey {@code true} to allow an RSA key shorter than 207 * 2048 bits. 208 * 209 * @throws JOSEException If the RSA JWK doesn't contain a private part 210 * or its extraction failed. 211 */ 212 @Deprecated 213 public RSASSASigner(final RSAKey rsaJWK, final boolean allowWeakKey) 214 throws JOSEException { 215 216 this(RSAKeyUtils.toRSAPrivateKey(rsaJWK), allowWeakKey); 217 } 218 219 220 /** 221 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 222 * 223 * @param rsaJWK The RSA JSON Web Key (JWK). Must contain or reference 224 * a private part. Its length must be at least 2048 bits. 225 * Note that the length of an RSA key in a PKCS#11 store 226 * cannot be checked. Must not be {@code null}. 227 * @param opts The signing options, empty or {@code null} if 228 * none. 229 * 230 * @throws JOSEException If the RSA JWK doesn't contain a private part 231 * or its extraction failed. 232 */ 233 public RSASSASigner(final RSAKey rsaJWK, final Set<JWSSignerOption> opts) 234 throws JOSEException { 235 236 this(RSAKeyUtils.toRSAPrivateKey(rsaJWK), opts); 237 } 238 239 240 /** 241 * Gets the private RSA key. 242 * 243 * @return The private RSA key. Casting to 244 * {@link java.security.interfaces.RSAPrivateKey} may not be 245 * possible if the key is located in a PKCS#11 store that 246 * doesn't expose the private key parameters. 247 */ 248 public PrivateKey getPrivateKey() { 249 250 return privateKey; 251 } 252 253 254 @Override 255 public Base64URL sign(final JWSHeader header, final byte[] signingInput) 256 throws JOSEException { 257 258 final Signature signer = getInitiatedSignature(header); 259 260 if (OptionUtils.optionIsPresent(opts, UserAuthenticationRequired.class)) { 261 262 throw new ActionRequiredForJWSCompletionException( 263 "Authenticate user to complete signing", 264 UserAuthenticationRequired.getInstance(), 265 new CompletableJWSObjectSigning() { 266 267 @Override 268 public Signature getInitializedSignature() { 269 return signer; 270 } 271 272 @Override 273 public Base64URL complete() throws JOSEException { 274 return sign(signingInput, signer); 275 } 276 } 277 ); 278 } 279 280 return sign(signingInput, signer); 281 } 282 283 284 private Signature getInitiatedSignature(final JWSHeader header) 285 throws JOSEException { 286 287 Signature signer = RSASSA.getSignerAndVerifier(header.getAlgorithm(), getJCAContext().getProvider()); 288 try { 289 signer.initSign(privateKey); 290 } catch (InvalidKeyException e) { 291 throw new JOSEException("Invalid private RSA key: " + e.getMessage(), e); 292 } 293 294 return signer; 295 } 296 297 298 private Base64URL sign(final byte[] signingInput, final Signature signer) 299 throws JOSEException { 300 301 try { 302 signer.update(signingInput); 303 return Base64URL.encode(signer.sign()); 304 } catch (SignatureException e) { 305 throw new JOSEException("RSA signature exception: " + e.getMessage(), e); 306 } 307 } 308}