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 * @author Vladimir Dzhuvinov 080 * @author Omer Levi Hevroni 081 * @version 2020-12-27 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(privateKey, allowWeakKey ? Collections.singleton((JWSSignerOption) AllowWeakRSAKey.getInstance()) : Collections.<JWSSignerOption>emptySet()); 138 } 139 140 141 /** 142 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 143 * This constructor can also accept a private RSA key located in a 144 * PKCS#11 store that doesn't expose the private key parameters (such 145 * as a smart card or HSM). 146 * 147 * @param privateKey The private RSA key. Its algorithm must be "RSA" 148 * and its length at least 2048 bits. Note that the 149 * length of an RSA key in a PKCS#11 store cannot be 150 * checked. Must not be {@code null}. 151 * @param opts The signing options, empty or {@code null} if 152 * none. 153 */ 154 public RSASSASigner(final PrivateKey privateKey, final Set<JWSSignerOption> opts) { 155 156 if (! "RSA".equalsIgnoreCase(privateKey.getAlgorithm())) { 157 throw new IllegalArgumentException("The private key algorithm must be RSA"); 158 } 159 160 this.privateKey = privateKey; 161 162 this.opts = opts != null ? opts : Collections.<JWSSignerOption>emptySet(); 163 164 if (! OptionUtils.optionIsPresent(this.opts, AllowWeakRSAKey.class)) { 165 int keyBitLength = RSAKeyUtils.keyBitLength(privateKey); 166 167 if (keyBitLength > 0 && keyBitLength < MIN_KEY_SIZE_BITS) { 168 throw new IllegalArgumentException("The RSA key size must be at least " + MIN_KEY_SIZE_BITS + " bits"); 169 } 170 } 171 } 172 173 174 /** 175 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 176 * 177 * @param rsaJWK The RSA JSON Web Key (JWK). Must contain or reference 178 * a private part. Its length must be at least 2048 bits. 179 * Note that the length of an RSA key in a PKCS#11 store 180 * cannot be checked. Must not be {@code null}. 181 * 182 * @throws JOSEException If the RSA JWK doesn't contain a private part 183 * or its extraction failed. 184 */ 185 public RSASSASigner(final RSAKey rsaJWK) 186 throws JOSEException { 187 188 this(rsaJWK.toRSAPrivateKey()); 189 } 190 191 192 /** 193 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 194 * 195 * @param rsaJWK The RSA JSON Web Key (JWK). Must contain or 196 * reference a private part. Its length must be at 197 * least 2048 bits. Note that the length of an RSA 198 * key in a PKCS#11 store cannot be checked. Must 199 * not be {@code null}. 200 * @param allowWeakKey {@code true} to allow an RSA key shorter than 201 * 2048 bits. 202 * 203 * @throws JOSEException If the RSA JWK doesn't contain a private part 204 * or its extraction failed. 205 */ 206 @Deprecated 207 public RSASSASigner(final RSAKey rsaJWK, final boolean allowWeakKey) 208 throws JOSEException { 209 210 this(RSAKeyUtils.toRSAPrivateKey(rsaJWK), allowWeakKey); 211 } 212 213 214 /** 215 * Gets the private RSA key. 216 * 217 * @return The private RSA key. Casting to 218 * {@link java.security.interfaces.RSAPrivateKey} may not be 219 * possible if the key is located in a PKCS#11 store that 220 * doesn't expose the private key parameters. 221 */ 222 public PrivateKey getPrivateKey() { 223 224 return privateKey; 225 } 226 227 228 @Override 229 public Base64URL sign(final JWSHeader header, final byte[] signingInput) 230 throws JOSEException { 231 232 final Signature signer = getInitiatedSignature(header); 233 234 if (OptionUtils.optionIsPresent(opts, UserAuthenticationRequired.class)) { 235 236 throw new ActionRequiredForJWSCompletionException( 237 "Authenticate user to complete signing", 238 UserAuthenticationRequired.getInstance(), 239 new CompletableJWSObjectSigning() { 240 @Override 241 public Base64URL complete() throws JOSEException { 242 return sign(signingInput, signer); 243 } 244 } 245 ); 246 } 247 248 return sign(signingInput, signer); 249 } 250 251 252 private Signature getInitiatedSignature(final JWSHeader header) 253 throws JOSEException { 254 255 Signature signer = RSASSA.getSignerAndVerifier(header.getAlgorithm(), getJCAContext().getProvider()); 256 try { 257 signer.initSign(privateKey); 258 } catch (InvalidKeyException e) { 259 throw new JOSEException("Invalid private RSA key: " + e.getMessage(), e); 260 } 261 262 return signer; 263 } 264 265 266 private Base64URL sign(final byte[] signingInput, final Signature signer) 267 throws JOSEException { 268 269 try { 270 signer.update(signingInput); 271 return Base64URL.encode(signer.sign()); 272 } catch (SignatureException e) { 273 throw new JOSEException("RSA signature exception: " + e.getMessage(), e); 274 } 275 } 276}