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