/** * 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.security.KeyPair; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import org.apache.cxf.common.util.Base64UrlUtility; import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm; import org.apache.cxf.rs.security.jose.jwa.KeyAlgorithm; import org.apache.cxf.rs.security.jose.jwk.JwkUtils; import org.apache.cxf.rt.security.crypto.CryptoUtils; public class EcdhDirectKeyJweEncryption extends JweEncryption { public EcdhDirectKeyJweEncryption(ECPublicKey peerPublicKey, String curve, ContentAlgorithm ctAlgo) { this(peerPublicKey, curve, null, null, ctAlgo); } public EcdhDirectKeyJweEncryption(ECPublicKey peerPublicKey, String curve, String apuString, String apvString, ContentAlgorithm ctAlgo) { super(new EcdhDirectKeyEncryptionAlgorithm(), new EcdhAesGcmContentEncryptionAlgorithm(peerPublicKey, curve, apuString, apvString, ctAlgo)); } protected static class EcdhDirectKeyEncryptionAlgorithm extends DirectKeyEncryptionAlgorithm { protected void checkKeyEncryptionAlgorithm(JweHeaders headers) { headers.setKeyEncryptionAlgorithm(KeyAlgorithm.ECDH_ES_DIRECT); } } protected static class EcdhAesGcmContentEncryptionAlgorithm extends AesGcmContentEncryptionAlgorithm { private EcdhHelper helper; public EcdhAesGcmContentEncryptionAlgorithm(ECPublicKey peerPublicKey, String curve, String apuString, String apvString, ContentAlgorithm ctAlgo) { super(ctAlgo); helper = new EcdhHelper(peerPublicKey, curve, apuString, apvString, ctAlgo.getJwaName()); } public byte[] getContentEncryptionKey(JweHeaders headers) { return helper.getDerivedKey(headers); } } protected static class EcdhHelper { private ECPublicKey peerPublicKey; private String ecurve; private byte[] apuBytes; private byte[] apvBytes; private String ctAlgo; public EcdhHelper(ECPublicKey peerPublicKey, String curve, String apuString, String apvString, String ctAlgo) { this.ctAlgo = ctAlgo; this.peerPublicKey = peerPublicKey; this.ecurve = curve; // JWA spec suggests the "apu" field MAY either be omitted or // represent a random 512-bit value (...) and the "apv" field SHOULD NOT be present." this.apuBytes = toApuBytes(apuString); this.apvBytes = toBytes(apvString); } public byte[] getDerivedKey(JweHeaders headers) { KeyPair pair = CryptoUtils.generateECKeyPair(ecurve); ECPublicKey publicKey = (ECPublicKey)pair.getPublic(); ECPrivateKey privateKey = (ECPrivateKey)pair.getPrivate(); ContentAlgorithm jwtAlgo = ContentAlgorithm.valueOf(ctAlgo); headers.setHeader("apu", Base64UrlUtility.encode(apuBytes)); headers.setHeader("apv", Base64UrlUtility.encode(apvBytes)); headers.setJsonWebKey("epk", JwkUtils.fromECPublicKey(publicKey, ecurve)); return JweUtils.getECDHKey(privateKey, peerPublicKey, apuBytes, apvBytes, jwtAlgo.getJwaName(), jwtAlgo.getKeySizeBits()); } private byte[] toApuBytes(String apuString) { if (apuString != null) { return toBytes(apuString); } else { return CryptoUtils.generateSecureRandomBytes(512 / 8); } } private byte[] toBytes(String str) { return str == null ? null : StringUtils.toBytesUTF8(str); } } }