001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2013-2016 ForgeRock AS. 015 */ 016 017package org.forgerock.json.jose.jws; 018 019import org.forgerock.json.jose.jwe.CompressionManager; 020import org.forgerock.json.jose.jws.handlers.SigningHandler; 021import org.forgerock.json.jose.jwt.Jwt; 022import org.forgerock.json.jose.jwt.JwtClaimsSet; 023import org.forgerock.json.jose.jwt.Payload; 024import org.forgerock.json.jose.utils.Utils; 025import org.forgerock.util.encode.Base64url; 026 027/** 028 * A JWS implementation of the <tt>Jwt</tt> interface. 029 * <p> 030 * JSON Web Signature (JWS) is a means of representing content secured with digital signatures or Message 031 * Authentication Codes (MACs) using JSON based data structures. 032 * <p> 033 * @see <a href="http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-11">JSON Web Signature Specification</a> 034 * 035 * @since 2.0.0 036 */ 037public class SignedJwt implements Jwt, Payload { 038 039 private final JwsHeader header; 040 private final Payload payload; 041 042 private final SigningHandler signingHandler; 043 044 private final byte[] signingInput; 045 private final byte[] signature; 046 047 /** 048 * Constructs a fresh, new SignedJwt from the given JwsHeader and JwtClaimsSet. 049 * <p> 050 * The specified private key will be used in the creation of the JWS signature. 051 * 052 * @param header The JwsHeader containing the header parameters of the JWS. 053 * @param claimsSet The JwtClaimsSet containing the claims of the JWS. 054 * @param signingHandler The SigningHandler instance used to sign the JWS. 055 */ 056 public SignedJwt(JwsHeader header, JwtClaimsSet claimsSet, SigningHandler signingHandler) { 057 this.header = header; 058 this.payload = claimsSet; 059 this.signingHandler = signingHandler; 060 061 this.signingInput = null; 062 this.signature = null; 063 } 064 065 /** 066 * Constructs a reconstructed SignedJwt from its constituent parts, the JwsHeader, JwtClaimsSet, signing input and 067 * signature. 068 * <p> 069 * For use when a signed JWT has been reconstructed from its base64url encoded string representation and the 070 * signature needs verifying. 071 * 072 * @param header The JwsHeader containing the header parameters of the JWS. 073 * @param claimsSet The JwsClaimsSet containing the claims of the JWS. 074 * @param signingInput The original data that was signed, being the base64url encoding of the JWS header and 075 * claims set concatenated using a "." character. 076 * @param signature The resulting signature of signing the signing input. 077 */ 078 public SignedJwt(JwsHeader header, JwtClaimsSet claimsSet, byte[] signingInput, byte[] signature) { 079 this.header = header; 080 this.payload = claimsSet; 081 this.signingInput = signingInput; 082 this.signature = signature; 083 084 this.signingHandler = null; 085 } 086 087 /** 088 * Constructs a fresh, new SignedJwt from the given JwsHeader and nested Encrypted JWT. 089 * <p> 090 * The specified private key will be used in the creation of the JWS signature. 091 * 092 * @param header The JwsHeader containing the header parameters of the JWS. 093 * @param nestedPayload The nested payload that will be the payload of this JWS. 094 * @param signingHandler The SigningHandler instance used to sign the JWS. 095 */ 096 protected SignedJwt(JwsHeader header, Payload nestedPayload, SigningHandler signingHandler) { 097 this.header = header; 098 this.payload = nestedPayload; 099 this.signingHandler = signingHandler; 100 101 this.signingInput = null; 102 this.signature = null; 103 } 104 105 /** 106 * Constructs a reconstructed SignedJwt from its constituent parts, the JwsHeader, nested Encrypted JWT, signing 107 * input and signature. 108 * <p> 109 * For use when a signed nested encrypted JWT has been reconstructed from its base64url encoded string 110 * representation and the signature needs verifying. 111 * 112 * @param header The JwsHeader containing the header parameters of the JWS. 113 * @param nestedPayload The nested payload that is the payload of the JWS. 114 * @param signingInput The original data that was signed, being the base64url encoding of the JWS header and 115 * payload concatenated using a "." character. 116 * @param signature The resulting signature of signing the signing input. 117 */ 118 protected SignedJwt(JwsHeader header, Payload nestedPayload, byte[] signingInput, byte[] signature) { 119 this.header = header; 120 this.payload = nestedPayload; 121 this.signingInput = signingInput; 122 this.signature = signature; 123 124 this.signingHandler = null; 125 } 126 127 /** 128 * {@inheritDoc} 129 */ 130 @Override 131 public JwsHeader getHeader() { 132 return header; 133 } 134 135 /** 136 * {@inheritDoc} 137 */ 138 @Override 139 public JwtClaimsSet getClaimsSet() { 140 return (JwtClaimsSet) payload; 141 } 142 143 /** 144 * Gets the payload for the JWS, which will either be a JWT Claims Set, {@link #getClaimsSet()}, or a nested 145 * EncryptedJwt, {@link org.forgerock.json.jose.jwe.EncryptedJwt}. 146 * 147 * @return The JWS' payload. 148 * @see EncryptedThenSignedJwt 149 */ 150 protected Payload getPayload() { 151 return payload; 152 } 153 154 /** 155 * {@inheritDoc} 156 */ 157 @Override 158 public String build() { 159 160 String jwsHeader = header.build(); 161 String encodedHeader = Utils.base64urlEncode(jwsHeader); 162 String jwsPayload = payload.build(); 163 164 String encodedClaims = new CompressionManager().compress(header.getCompressionAlgorithm(), jwsPayload); 165 166 String signingInput = encodedHeader + "." + encodedClaims; 167 168 byte[] signature = signingHandler.sign(header.getAlgorithm(), signingInput); 169 170 return signingInput + "." + Base64url.encode(signature); 171 } 172 173 /** 174 * Verifies that the JWS signature is valid for the contents of its payload. 175 * <p> 176 * The same private key must be given here as was used to create the signature. 177 * 178 * @param signingHandler The SigningHandler instance used to verify the JWS. 179 * @return <code>true</code> if the signature matches the JWS Header and payload. 180 */ 181 public boolean verify(SigningHandler signingHandler) { 182 return signingHandler.verify(header.getAlgorithm(), signingInput, signature); 183 } 184}