All Downloads are FREE. Search and download functionalities are using the official Maven repository.

be.atbash.ee.security.octopus.keys.reader.KeyReaderJWK Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017-2022 Rudy De Busscher (https://www.atbash.be)
 *
 * Licensed 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 be.atbash.ee.security.octopus.keys.reader;

import be.atbash.ee.security.octopus.exception.MissingPasswordLookupException;
import be.atbash.ee.security.octopus.exception.ResourceNotFoundException;
import be.atbash.ee.security.octopus.keys.AtbashKey;
import be.atbash.ee.security.octopus.keys.reader.password.KeyResourcePasswordLookup;
import be.atbash.ee.security.octopus.nimbus.jwk.*;
import be.atbash.ee.security.octopus.nimbus.util.JSONObjectUtils;
import be.atbash.ee.security.octopus.util.EncryptionHelper;
import be.atbash.ee.security.octopus.util.JsonbUtil;
import be.atbash.util.exception.AtbashUnexpectedException;
import be.atbash.util.resource.ResourceUtil;

import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.json.bind.Jsonb;
import javax.json.stream.JsonParsingException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Scanner;

public class KeyReaderJWK {

    @SuppressWarnings("S1143")
    public List readResource(String path, KeyResourcePasswordLookup passwordLookup) {
        List result;
        InputStream inputStream = null;
        try {
            ResourceUtil resourceUtil = ResourceUtil.getInstance();
            if (!resourceUtil.resourceExists(path)) {
                throw new ResourceNotFoundException(path);
            }

            inputStream = resourceUtil.getStream(path);
            if (inputStream == null) {
                throw new KeyResourceNotFoundException(path);
            }

            String fileContent = new Scanner(inputStream).useDelimiter("\\Z").next();

            result = parse(fileContent, path, passwordLookup);
        } catch (ParseException | IOException e) {
            throw new AtbashUnexpectedException(e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // Intended and does not hide any other exception or statement executions.
                    throw new AtbashUnexpectedException(e);
                }
            }
        }

        return result;

    }

    /**
     * Parses the JWK directly from a String in JSON or Base64 encoded JSON.
     *
     * @param json           The JWK content, might be encoded with Base64
     * @param passwordLookup Lookup for the password/passphrase in case the JWK is encoded.
     * @return Content of the JWK in AtbashKey format.
     */
    public List parseContent(String json, KeyResourcePasswordLookup passwordLookup) {
        if (!json.startsWith("{")) {
            // The JSON is BASE64 encoded, hopefully.
            byte[] bytes = Base64.getDecoder().decode(json);
            json = new String(bytes, StandardCharsets.UTF_8);
        }
        if (!json.startsWith("{")) {
            throw new AtbashUnexpectedException(new ParseException("The content is not a valid JSON", 0));
        }
        List result;
        try {
            result = parse(json, "inline", passwordLookup);
        } catch (ParseException e) {
            throw new AtbashUnexpectedException(e);
        }
        return result;
    }

    protected List parse(String json, String path, KeyResourcePasswordLookup passwordLookup) throws ParseException {

        Jsonb jsonb = JsonbUtil.getJsonb();
        JsonObject jwkJsonObject;
        try {
            jwkJsonObject = jsonb.fromJson(json, JsonObject.class);
        } catch (JsonParsingException e) {
            // Not a JSON, but as this can be part of 'testing' out which type it is.
            return new ArrayList<>();
        }

        JWK jwk;
        if (jwkJsonObject.get(JWKIdentifiers.ENCRYPTION_ALGORITHM) == null) {
            // not encrypted
            jwk = JWK.parse(json);

        } else {
            if (passwordLookup == null) {
                throw new MissingPasswordLookupException();
            }
            String kid = JSONObjectUtils.getString(jwkJsonObject, JWKIdentifiers.KEY_ID);
            String enc = JSONObjectUtils.getString(jwkJsonObject, "enc");
            char[] password = passwordLookup.getKeyPassword(path, kid);
            String decoded = EncryptionHelper.decode(enc, password);

            JsonObject decodedJSON = jsonb.fromJson(decoded, JsonObject.class);

            JsonObjectBuilder merged = Json.createObjectBuilder();
            decodedJSON.forEach(merged::add);
            jwkJsonObject.forEach(merged::add);

            jwk = JWK.parse(merged.build().toString());
        }
        return processJWK(jwk);
    }

    private List processJWK(JWK jwk) {
        List result = new ArrayList<>();
        if (jwk instanceof AsymmetricJWK) {

            PrivateKey privateKey;
            if (jwk instanceof ECKey) {
                privateKey = ((ECKey) jwk).toECPrivateKey();
            } else {
                privateKey = ((AsymmetricJWK) jwk).toPrivateKey();
            }
            if (privateKey != null) {
                result.add(new AtbashKey(jwk.getKeyID(), privateKey));
            }
            PublicKey publicKey = ((AsymmetricJWK) jwk).toPublicKey();
            if (publicKey != null) {
                result.add(new AtbashKey(jwk.getKeyID(), publicKey));
            }
        }
        if (jwk instanceof OctetSequenceKey) {
            OctetSequenceKey octKey = (OctetSequenceKey) jwk;
            result.add(new AtbashKey(jwk.getKeyID(), octKey.toSecretKey()));
        }
        if (result.isEmpty() && jwk instanceof RSAKey) {
            // Support Payara because it has an old version of Nimbus which uses AssymmetricJWK
            PrivateKey privateKey = ((RSAKey) jwk).toPrivateKey();
            if (privateKey != null) {
                result.add(new AtbashKey(jwk.getKeyID(), privateKey));
            }
            PublicKey publicKey = ((RSAKey) jwk).toPublicKey();
            if (publicKey != null) {
                result.add(new AtbashKey(jwk.getKeyID(), publicKey));
            }

        }
        return result;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy