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; 025 026import static com.nimbusds.jose.jwk.gen.RSAKeyGenerator.MIN_KEY_SIZE_BITS; 027 028import com.nimbusds.jose.JOSEException; 029import com.nimbusds.jose.JWSHeader; 030import com.nimbusds.jose.JWSSigner; 031import com.nimbusds.jose.jwk.RSAKey; 032import com.nimbusds.jose.util.Base64URL; 033import net.jcip.annotations.ThreadSafe; 034 035 036 037/** 038 * RSA Signature-Scheme-with-Appendix (RSASSA) signer of 039 * {@link com.nimbusds.jose.JWSObject JWS objects}. Expects a private RSA key. 040 * 041 * <p>See RFC 7518, sections 042 * <a href="https://tools.ietf.org/html/rfc7518#section-3.3">3.3</a> and 043 * <a href="https://tools.ietf.org/html/rfc7518#section-3.5">3.5</a> for more 044 * information. 045 * 046 * <p>This class is thread-safe. 047 * 048 * <p>Supports the following algorithms: 049 * 050 * <ul> 051 * <li>{@link com.nimbusds.jose.JWSAlgorithm#RS256} 052 * <li>{@link com.nimbusds.jose.JWSAlgorithm#RS384} 053 * <li>{@link com.nimbusds.jose.JWSAlgorithm#RS512} 054 * <li>{@link com.nimbusds.jose.JWSAlgorithm#PS256} 055 * <li>{@link com.nimbusds.jose.JWSAlgorithm#PS384} 056 * <li>{@link com.nimbusds.jose.JWSAlgorithm#PS512} 057 * </ul> 058 * 059 * @author Vladimir Dzhuvinov 060 * @author Omer Levi Hevroni 061 * @version 2018-10-11 062 */ 063@ThreadSafe 064public class RSASSASigner extends RSASSAProvider implements JWSSigner { 065 066 067 /** 068 * The private RSA key. Represented by generic private key interface to 069 * support key stores that prevent exposure of the private key 070 * parameters via the {@link java.security.interfaces.RSAPrivateKey} 071 * API. 072 * 073 * See https://bitbucket.org/connect2id/nimbus-jose-jwt/issues/169 074 */ 075 private final PrivateKey privateKey; 076 077 078 /** 079 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 080 * This constructor can also accept a private RSA key located in a 081 * PKCS#11 store that doesn't expose the private key parameters (such 082 * as a smart card or HSM). 083 * 084 * @param privateKey The private RSA key. Its algorithm must be "RSA" 085 * and its length at least 2048 bits. Note that the 086 * length of an RSA key in a PKCS#11 store cannot be 087 * checked. Must not be {@code null}. 088 */ 089 public RSASSASigner(final PrivateKey privateKey) { 090 091 this(privateKey, false); 092 } 093 094 095 /** 096 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 097 * This constructor can also accept a private RSA key located in a 098 * PKCS#11 store that doesn't expose the private key parameters (such 099 * as a smart card or HSM). 100 * 101 * @param privateKey The private RSA key. Its algorithm must be 102 * "RSA" and its length at least 2048 bits. Note 103 * that the length of an RSA key in a PKCS#11 store 104 * cannot be checked. Must not be {@code null}. 105 * @param allowWeakKey {@code true} to allow an RSA key shorter than 106 * 2048 bits. 107 */ 108 public RSASSASigner(final PrivateKey privateKey, final boolean allowWeakKey) { 109 110 if (! "RSA".equalsIgnoreCase(privateKey.getAlgorithm())) { 111 throw new IllegalArgumentException("The private key algorithm must be RSA"); 112 } 113 114 if (! allowWeakKey) { 115 116 int keyBitLength = RSAKeyUtils.keyBitLength(privateKey); 117 118 if (keyBitLength > 0 && keyBitLength < MIN_KEY_SIZE_BITS) { 119 throw new IllegalArgumentException("The RSA key size must be at least " + MIN_KEY_SIZE_BITS + " bits"); 120 } 121 } 122 123 this.privateKey = privateKey; 124 } 125 126 127 /** 128 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 129 * 130 * @param rsaJWK The RSA JSON Web Key (JWK). Must contain or reference 131 * a private part. Its length must be at least 2048 bits. 132 * Note that the length of an RSA key in a PKCS#11 store 133 * cannot be checked. Must not be {@code null}. 134 * 135 * @throws JOSEException If the RSA JWK doesn't contain a private part 136 * or its extraction failed. 137 */ 138 public RSASSASigner(final RSAKey rsaJWK) 139 throws JOSEException { 140 141 this(rsaJWK, false); 142 } 143 144 145 /** 146 * Creates a new RSA Signature-Scheme-with-Appendix (RSASSA) signer. 147 * 148 * @param rsaJWK The RSA JSON Web Key (JWK). Must contain or 149 * reference a private part. Its length must be at 150 * least 2048 bits. Note that the length of an RSA 151 * key in a PKCS#11 store cannot be checked. Must 152 * not be {@code null}. 153 * @param allowWeakKey {@code true} to allow an RSA key shorter than 154 * 2048 bits. 155 * 156 * @throws JOSEException If the RSA JWK doesn't contain a private part 157 * or its extraction failed. 158 */ 159 public RSASSASigner(final RSAKey rsaJWK, final boolean allowWeakKey) 160 throws JOSEException { 161 162 this(RSAKeyUtils.toRSAPrivateKey(rsaJWK), allowWeakKey); 163 } 164 165 166 /** 167 * Gets the private RSA key. 168 * 169 * @return The private RSA key. Casting to 170 * {@link java.security.interfaces.RSAPrivateKey} may not be 171 * possible if the key is located in a PKCS#11 store that 172 * doesn't expose the private key parameters. 173 */ 174 public PrivateKey getPrivateKey() { 175 176 return privateKey; 177 } 178 179 180 @Override 181 public Base64URL sign(final JWSHeader header, final byte[] signingInput) 182 throws JOSEException { 183 184 Signature signer = RSASSA.getSignerAndVerifier(header.getAlgorithm(), getJCAContext().getProvider()); 185 186 try { 187 signer.initSign(privateKey); 188 signer.update(signingInput); 189 return Base64URL.encode(signer.sign()); 190 191 } catch (InvalidKeyException e) { 192 throw new JOSEException("Invalid private RSA key: " + e.getMessage(), e); 193 194 } catch (SignatureException e) { 195 throw new JOSEException("RSA signature exception: " + e.getMessage(), e); 196 } 197 } 198}