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