/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cxf.rs.security.jose.jws;
import java.security.PublicKey;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.jaxrs.json.basic.JsonMapObject;
import org.apache.cxf.jaxrs.json.basic.JsonMapObjectReaderWriter;
import org.apache.cxf.rs.security.jose.common.JoseUtils;
import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
import org.apache.cxf.rs.security.jose.jwk.JsonWebKey;
public class JwsJsonConsumer {
protected static final Logger LOG = LogUtils.getL7dLogger(JwsJsonConsumer.class);
private String jwsSignedDocument;
private String jwsPayload;
private List<JwsJsonSignatureEntry> signatures = new LinkedList<JwsJsonSignatureEntry>();
/**
* @param jwsSignedDocument
* signed JWS Document
*/
public JwsJsonConsumer(String jwsSignedDocument) {
this(jwsSignedDocument, null);
}
public JwsJsonConsumer(String jwsSignedDocument, String detachedPayload) {
this.jwsSignedDocument = jwsSignedDocument;
prepare(detachedPayload);
}
private void prepare(String detachedPayload) {
JsonMapObject jsonObject = new JsonMapObject();
new JsonMapObjectReaderWriter().fromJson(jsonObject, jwsSignedDocument);
Map<String, Object> jsonObjectMap = jsonObject.asMap();
jwsPayload = (String)jsonObjectMap.get("payload");
if (jwsPayload == null) {
jwsPayload = detachedPayload;
} else if (detachedPayload != null) {
LOG.warning("JSON JWS includes a payload expected to be detached");
throw new JwsException(JwsException.Error.INVALID_JSON_JWS);
}
if (jwsPayload == null) {
LOG.warning("JSON JWS has no payload");
throw new JwsException(JwsException.Error.INVALID_JSON_JWS);
}
List<Map<String, Object>> signatureArray = CastUtils.cast((List<?>)jsonObjectMap.get("signatures"));
if (signatureArray != null) {
if (jsonObjectMap.containsKey("signature")) {
LOG.warning("JSON JWS has a flattened 'signature' element and a 'signatures' object");
throw new JwsException(JwsException.Error.INVALID_JSON_JWS);
}
for (Map<String, Object> signatureEntry : signatureArray) {
this.signatures.add(getSignatureObject(signatureEntry));
}
} else {
this.signatures.add(getSignatureObject(jsonObjectMap));
}
if (signatures.isEmpty()) {
LOG.warning("JSON JWS has no signatures");
throw new JwsException(JwsException.Error.INVALID_JSON_JWS);
}
validateB64Status();
}
private Boolean validateB64Status() {
return JwsJsonProducer.validateB64Status(signatures);
}
protected final JwsJsonSignatureEntry getSignatureObject(Map<String, Object> signatureEntry) {
String protectedHeader = (String)signatureEntry.get("protected");
Map<String, Object> header = CastUtils.cast((Map<?, ?>)signatureEntry.get("header"));
String signature = (String)signatureEntry.get("signature");
return
new JwsJsonSignatureEntry(jwsPayload,
protectedHeader,
signature,
header != null ? new JwsHeaders(header) : null);
}
public String getSignedDocument() {
return this.jwsSignedDocument;
}
public String getJwsPayload() {
return this.jwsPayload;
}
public String getDecodedJwsPayload() {
if (validateB64Status()) {
return JoseUtils.decodeToString(jwsPayload);
} else {
return jwsPayload;
}
}
public byte[] getDecodedJwsPayloadBytes() {
return StringUtils.toBytesUTF8(getDecodedJwsPayload());
}
public List<JwsJsonSignatureEntry> getSignatureEntries() {
return Collections.unmodifiableList(signatures);
}
public Map<SignatureAlgorithm, List<JwsJsonSignatureEntry>> getSignatureEntryMap() {
return JwsUtils.getJwsJsonSignatureMap(signatures);
}
public boolean verifySignatureWith(JwsSignatureVerifier validator) {
return verifySignatureWith(validator, null);
}
public boolean verifySignatureWith(JwsSignatureVerifier validator, Map<String, Object> entryProps) {
List<JwsJsonSignatureEntry> theSignatureEntries =
getSignatureEntryMap().get(validator.getAlgorithm());
if (theSignatureEntries != null) {
for (JwsJsonSignatureEntry signatureEntry : theSignatureEntries) {
if (entryProps != null
&& !signatureEntry.getUnionHeader().asMap().entrySet().containsAll(entryProps.entrySet())) {
continue;
}
if (signatureEntry.verifySignatureWith(validator)) {
return true;
}
}
}
return false;
}
public boolean verifySignatureWith(PublicKey key, SignatureAlgorithm algo) {
return verifySignatureWith(key, algo, null);
}
public boolean verifySignatureWith(PublicKey key, SignatureAlgorithm algo, Map<String, Object> entryProps) {
return verifySignatureWith(JwsUtils.getPublicKeySignatureVerifier(key, algo), entryProps);
}
public boolean verifySignatureWith(byte[] key, SignatureAlgorithm algo) {
return verifySignatureWith(key, algo, null);
}
public boolean verifySignatureWith(byte[] key, SignatureAlgorithm algo, Map<String, Object> entryProps) {
return verifySignatureWith(JwsUtils.getHmacSignatureVerifier(key, algo), entryProps);
}
public boolean verifySignatureWith(JsonWebKey key) {
return verifySignatureWith(JwsUtils.getSignatureVerifier(key));
}
public boolean verifySignatureWith(JsonWebKey key, SignatureAlgorithm algo) {
return verifySignatureWith(key, algo, null);
}
public boolean verifySignatureWith(JsonWebKey key, SignatureAlgorithm algo, Map<String, Object> entryProps) {
return verifySignatureWith(JwsUtils.getSignatureVerifier(key, algo), entryProps);
}
public boolean verifySignatureWith(List<JwsSignatureVerifier> validators) {
return verifySignatureWith(validators, null);
}
public boolean verifySignatureWith(List<JwsSignatureVerifier> validators, Map<String, Object> entryProps) {
try {
verifyAndGetNonValidated(validators, entryProps);
} catch (JwsException ex) {
LOG.warning("One of JSON JWS signatures is invalid");
return false;
}
return true;
}
public List<JwsJsonSignatureEntry> verifyAndGetNonValidated(List<JwsSignatureVerifier> validators) {
return verifyAndGetNonValidated(validators, null);
}
public List<JwsJsonSignatureEntry> verifyAndGetNonValidated(List<JwsSignatureVerifier> validators,
Map<String, Object> entryProps) {
List<JwsJsonSignatureEntry> validatedSignatures = new LinkedList<JwsJsonSignatureEntry>();
for (JwsSignatureVerifier validator : validators) {
List<JwsJsonSignatureEntry> theSignatureEntries =
getSignatureEntryMap().get(validator.getAlgorithm());
if (theSignatureEntries != null) {
for (JwsJsonSignatureEntry sigEntry : theSignatureEntries) {
if (entryProps != null
&& !sigEntry.getUnionHeader().asMap().entrySet().containsAll(entryProps.entrySet())) {
continue;
}
if (sigEntry.verifySignatureWith(validator)) {
validatedSignatures.add(sigEntry);
break;
}
}
}
}
if (validatedSignatures.isEmpty()) {
throw new JwsException(JwsException.Error.INVALID_SIGNATURE);
}
List<JwsJsonSignatureEntry> nonValidatedSignatures = new LinkedList<JwsJsonSignatureEntry>();
for (JwsJsonSignatureEntry sigEntry : signatures) {
if (!validatedSignatures.contains(sigEntry)) {
nonValidatedSignatures.add(sigEntry);
}
}
return nonValidatedSignatures;
}
public String verifyAndProduce(List<JwsSignatureVerifier> validators) {
return verifyAndProduce(validators, null);
}
public String verifyAndProduce(List<JwsSignatureVerifier> validators, Map<String, Object> entryProps) {
List<JwsJsonSignatureEntry> nonValidated = verifyAndGetNonValidated(validators, entryProps);
if (!nonValidated.isEmpty()) {
JwsJsonProducer producer = new JwsJsonProducer(getDecodedJwsPayload());
producer.getSignatureEntries().addAll(nonValidated);
return producer.getJwsJsonSignedDocument();
} else {
return null;
}
}
}