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.util.Collections; 022import java.util.LinkedHashSet; 023import java.util.Set; 024import javax.crypto.SecretKey; 025 026import com.nimbusds.jose.*; 027import com.nimbusds.jose.jwk.OctetSequenceKey; 028import com.nimbusds.jose.util.Base64URL; 029import com.nimbusds.jose.util.ByteUtils; 030import com.nimbusds.jose.util.StandardCharset; 031import net.jcip.annotations.ThreadSafe; 032 033 034 035/** 036 * Message Authentication Code (MAC) signer of 037 * {@link com.nimbusds.jose.JWSObject JWS objects}. Expects a secret key. 038 * 039 * <p>See RFC 7518 040 * <a href="https://tools.ietf.org/html/rfc7518#section-3.2">section 3.2</a> 041 * for more information. 042 * 043 * <p>This class is thread-safe. 044 * 045 * <p>Supports the following algorithms: 046 * 047 * <ul> 048 * <li>{@link com.nimbusds.jose.JWSAlgorithm#HS256} 049 * <li>{@link com.nimbusds.jose.JWSAlgorithm#HS384} 050 * <li>{@link com.nimbusds.jose.JWSAlgorithm#HS512} 051 * </ul> 052 * 053 * @author Vladimir Dzhuvinov 054 * @version 2016-07-27 055 */ 056@ThreadSafe 057public class MACSigner extends MACProvider implements JWSSigner { 058 059 060 /** 061 * Returns the minimal required secret length for the specified HMAC 062 * JWS algorithm. 063 * 064 * @param alg The HMAC JWS algorithm. Must be 065 * {@link #SUPPORTED_ALGORITHMS supported} and not 066 * {@code null}. 067 * 068 * @return The minimal required secret length, in bits. 069 * 070 * @throws JOSEException If the algorithm is not supported. 071 */ 072 public static int getMinRequiredSecretLength(final JWSAlgorithm alg) 073 throws JOSEException { 074 075 if (JWSAlgorithm.HS256.equals(alg)) { 076 return 256; 077 } else if (JWSAlgorithm.HS384.equals(alg)) { 078 return 384; 079 } else if (JWSAlgorithm.HS512.equals(alg)) { 080 return 512; 081 } else { 082 throw new JOSEException(AlgorithmSupportMessage.unsupportedJWSAlgorithm( 083 alg, 084 SUPPORTED_ALGORITHMS)); 085 } 086 } 087 088 089 /** 090 * Returns the compatible JWS HMAC algorithms for the specified secret 091 * length. 092 * 093 * @param secretLength The secret length in bits. Must not be negative. 094 * 095 * @return The compatible HMAC algorithms, empty set if the secret 096 * length is too short for any algorithm. 097 */ 098 public static Set<JWSAlgorithm> getCompatibleAlgorithms(final int secretLength) { 099 100 Set<JWSAlgorithm> hmacAlgs = new LinkedHashSet<>(); 101 102 if (secretLength >= 256) 103 hmacAlgs.add(JWSAlgorithm.HS256); 104 105 if (secretLength >= 384) 106 hmacAlgs.add(JWSAlgorithm.HS384); 107 108 if (secretLength >= 512) 109 hmacAlgs.add(JWSAlgorithm.HS512); 110 111 return Collections.unmodifiableSet(hmacAlgs); 112 } 113 114 115 /** 116 * Creates a new Message Authentication (MAC) signer. 117 * 118 * @param secret The secret. Must be at least 256 bits long and not 119 * {@code null}. 120 * 121 * @throws KeyLengthException If the secret length is shorter than the 122 * minimum 256-bit requirement. 123 */ 124 public MACSigner(final byte[] secret) 125 throws KeyLengthException { 126 127 super(secret, getCompatibleAlgorithms(ByteUtils.bitLength(secret.length))); 128 } 129 130 131 /** 132 * Creates a new Message Authentication (MAC) signer. 133 * 134 * @param secretString The secret as a UTF-8 encoded string. Must be at 135 * least 256 bits long and not {@code null}. 136 * 137 * @throws KeyLengthException If the secret length is shorter than the 138 * minimum 256-bit requirement. 139 */ 140 public MACSigner(final String secretString) 141 throws KeyLengthException { 142 143 this(secretString.getBytes(StandardCharset.UTF_8)); 144 } 145 146 147 /** 148 * Creates a new Message Authentication (MAC) signer. 149 * 150 * @param secretKey The secret key. Must be at least 256 bits long and 151 * not {@code null}. 152 * 153 * @throws KeyLengthException If the secret length is shorter than the 154 * minimum 256-bit requirement. 155 */ 156 public MACSigner(final SecretKey secretKey) 157 throws KeyLengthException { 158 159 this(secretKey.getEncoded()); 160 } 161 162 163 /** 164 * Creates a new Message Authentication (MAC) signer. 165 * 166 * @param jwk The secret as a JWK. Must be at least 256 bits long and 167 * not {@code null}. 168 * 169 * @throws KeyLengthException If the secret length is shorter than the 170 * minimum 256-bit requirement. 171 */ 172 public MACSigner(final OctetSequenceKey jwk) 173 throws KeyLengthException { 174 175 this(jwk.toByteArray()); 176 } 177 178 179 @Override 180 public Base64URL sign(final JWSHeader header, final byte[] signingInput) 181 throws JOSEException { 182 183 final int minRequiredLength = getMinRequiredSecretLength(header.getAlgorithm()); 184 185 if (getSecret().length < ByteUtils.byteLength(minRequiredLength)) { 186 throw new KeyLengthException("The secret length for " + header.getAlgorithm() + " must be at least " + minRequiredLength + " bits"); 187 } 188 189 String jcaAlg = getJCAAlgorithmName(header.getAlgorithm()); 190 byte[] hmac = HMAC.compute(jcaAlg, getSecret(), signingInput, getJCAContext().getProvider()); 191 return Base64URL.encode(hmac); 192 } 193}