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

io.helidon.integrations.oci.tls.certificates.DefaultOciPrivateKeyDownloader Maven / Gradle / Ivy

There is a newer version: 4.1.1
Show newest version
/*
 * Copyright (c) 2023, 2024 Oracle and/or its affiliates.
 *
 * 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 io.helidon.integrations.oci.tls.certificates;

import java.net.URI;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.MGF1ParameterSpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.Objects;

import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import javax.crypto.spec.SecretKeySpec;

import io.helidon.integrations.oci.tls.certificates.spi.OciPrivateKeyDownloader;
import io.helidon.service.registry.Service;

import com.oracle.bmc.auth.AbstractAuthenticationDetailsProvider;
import com.oracle.bmc.keymanagement.KmsCryptoClient;
import com.oracle.bmc.keymanagement.model.ExportKeyDetails;
import com.oracle.bmc.keymanagement.requests.ExportKeyRequest;
import com.oracle.bmc.keymanagement.responses.ExportKeyResponse;

/**
 * Implementation of the {@link OciPrivateKeyDownloader} that will use OCI's KMS to export a key.
 */
@Service.Provider
class DefaultOciPrivateKeyDownloader implements OciPrivateKeyDownloader {
    private final PrivateKey wrappingPrivateKey;
    private final String wrappingPublicKeyPem;
    private final AbstractAuthenticationDetailsProvider authProvider;

    DefaultOciPrivateKeyDownloader(AbstractAuthenticationDetailsProvider authProvider) {
        this.authProvider = authProvider;
        try {
            KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
            generator.initialize(2048);
            KeyPair wrappingKeyPair = generator.generateKeyPair();
            this.wrappingPrivateKey = wrappingKeyPair.getPrivate();
            PublicKey wrappingPublicKey = wrappingKeyPair.getPublic();
            String pubBase64 = Base64.getEncoder().encodeToString(wrappingPublicKey.getEncoded());
            this.wrappingPublicKeyPem = "-----BEGIN PUBLIC KEY-----" + pubBase64 + "-----END PUBLIC KEY-----";
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public PrivateKey loadKey(String keyOcid,
                              URI vaultCryptoEndpoint) {
        Objects.requireNonNull(keyOcid);
        Objects.requireNonNull(vaultCryptoEndpoint);
        try (KmsCryptoClient client = KmsCryptoClient.builder()
                .endpoint(vaultCryptoEndpoint.toString())
                .build(authProvider)) {
            ExportKeyResponse exportKeyResponse =
                    client.exportKey(ExportKeyRequest.builder()
                                             .exportKeyDetails(ExportKeyDetails.builder()
                                                                       .keyId(keyOcid)
                                                                       .publicKey(wrappingPublicKeyPem)
                                                                       .algorithm(ExportKeyDetails.Algorithm.RsaOaepAesSha256)
                                                                       .build())
                                             .build());
            String encryptedKey = exportKeyResponse.getExportedKeyData().getEncryptedKey();
            return decode(encryptedKey);
        } catch (GeneralSecurityException e) {
            throw new IllegalStateException("Problem encountered loading key: " + keyOcid + " from: " + vaultCryptoEndpoint, e);
        }
    }

    PrivateKey decode(String encryptedKey) throws GeneralSecurityException {
        byte[] encryptedMaterial = Base64.getDecoder().decode(encryptedKey);

        // rfc3394 - first 256 bytes is tmp AES key encrypted by our temp wrapping RSA
        byte[] tmpAes = decryptAesKey(Arrays.copyOf(encryptedMaterial, 256));

        // rfc3394 - rest of the bytes is secret key wrapped by tmp AES
        byte[] wrappedSecretKey = Arrays.copyOfRange(encryptedMaterial, 256, encryptedMaterial.length);

        // unwrap with decrypted tmp AES
        return (PrivateKey) unwrapRSA(wrappedSecretKey, tmpAes);
    }

    Key unwrapRSA(byte[] in,
                  byte[] keyBytes) throws GeneralSecurityException {
        SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
        // https://docs.oracle.com/en/java/javase/20/docs/specs/security/standard-names.html
        Cipher c = Cipher.getInstance("AESWrapPad");
        c.init(Cipher.UNWRAP_MODE, key);
        return c.unwrap(in, "RSA", Cipher.PRIVATE_KEY);
    }

    byte[] decryptAesKey(byte[] in) throws GeneralSecurityException {
        Cipher decrypt = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256andMGF1Padding");

        // https://stackoverflow.com/questions/61665435/encryption-in-java-with-rsa-ecb-oaepwithsha-256andmgf1padding-could-not-be-decry
        // decrypt.init(Cipher.DECRYPT_MODE, wrappingPrivateKey);
        decrypt.init(Cipher.DECRYPT_MODE, wrappingPrivateKey,
                     new OAEPParameterSpec("SHA-256", "MGF1",
                                           new MGF1ParameterSpec("SHA-256"),
                                           PSource.PSpecified.DEFAULT));

        return decrypt.doFinal(in);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy