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.util.Collections; 026import java.util.Set; 027 028import static com.nimbusds.jose.jwk.gen.RSAKeyGenerator.MIN_KEY_SIZE_BITS; 029 030import net.jcip.annotations.ThreadSafe; 031 032import com.nimbusds.jose.*; 033import com.nimbusds.jose.crypto.impl.RSAKeyUtils; 034import com.nimbusds.jose.crypto.impl.RSASSA; 035import com.nimbusds.jose.crypto.impl.RSASSAProvider; 036import com.nimbusds.jose.crypto.opts.AllowWeakRSAKey; 037import com.nimbusds.jose.crypto.opts.OptionUtils; 038import com.nimbusds.jose.crypto.opts.UserAuthenticationRequired; 039import com.nimbusds.jose.jwk.RSAKey; 040import com.nimbusds.jose.util.Base64URL; 041 042 043 044/** 045 * RSA Signature-Scheme-with-Appendix (RSASSA) signer of 046 * {@link com.nimbusds.jose.JWSObject JWS objects}. Expects a private RSA key. 047 * 048 * <p>See RFC 7518, sections 049 * <a href="https://tools.ietf.org/html/rfc7518#section-3.3">3.3</a> and 050 * <a href="https://tools.ietf.org/html/rfc7518#section-3.5">3.5</a> for more 051 * information. 052 * 053 * <p>This class is thread-safe. 054 * 055 * <p>Supports the following algorithms: 056 * 057 * <ul> 058 * <li>{@link com.nimbusds.jose.JWSAlgorithm#RS256} 059 * <li>{@link com.nimbusds.jose.JWSAlgorithm#RS384} 060 * <li>{@link com.nimbusds.jose.JWSAlgorithm#RS512} 061 * <li>{@link com.nimbusds.jose.JWSAlgorithm#PS256} 062 * <li>{@link com.nimbusds.jose.JWSAlgorithm#PS384} 063 * <li>{@link com.nimbusds.jose.JWSAlgorithm#PS512} 064 * </ul> 065 * 066 * <p>Supports the following {@link JWSSignerOption options}: 067 * 068 * <ul> 069 * <li>{@link UserAuthenticationRequired} -- to prompt the user to 070 * authenticate in order to complete the signing operation. Android 071 * applications can use this option to trigger a biometric prompt that 072 * is required to unlock a private key created with 073 * {@code setUserAuthenticationRequired(true)}. 074 * <li>{@link AllowWeakRSAKey} -- to allow weak RSA keys that are shorter 075 * than {@link com.nimbusds.jose.jwk.gen.RSAKeyGenerator#MIN_KEY_SIZE_BITS 076 * 2048 bits} 077 * </ul> 078 * 079 * <p>Supports the 080 * {@link com.nimbusds.jose.crypto.bc.BouncyCastleFIPSProviderSingleton 081 * BouncyCastle FIPS provider} for the PSxxx family of JWS algorithms. 082 * 083 * @author Vladimir Dzhuvinov 084 * @author Omer Levi Hevroni 085 * @version 2020-12-27 086 */ 087@ThreadSafe 088public class RSASSASigner extends RSASSAProvider implements JWSSigner { 089 090 091 /** 092 * The private RSA key. Represented by generic private key interface to 093 * support key stores that prevent exposure of the private key 094 * parameters via the {@link java.security.interfaces.RSAPrivateKey} 095 * API. 096 * 097 * See https://bitbucket.org/connect2id/nimbus-jose-jwt/issues/169 098 */ 099 private final PrivateKey privateKey; 100 101 102 /** 103 * The configured options, empty set if none. 104 */ 105 private final Set<JWSSignerOption> opts; 106 107 108 /** 109 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 110 * This constructor can also accept a private RSA key located in a 111 * PKCS#11 store that doesn't expose the private key parameters (such 112 * as a smart card or HSM). 113 * 114 * @param privateKey The private RSA key. Its algorithm must be "RSA" 115 * and its length at least 2048 bits. Note that the 116 * length of an RSA key in a PKCS#11 store cannot be 117 * checked. Must not be {@code null}. 118 */ 119 public RSASSASigner(final PrivateKey privateKey) { 120 121 this(privateKey, false); 122 } 123 124 125 /** 126 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 127 * This constructor can also accept a private RSA key located in a 128 * PKCS#11 store that doesn't expose the private key parameters (such 129 * as a smart card or HSM). 130 * 131 * @param privateKey The private RSA key. Its algorithm must be 132 * "RSA" and its length at least 2048 bits. Note 133 * that the length of an RSA key in a PKCS#11 store 134 * cannot be checked. Must not be {@code null}. 135 * @param allowWeakKey {@code true} to allow an RSA key shorter than 136 * 2048 bits. 137 */ 138 @Deprecated 139 public RSASSASigner(final PrivateKey privateKey, final boolean allowWeakKey) { 140 141 this(privateKey, allowWeakKey ? Collections.singleton((JWSSignerOption) AllowWeakRSAKey.getInstance()) : Collections.<JWSSignerOption>emptySet()); 142 } 143 144 145 /** 146 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 147 * This constructor can also accept a private RSA key located in a 148 * PKCS#11 store that doesn't expose the private key parameters (such 149 * as a smart card or HSM). 150 * 151 * @param privateKey The private RSA key. Its algorithm must be "RSA" 152 * and its length at least 2048 bits. Note that the 153 * length of an RSA key in a PKCS#11 store cannot be 154 * checked. Must not be {@code null}. 155 * @param opts The signing options, empty or {@code null} if 156 * none. 157 */ 158 public RSASSASigner(final PrivateKey privateKey, final Set<JWSSignerOption> opts) { 159 160 if (! "RSA".equalsIgnoreCase(privateKey.getAlgorithm())) { 161 throw new IllegalArgumentException("The private key algorithm must be RSA"); 162 } 163 164 this.privateKey = privateKey; 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(RSAKeyUtils.toRSAPrivateKey(rsaJWK)); 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 * Gets the private RSA key. 220 * 221 * @return The private RSA key. Casting to 222 * {@link java.security.interfaces.RSAPrivateKey} may not be 223 * possible if the key is located in a PKCS#11 store that 224 * doesn't expose the private key parameters. 225 */ 226 public PrivateKey getPrivateKey() { 227 228 return privateKey; 229 } 230 231 232 @Override 233 public Base64URL sign(final JWSHeader header, final byte[] signingInput) 234 throws JOSEException { 235 236 final Signature signer = getInitiatedSignature(header); 237 238 if (OptionUtils.optionIsPresent(opts, UserAuthenticationRequired.class)) { 239 240 throw new ActionRequiredForJWSCompletionException( 241 "Authenticate user to complete signing", 242 UserAuthenticationRequired.getInstance(), 243 new CompletableJWSObjectSigning() { 244 @Override 245 public Base64URL complete() throws JOSEException { 246 return sign(signingInput, signer); 247 } 248 } 249 ); 250 } 251 252 return sign(signingInput, signer); 253 } 254 255 256 private Signature getInitiatedSignature(final JWSHeader header) 257 throws JOSEException { 258 259 Signature signer = RSASSA.getSignerAndVerifier(header.getAlgorithm(), getJCAContext().getProvider()); 260 try { 261 signer.initSign(privateKey); 262 } catch (InvalidKeyException e) { 263 throw new JOSEException("Invalid private RSA key: " + e.getMessage(), e); 264 } 265 266 return signer; 267 } 268 269 270 private Base64URL sign(final byte[] signingInput, final Signature signer) 271 throws JOSEException { 272 273 try { 274 signer.update(signingInput); 275 return Base64URL.encode(signer.sign()); 276 } catch (SignatureException e) { 277 throw new JOSEException("RSA signature exception: " + e.getMessage(), e); 278 } 279 } 280}