/** * 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.jwe; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.LinkedHashMap; 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.helpers.CastUtils; 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.KeyAlgorithm; public class JweJsonConsumer { protected static final Logger LOG = LogUtils.getL7dLogger(JweJsonConsumer.class); private String protectedHeaderJson; private JweHeaders protectedHeaderJwe; private JweHeaders sharedUnprotectedHeader; private List<JweJsonEncryptionEntry> recipients = new LinkedList<JweJsonEncryptionEntry>(); private Map<JweJsonEncryptionEntry, JweHeaders> recipientsMap = new LinkedHashMap<JweJsonEncryptionEntry, JweHeaders>(); private byte[] aad; private byte[] iv; private byte[] cipherBytes; private byte[] authTag; private JsonMapObjectReaderWriter reader = new JsonMapObjectReaderWriter(); public JweJsonConsumer(String payload) { prepare(payload); } public JweDecryptionOutput decryptWith(JweDecryptionProvider jwe) { return decryptWith(jwe, (Map<String, Object>)null); } public JweDecryptionOutput decryptWith(JweDecryptionProvider jwe, Map<String, Object> recipientProps) { JweJsonEncryptionEntry entry = getJweDecryptionEntry(jwe, recipientProps); return decryptWith(jwe, entry); } public JweDecryptionOutput decryptWith(JweDecryptionProvider jwe, JweJsonEncryptionEntry entry) { JweDecryptionInput jweDecryptionInput = getJweDecryptionInput(jwe, entry); byte[] content = jwe.decrypt(jweDecryptionInput); return new JweDecryptionOutput(jweDecryptionInput.getJweHeaders(), content); } private JweDecryptionInput getJweDecryptionInput(JweDecryptionProvider jwe, JweJsonEncryptionEntry entry) { if (entry == null) { LOG.warning("JWE JSON Entry is not available"); throw new JweException(JweException.Error.INVALID_JSON_JWE); } JweHeaders unionHeaders = recipientsMap.get(entry); if (unionHeaders == null) { LOG.warning("JWE JSON Entry union headers are not available"); throw new JweException(JweException.Error.INVALID_JSON_JWE); } return new JweDecryptionInput(entry.getEncryptedKey(), iv, cipherBytes, authTag, aad, protectedHeaderJson, unionHeaders); } public JweJsonEncryptionEntry getJweDecryptionEntry(JweDecryptionProvider jwe) { return getJweDecryptionEntry(jwe, null); } public JweJsonEncryptionEntry getJweDecryptionEntry(JweDecryptionProvider jwe, Map<String, Object> recipientProps) { for (Map.Entry<JweJsonEncryptionEntry, JweHeaders> entry : recipientsMap.entrySet()) { KeyAlgorithm keyAlgo = entry.getValue().getKeyEncryptionAlgorithm(); if (keyAlgo != null && keyAlgo.equals(jwe.getKeyAlgorithm()) || keyAlgo == null && jwe.getKeyAlgorithm() == null) { if (recipientProps != null && !entry.getValue().asMap().entrySet().containsAll(recipientProps.entrySet())) { continue; } return entry.getKey(); } } return null; } private void prepare(String payload) { Map<String, Object> jsonObjectMap = reader.fromJson(payload); String encodedProtectedHeader = (String)jsonObjectMap.get("protected"); if (encodedProtectedHeader != null) { protectedHeaderJson = JoseUtils.decodeToString(encodedProtectedHeader); protectedHeaderJwe = new JweHeaders(reader.fromJson(protectedHeaderJson)); } Map<String, Object> unprotectedHeader = CastUtils.cast((Map<?, ?>)jsonObjectMap.get("unprotected")); sharedUnprotectedHeader = unprotectedHeader == null ? null : new JweHeaders(unprotectedHeader); List<Map<String, Object>> encryptionArray = CastUtils.cast((List<?>)jsonObjectMap.get("recipients")); if (encryptionArray != null) { if (jsonObjectMap.containsKey("encryption_key")) { LOG.warning("JWE JSON encryption_key is missing"); throw new JweException(JweException.Error.INVALID_JSON_JWE); } for (Map<String, Object> encryptionEntry : encryptionArray) { this.recipients.add(getEncryptionObject(encryptionEntry)); } } else { this.recipients.add(getEncryptionObject(jsonObjectMap)); } aad = getDecodedBytes(jsonObjectMap, "aad"); cipherBytes = getDecodedBytes(jsonObjectMap, "ciphertext"); iv = getDecodedBytes(jsonObjectMap, "iv"); authTag = getDecodedBytes(jsonObjectMap, "tag"); } protected final JweJsonEncryptionEntry getEncryptionObject(Map<String, Object> encryptionEntry) { Map<String, Object> header = CastUtils.cast((Map<?, ?>)encryptionEntry.get("header")); JweHeaders recipientUnprotected = header == null ? null : new JweHeaders(header); String encodedKey = (String)encryptionEntry.get("encrypted_key"); JweJsonEncryptionEntry entry = new JweJsonEncryptionEntry(recipientUnprotected, encodedKey); JweHeaders unionHeaders = new JweHeaders(); if (protectedHeaderJwe != null) { unionHeaders.asMap().putAll(protectedHeaderJwe.asMap()); unionHeaders.setProtectedHeaders(protectedHeaderJwe); } if (sharedUnprotectedHeader != null) { if (!Collections.disjoint(unionHeaders.asMap().keySet(), sharedUnprotectedHeader.asMap().keySet())) { LOG.warning("Protected and unprotected headers have duplicate values"); throw new JweException(JweException.Error.INVALID_JSON_JWE); } unionHeaders.asMap().putAll(sharedUnprotectedHeader.asMap()); } if (recipientUnprotected != null) { if (!Collections.disjoint(unionHeaders.asMap().keySet(), recipientUnprotected.asMap().keySet())) { LOG.warning("Union and recipient unprotected headers have duplicate values"); throw new JweException(JweException.Error.INVALID_JSON_JWE); } unionHeaders.asMap().putAll(recipientUnprotected.asMap()); } recipientsMap.put(entry, unionHeaders); return entry; } protected byte[] getDecodedBytes(Map<String, Object> map, String name) { String value = (String)map.get(name); if (value != null) { return JoseUtils.decode(value); } return null; } public JweHeaders getProtectedHeader() { return protectedHeaderJwe; } public JweHeaders getSharedUnprotectedHeader() { return sharedUnprotectedHeader; } public byte[] getAad() { return aad; } public String getAadText() { if (aad == null) { return null; } return new String(aad, StandardCharsets.UTF_8); } public List<JweJsonEncryptionEntry> getRecipients() { return recipients; } public Map<JweJsonEncryptionEntry, JweHeaders> getRecipientsMap() { return recipientsMap; } }