Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ For information on the classes and types, see the [REFERENCE.md](https://github.

The java_ks module uses the `keytool` and `openssl` commands. It should work on all systems with these commands.

On a host running in FIPS mode, the module creates the temporary PKCS12 file it hands to `keytool` with PBES2 and AES-256 and without an integrity MAC, because the PKCS12 defaults in OpenSSL 1.x (RC2 and 3DES) and the key derivation for the PKCS12 MAC are not FIPS approved. Without this, importing a private key fails with `encrypt error` (OpenSSL 1.1.1) or `mac generation error` (OpenSSL 3 whose FIPS provider has no PKCS12 KDF, such as the one bundled with the FIPS builds of the OpenVox agent). `keytool` reads PBES2 since Java 8u301 and 11.0.12. Outside FIPS mode the file is created as before.

Java 7 is supported as of 1.0.0.

Developed against IBM Java 6 on AIX. Other versions may be unsupported.
Expand Down
22 changes: 21 additions & 1 deletion lib/puppet/provider/java_ks/keytool.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,30 @@ def to_pkcs12(path)
chain_certs = get_chain(certificate)
x509_cert = chain_certs.shift
end
pkcs12 = OpenSSL::PKCS12.create(password, @resource[:name], pkey, x509_cert, chain_certs)
pkcs12 = pkcs12_create(password, @resource[:name], pkey, x509_cert, chain_certs)
File.open(path, 'wb') { |f| f.print pkcs12.to_der }
end

# OpenSSL's PKCS12 defaults do not work on a host running in FIPS mode.
# OpenSSL 1.x encrypts the bags with RC2-40 and 3DES, which FIPS mode refuses,
# and the PKCS12 integrity MAC is keyed with the PKCS12 KDF, which the OpenSSL 3
# FIPS provider does not implement. In FIPS mode, ask for PBES2 with AES-256
# explicitly (the OpenSSL 3 default, and readable by keytool since Java 8u301
# and 11.0.12) and skip the MAC; the file only exists long enough for keytool
# to read it once, so the MAC adds nothing. Outside FIPS mode the call is
# unchanged, so the file keeps whatever format the host's OpenSSL and Java
# already agreed on.
def pkcs12_create(pass, name, pkey, cert, chain)
return OpenSSL::PKCS12.create(pass, name, pkey, cert, chain) unless fips_mode?

Puppet.debug("java_ks: OpenSSL is in FIPS mode, creating the PKCS12 for #{name} with AES-256 and without a MAC")
OpenSSL::PKCS12.create(pass, name, pkey, cert, chain, 'AES-256-CBC', 'AES-256-CBC', nil, -1)
end

def fips_mode?
OpenSSL.respond_to?(:fips_mode) && OpenSSL.fips_mode
end

# Keytool can only import a jceks keystore if the format is der. Generating and
# importing a keystore is used to add private_key and certificate pairs.
def to_der(path)
Expand Down
14 changes: 14 additions & 0 deletions spec/unit/puppet/provider/java_ks/keytool_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,20 @@
end
end

describe '#pkcs12_create' do
it 'keeps the default algorithms outside FIPS mode' do
allow(provider).to receive(:fips_mode?).and_return(false)
expect(OpenSSL::PKCS12).to receive(:create).with('puppet', 'app', 'priv_obj', 'cert_obj', [])
provider.pkcs12_create('puppet', 'app', 'priv_obj', 'cert_obj', [])
end

it 'uses AES-256 and skips the MAC in FIPS mode' do
allow(provider).to receive(:fips_mode?).and_return(true)
expect(OpenSSL::PKCS12).to receive(:create).with('puppet', 'app', 'priv_obj', 'cert_obj', [], 'AES-256-CBC', 'AES-256-CBC', nil, -1)
provider.pkcs12_create('puppet', 'app', 'priv_obj', 'cert_obj', [])
end
end

describe '#import_ks' do
it 'executes openssl and keytool with specific options' do
expect(provider).to receive(:to_pkcs12).with("#{temp_dir}testing.stuff")
Expand Down
Loading