001    package com.nimbusds.jose.crypto;
002    
003    
004    import java.util.HashSet;
005    import java.util.Set;
006    
007    import java.security.InvalidKeyException;
008    
009    import javax.crypto.Mac;
010    
011    import javax.crypto.spec.SecretKeySpec;
012    
013    import net.jcip.annotations.ThreadSafe;
014    
015    import com.nimbusds.jose.JOSEException;
016    import com.nimbusds.jose.JWSHeaderFilter;
017    import com.nimbusds.jose.JWSVerifier;
018    import com.nimbusds.jose.ReadOnlyJWSHeader;
019    
020    import com.nimbusds.jose.util.Base64URL;
021    
022    
023    
024    /**
025     * Message Authentication Code (MAC) verifier of 
026     * {@link com.nimbusds.jose.JWSObject JWS objects}. This class is thread-safe.
027     *
028     * <p>Supports the following JSON Web Algorithms (JWAs):
029     *
030     * <ul>
031     *     <li>{@link com.nimbusds.jose.JWSAlgorithm#HS256}
032     *     <li>{@link com.nimbusds.jose.JWSAlgorithm#HS384}
033     *     <li>{@link com.nimbusds.jose.JWSAlgorithm#HS512}
034     * </ul>
035     *
036     * <p>Accepts the following JWS header parameters:
037     *
038     * <ul>
039     *     <li>{@code alg}
040     *     <li>{@code typ}
041     *     <li>{@code cty}
042     * </ul>
043     * 
044     * @author Vladimir Dzhuvinov
045     * @version $version$ (2012-10-23)
046     */
047    @ThreadSafe
048    public class MACVerifier extends MACProvider implements JWSVerifier {
049    
050    
051            /**
052             * The accepted JWS header parameters.
053             */
054            private static final Set<String> ACCEPTED_HEADER_PARAMETERS;
055            
056            
057            /**
058             * Initialises the accepted JWS header parameters.
059             */
060            static {
061            
062                    Set<String> params = new HashSet<String>();
063                    params.add("alg");
064                    params.add("typ");
065                    params.add("cty");
066                    
067                    ACCEPTED_HEADER_PARAMETERS = params;
068            }
069            
070            
071            /**
072             * The JWS header filter.
073             */
074            private DefaultJWSHeaderFilter headerFilter;
075            
076             
077            /**
078            * Creates a new Message Authentication (MAC) verifier.
079            *
080            * @param sharedSecret The shared secret. Must not be {@code null}.
081            */
082            public MACVerifier(final byte[] sharedSecret) {
083    
084                    super(sharedSecret);
085    
086                    headerFilter = new DefaultJWSHeaderFilter(supportedAlgorithms(), ACCEPTED_HEADER_PARAMETERS);
087            }
088            
089            
090            @Override
091            public JWSHeaderFilter getJWSHeaderFilter() {
092            
093                    return headerFilter;
094            }
095    
096    
097            @Override
098            public boolean verify(final ReadOnlyJWSHeader header, 
099                                  final byte[] signedContent, 
100                                  final Base64URL signature)
101                    throws JOSEException {
102                    
103                    Mac mac = getMAC(header.getAlgorithm());
104                    
105                    try {
106                            mac.init(new SecretKeySpec(getSharedSecret(), mac.getAlgorithm()));
107                            
108                    } catch (InvalidKeyException e) {
109                    
110                            throw new JOSEException("Invalid HMAC key: " + e.getMessage(), e);
111                    }
112                    
113                    mac.update(signedContent);
114                    
115                    Base64URL expectedSignature = Base64URL.encode(mac.doFinal());
116                    
117                    if (expectedSignature.equals(signature))
118                            return true;
119                    else
120                            return false;
121            }
122    }