mirror of
https://github.com/square/certigo.git
synced 2026-10-19 16:15:40 +00:00
This commit adds tests for the `jceks` package against the installed JDK's `keytool` CLI. The tests ensure that JCEKS files created by `keytool` can be read by the package, and vice versa. These tests are disabled by default and are enabled with the `-jceks.keytool-tests=true` flag. This commit configures GitHub Actions to test the package against a wide variety of JDKs. This commit also restricts GitHub Actions permissions.
307 lines
7.8 KiB
Go
307 lines
7.8 KiB
Go
// Copyright 2025 Block, Inc.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
// 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 jceks
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha1"
|
|
"crypto/x509"
|
|
"errors"
|
|
"flag"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/crypto/sha3"
|
|
)
|
|
|
|
var writeReencoded = flag.Bool("jceks.write-reencoded", false, "write expected re-encoded JCEKS files")
|
|
|
|
type discardErrWriter struct {
|
|
err error
|
|
writeAfterErr int
|
|
}
|
|
|
|
func (dew *discardErrWriter) Write(p []byte) (int, error) {
|
|
if dew.err != nil {
|
|
dew.writeAfterErr++
|
|
return 1, dew.err
|
|
}
|
|
|
|
return len(p), nil
|
|
}
|
|
|
|
func TestCounterWriter(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var dew discardErrWriter
|
|
|
|
cw := &counterWriter{w: &dew}
|
|
|
|
nn, err := cw.Write([]byte("Hello "))
|
|
require.NoError(t, err)
|
|
require.Equal(t, 6, nn)
|
|
|
|
nn, err = cw.Write([]byte("World"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, 5, nn)
|
|
|
|
expectErr := errors.New("expected error")
|
|
dew.err = expectErr
|
|
|
|
nn, err = cw.Write([]byte("discarded"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, 9, nn)
|
|
|
|
nn, err = cw.Write([]byte("more"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, 4, nn)
|
|
|
|
n, err := cw.Results()
|
|
require.Equal(t, expectErr, err)
|
|
require.Equal(t, int64(6+5+1), n)
|
|
require.Equal(t, 1, dew.writeAfterErr)
|
|
}
|
|
|
|
func TestJCEKSDigester(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
jd := newJCEKSDigester([]byte("CHANGE"))
|
|
_, err := jd.Write([]byte("Hello, "))
|
|
require.NoError(t, err)
|
|
_, err = jd.Write([]byte("World!"))
|
|
require.NoError(t, err)
|
|
|
|
var buf bytes.Buffer
|
|
err = jd.WriteDigest(&buf)
|
|
require.NoError(t, err)
|
|
|
|
expected := sha1.Sum([]byte("CHANGEMighty AphroditeHello, World!"))
|
|
|
|
require.Equal(t, expected[:], buf.Bytes())
|
|
}
|
|
|
|
func TestSetIntegrityPassword(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var enc Encoder
|
|
require.False(t, enc.setIntegrityPassword)
|
|
|
|
err := enc.SetIntegrityPassword("")
|
|
require.Error(t, err)
|
|
require.ErrorIs(t, err, ErrInvalidPassword)
|
|
require.False(t, enc.setIntegrityPassword)
|
|
|
|
err = enc.SetIntegrityPassword("123456")
|
|
require.NoError(t, err)
|
|
require.True(t, enc.setIntegrityPassword)
|
|
require.Equal(t, []byte{0, '1', 0, '2', 0, '3', 0, '4', 0, '5', 0, '6'}, enc.integrityPassword)
|
|
}
|
|
|
|
func TestEncodePrivateKeyStore(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rnd := sha3.NewShake128()
|
|
now := time.Date(2025, 4, 24, 0, 0, 0, 0, time.UTC)
|
|
|
|
rsaPriv, err := LoadPEMKey("testdata/private-key.key")
|
|
require.NoError(t, err)
|
|
pkcs1 := x509.MarshalPKCS1PrivateKey(rsaPriv)
|
|
|
|
leafCert, err := LoadPEMCert("testdata/private-key.crt")
|
|
require.NoError(t, err)
|
|
caCert, err := LoadPEMCert("testdata/private-key-ca.crt")
|
|
require.NoError(t, err)
|
|
|
|
var enc Encoder
|
|
|
|
cipher, err := PBEWithMD5AndDES3CBC([]byte("changeit"), rnd, 20)
|
|
require.NoError(t, err)
|
|
err = enc.AddPrivateKeyPKCS1("example.test", now, pkcs1, [][]byte{leafCert.Raw, caCert.Raw}, cipher)
|
|
require.NoError(t, err)
|
|
|
|
err = enc.SetIntegrityPassword("changeit")
|
|
require.NoError(t, err)
|
|
|
|
var buf bytes.Buffer
|
|
n, err := enc.WriteTo(&buf)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(buf.Len()), n)
|
|
|
|
if *writeReencoded {
|
|
err = os.WriteFile("testdata/encoder-private-store.jceks", buf.Bytes(), 0666)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
expected, err := os.ReadFile("testdata/encoder-private-store.jceks")
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, buf.Bytes())
|
|
}
|
|
|
|
func TestEncodeTrustedCertificateStore(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
now := time.Date(2025, 4, 24, 0, 0, 0, 0, time.UTC)
|
|
|
|
cert, err := LoadPEMCert("testdata/trusted-cert.crt")
|
|
require.NoError(t, err)
|
|
|
|
var enc Encoder
|
|
|
|
err = enc.AddTrustedCertificate("ca1", now, cert.Raw)
|
|
require.NoError(t, err)
|
|
|
|
err = enc.SetIntegrityPassword("changeit")
|
|
require.NoError(t, err)
|
|
|
|
var buf bytes.Buffer
|
|
n, err := enc.WriteTo(&buf)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(buf.Len()), n)
|
|
|
|
if *writeReencoded {
|
|
err = os.WriteFile("testdata/encoder-cert-store.jceks", buf.Bytes(), 0666)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
expected, err := os.ReadFile("testdata/encoder-cert-store.jceks")
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, buf.Bytes())
|
|
}
|
|
|
|
func TestEncodeExistingPrivateKeyStore(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ks, err := LoadFromFile("testdata/private-key.jceks", []byte("store-password"))
|
|
require.NoError(t, err)
|
|
|
|
var enc Encoder
|
|
err = enc.AddKeyStore(ks)
|
|
require.NoError(t, err)
|
|
err = enc.SetIntegrityPassword("changeit")
|
|
require.NoError(t, err)
|
|
|
|
var buf bytes.Buffer
|
|
n, err := enc.WriteTo(&buf)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(buf.Len()), n)
|
|
|
|
if *writeReencoded {
|
|
err = os.WriteFile("testdata/encoder-re-encode-private-key.jceks", buf.Bytes(), 0666)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
expected, err := os.ReadFile("testdata/encoder-re-encode-private-key.jceks")
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, buf.Bytes())
|
|
}
|
|
|
|
func TestEncodeExistingTrustedCertificateStore(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ks, err := LoadFromFile("testdata/trusted-cert.jceks", []byte("store-password"))
|
|
require.NoError(t, err)
|
|
|
|
var enc Encoder
|
|
err = enc.AddKeyStore(ks)
|
|
require.NoError(t, err)
|
|
err = enc.SetIntegrityPassword("changeit")
|
|
require.NoError(t, err)
|
|
|
|
var buf bytes.Buffer
|
|
n, err := enc.WriteTo(&buf)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(buf.Len()), n)
|
|
|
|
if *writeReencoded {
|
|
err = os.WriteFile("testdata/encoder-re-encode-trusted-cert.jceks", buf.Bytes(), 0666)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
expected, err := os.ReadFile("testdata/encoder-re-encode-trusted-cert.jceks")
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected, buf.Bytes())
|
|
}
|
|
|
|
func TestEncodeDuplicateAliases(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rnd := sha3.NewShake128()
|
|
now := time.Date(2025, 4, 24, 0, 0, 0, 0, time.UTC)
|
|
|
|
rsaPriv, err := LoadPEMKey("testdata/private-key.key")
|
|
require.NoError(t, err)
|
|
pkcs1 := x509.MarshalPKCS1PrivateKey(rsaPriv)
|
|
|
|
privCert, err := LoadPEMCert("testdata/private-key.crt")
|
|
require.NoError(t, err)
|
|
|
|
otherCert, err := LoadPEMCert("testdata/trusted-cert.crt")
|
|
require.NoError(t, err)
|
|
|
|
var enc Encoder
|
|
|
|
cipher, err := PBEWithMD5AndDES3CBC([]byte("confusion"), rnd, 20)
|
|
require.NoError(t, err)
|
|
|
|
// Write private key entry as "foo" and cert as "bar"
|
|
err = enc.AddPrivateKeyPKCS1("foo", now, pkcs1, [][]byte{privCert.Raw}, cipher)
|
|
require.NoError(t, err)
|
|
err = enc.AddTrustedCertificate("bar", now, otherCert.Raw)
|
|
require.NoError(t, err)
|
|
|
|
// Now write them again with opposite aliases, which should overwrite the previous ones
|
|
err = enc.AddPrivateKeyPKCS1("bar", now, pkcs1, [][]byte{privCert.Raw}, cipher)
|
|
require.NoError(t, err)
|
|
err = enc.AddTrustedCertificate("foo", now, otherCert.Raw)
|
|
require.NoError(t, err)
|
|
|
|
err = enc.SetIntegrityPassword("confusion")
|
|
require.NoError(t, err)
|
|
|
|
var buf bytes.Buffer
|
|
n, err := enc.WriteTo(&buf)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(buf.Len()), n)
|
|
|
|
reloaded, err := LoadFromReader(&buf, []byte("confusion"))
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, []string{"foo"}, reloaded.ListCerts())
|
|
require.Equal(t, []string{"bar"}, reloaded.ListPrivateKeys())
|
|
}
|
|
|
|
func TestProhibitDuplicateAliases(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
now := time.Date(2025, 4, 24, 0, 0, 0, 0, time.UTC)
|
|
|
|
cert, err := LoadPEMCert("testdata/trusted-cert.crt")
|
|
require.NoError(t, err)
|
|
|
|
var enc Encoder
|
|
|
|
enc.SetProhibitDuplicateAliases(true)
|
|
|
|
err = enc.AddTrustedCertificate("alias", now, cert.Raw)
|
|
require.NoError(t, err)
|
|
|
|
err = enc.AddTrustedCertificate("alias", now, cert.Raw)
|
|
require.Error(t, err)
|
|
require.ErrorIs(t, err, ErrDuplicateAlias)
|
|
}
|