├── .dockerignore ├── .github ├── ci-k8s-configs │ ├── java-spiffe-helper.yaml │ └── spire-values.yaml ├── dependabot.yml └── workflows │ ├── build.yml │ ├── coverage.yml │ ├── java-spiffe-helper-ci.yaml │ ├── release_build.yml │ └── scripts │ └── integration-tests.sh ├── .gitignore ├── CHANGELOG.md ├── CODEOWNERS ├── Dockerfile ├── LICENSE ├── README.md ├── build.gradle ├── conf └── java-spiffe-helper.properties ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── java-spiffe-core ├── README.md ├── build.gradle ├── grpc-netty-linux │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── io │ │ └── spiffe │ │ └── workloadapi │ │ └── internal │ │ ├── GrpcManagedChannelFactory.java │ │ └── ManagedChannelWrapper.java ├── grpc-netty-macos-aarch64 │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── io │ │ └── spiffe │ │ └── workloadapi │ │ └── internal │ │ ├── GrpcManagedChannelFactory.java │ │ └── ManagedChannelWrapper.java ├── grpc-netty-macos │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── io │ │ └── spiffe │ │ └── workloadapi │ │ └── internal │ │ ├── GrpcManagedChannelFactory.java │ │ └── ManagedChannelWrapper.java └── src │ ├── integrationTest │ └── java │ │ └── io │ │ └── spiffe │ │ └── workloadapi │ │ └── WorkloadApiIntegrationTest.java │ ├── main │ ├── java │ │ └── io │ │ │ └── spiffe │ │ │ ├── bundle │ │ │ ├── BundleSource.java │ │ │ ├── jwtbundle │ │ │ │ ├── JwtBundle.java │ │ │ │ └── JwtBundleSet.java │ │ │ └── x509bundle │ │ │ │ ├── X509Bundle.java │ │ │ │ └── X509BundleSet.java │ │ │ ├── exception │ │ │ ├── AuthorityNotFoundException.java │ │ │ ├── BundleNotFoundException.java │ │ │ ├── InvalidSpiffeIdException.java │ │ │ ├── JwtBundleException.java │ │ │ ├── JwtSourceException.java │ │ │ ├── JwtSvidException.java │ │ │ ├── SocketEndpointAddressException.java │ │ │ ├── WatcherException.java │ │ │ ├── X509BundleException.java │ │ │ ├── X509ContextException.java │ │ │ ├── X509SourceException.java │ │ │ └── X509SvidException.java │ │ │ ├── internal │ │ │ ├── AsymmetricKeyAlgorithm.java │ │ │ ├── CertificateUtils.java │ │ │ ├── JwtSignatureAlgorithm.java │ │ │ ├── KeyFileFormat.java │ │ │ └── KeyUsage.java │ │ │ ├── spiffeid │ │ │ ├── SpiffeId.java │ │ │ ├── SpiffeIdUtils.java │ │ │ └── TrustDomain.java │ │ │ ├── svid │ │ │ ├── jwtsvid │ │ │ │ ├── JwtSvid.java │ │ │ │ └── JwtSvidSource.java │ │ │ └── x509svid │ │ │ │ ├── X509Svid.java │ │ │ │ ├── X509SvidSource.java │ │ │ │ └── X509SvidValidator.java │ │ │ └── workloadapi │ │ │ ├── Address.java │ │ │ ├── AddressScheme.java │ │ │ ├── CachedJwtSource.java │ │ │ ├── DefaultJwtSource.java │ │ │ ├── DefaultWorkloadApiClient.java │ │ │ ├── DefaultX509Source.java │ │ │ ├── GrpcConversionUtils.java │ │ │ ├── JwtSource.java │ │ │ ├── JwtSourceOptions.java │ │ │ ├── StreamObservers.java │ │ │ ├── Watcher.java │ │ │ ├── WorkloadApiClient.java │ │ │ ├── X509Context.java │ │ │ ├── X509Source.java │ │ │ ├── internal │ │ │ ├── SecurityHeaderInterceptor.java │ │ │ └── ThreadUtils.java │ │ │ └── retry │ │ │ ├── ExponentialBackoffPolicy.java │ │ │ └── RetryHandler.java │ └── proto │ │ └── workload.proto │ ├── test │ ├── java │ │ └── io │ │ │ └── spiffe │ │ │ ├── bundle │ │ │ ├── jwtbundle │ │ │ │ ├── JwtBundleSetTest.java │ │ │ │ └── JwtBundleTest.java │ │ │ └── x509bundle │ │ │ │ ├── X509BundleSetTest.java │ │ │ │ └── X509BundleTest.java │ │ │ ├── internal │ │ │ ├── AsymmetricKeyAlgorithmTest.java │ │ │ ├── CertificateUtilsTest.java │ │ │ ├── DummyPublicKey.java │ │ │ └── DummyX509Certificate.java │ │ │ ├── spiffeid │ │ │ ├── SpiffeIdTest.java │ │ │ ├── SpiffeIdUtilsTest.java │ │ │ └── TrustDomainTest.java │ │ │ ├── svid │ │ │ ├── jwtsvid │ │ │ │ ├── JwtSignatureAlgorithmTest.java │ │ │ │ ├── JwtSvidParseAndValidateTest.java │ │ │ │ └── JwtSvidParseInsecureTest.java │ │ │ └── x509svid │ │ │ │ ├── X509SvidTest.java │ │ │ │ └── X509SvidValidatorTest.java │ │ │ └── workloadapi │ │ │ ├── AddressTest.java │ │ │ ├── CachedJwtSourceTest.java │ │ │ ├── DefaultJwtSourceTest.java │ │ │ ├── DefaultWorkloadApiClientCorruptedResponsesTest.java │ │ │ ├── DefaultWorkloadApiClientEmptyResponseTest.java │ │ │ ├── DefaultWorkloadApiClientInvalidArgumentTest.java │ │ │ ├── DefaultWorkloadApiClientMismatchSpiffeIdTest.java │ │ │ ├── DefaultWorkloadApiClientRetryableErrorTest.java │ │ │ ├── DefaultWorkloadApiClientTest.java │ │ │ ├── DefaultX509SourceTest.java │ │ │ ├── FakeWorkloadApi.java │ │ │ ├── FakeWorkloadApiCorruptedResponses.java │ │ │ ├── FakeWorkloadApiEmptyResponse.java │ │ │ ├── FakeWorkloadApiExceptions.java │ │ │ ├── FakeWorkloadApiMismatchSpiffeId.java │ │ │ ├── GrpcConversionUtilsTest.java │ │ │ ├── WorkloadApiClientErrorStub.java │ │ │ ├── WorkloadApiClientStub.java │ │ │ ├── WorkloadApiClientTestUtil.java │ │ │ ├── X509ContextTest.java │ │ │ └── retry │ │ │ ├── ExponentialBackoffPolicyTest.java │ │ │ └── RetryHandlerTest.java │ └── resources │ │ └── testdata │ │ ├── internal │ │ ├── bundle.pem │ │ ├── cert.pem │ │ ├── cert2.pem │ │ └── privateKeyRsa.pem │ │ ├── jwtbundle │ │ ├── jwks_invalid_keytype.json │ │ ├── jwks_missing_kid.json │ │ ├── jwks_valid_EC_1.json │ │ ├── jwks_valid_RSA_1.json │ │ └── jwks_valid_RSA_EC.json │ │ ├── spiffeid │ │ └── spiffeIds.txt │ │ ├── workloadapi │ │ ├── bundle.der │ │ ├── bundle.json │ │ ├── corrupted │ │ ├── federated-bundle.pem │ │ ├── svid.der │ │ └── svid.key.der │ │ ├── x509bundle │ │ ├── cert.pem │ │ ├── certs.pem │ │ ├── corrupted.pem │ │ ├── empty.pem │ │ ├── key.pem │ │ └── not-pem.pem │ │ └── x509svid │ │ ├── cert.der │ │ ├── corrupted │ │ ├── good-leaf-and-intermediate.pem │ │ ├── good-leaf-only.pem │ │ ├── key-ecdsa-other.pem │ │ ├── key-pkcs8-ecdsa.pem │ │ ├── key-pkcs8-rsa.pem │ │ ├── key-rsa-other.pem │ │ ├── keyEC.der │ │ ├── wrong-intermediate-no-ca.pem │ │ ├── wrong-intermediate-no-key-cert-sign.pem │ │ ├── wrong-leaf-ca-true.pem │ │ ├── wrong-leaf-cert-sign.pem │ │ ├── wrong-leaf-crl-sign.pem │ │ ├── wrong-leaf-empty-id.pem │ │ └── wrong-leaf-no-digital-signature.pem │ └── testFixtures │ └── java │ └── io │ └── spiffe │ └── utils │ ├── CertAndKeyPair.java │ ├── TestUtils.java │ └── X509CertificateTestUtils.java ├── java-spiffe-helper ├── README.md ├── build.gradle ├── gradle.properties └── src │ ├── main │ └── java │ │ └── io │ │ └── spiffe │ │ └── helper │ │ ├── cli │ │ ├── Config.java │ │ └── Runner.java │ │ ├── exception │ │ ├── KeyStoreHelperException.java │ │ └── RunnerException.java │ │ └── keystore │ │ ├── AuthorityEntry.java │ │ ├── KeyStore.java │ │ ├── KeyStoreHelper.java │ │ ├── KeyStoreType.java │ │ └── PrivateKeyEntry.java │ └── test │ ├── java │ └── io │ │ └── spiffe │ │ └── helper │ │ ├── cli │ │ ├── ConfigTest.java │ │ └── RunnerTest.java │ │ └── keystore │ │ ├── KeyStoreHelperTest.java │ │ ├── KeyStoreTest.java │ │ ├── KeyStoreTypeTest.java │ │ ├── WorkloadApiClientErrorStub.java │ │ └── WorkloadApiClientStub.java │ └── resources │ └── testdata │ ├── bundle.der │ ├── bundle.pem │ ├── cli │ ├── correct.conf │ ├── missing-keypass.conf │ ├── missing-keystorepass.conf │ ├── missing-keystorepath.conf │ ├── missing-truststorepass.conf │ └── missing-truststorepath.conf │ ├── svid.der │ ├── svid.key │ ├── svid.key.der │ └── svid.pem ├── java-spiffe-provider ├── README.md ├── build.gradle └── src │ ├── main │ └── java │ │ └── io │ │ └── spiffe │ │ └── provider │ │ ├── AllowedIdSupplierSpiffeIdVerifier.java │ │ ├── EnvironmentUtils.java │ │ ├── SpiffeIdVerifier.java │ │ ├── SpiffeKeyManager.java │ │ ├── SpiffeKeyManagerFactory.java │ │ ├── SpiffeKeyStore.java │ │ ├── SpiffeProvider.java │ │ ├── SpiffeProviderConstants.java │ │ ├── SpiffeSslContextFactory.java │ │ ├── SpiffeSslSocketFactory.java │ │ ├── SpiffeTrustManager.java │ │ ├── SpiffeTrustManagerFactory.java │ │ ├── SpiffeVerificationException.java │ │ ├── X509SourceManager.java │ │ └── exception │ │ └── SpiffeProviderException.java │ └── test │ ├── java │ └── io │ │ └── spiffe │ │ └── provider │ │ ├── EnvironmentUtilsTest.java │ │ ├── SpiffeKeyManagerFactoryTest.java │ │ ├── SpiffeKeyManagerTest.java │ │ ├── SpiffeKeyStoreTest.java │ │ ├── SpiffeProviderTest.java │ │ ├── SpiffeSslContextFactoryTest.java │ │ ├── SpiffeSslSocketFactoryTest.java │ │ ├── SpiffeTrustManagerFactoryTest.java │ │ ├── SpiffeTrustManagerTest.java │ │ ├── X509SourceManagerTest.java │ │ ├── X509SourceStub.java │ │ └── examples │ │ └── mtls │ │ ├── HttpsClient.java │ │ ├── HttpsServer.java │ │ └── WorkloadThread.java │ └── resources │ └── testdata │ ├── bundle.pem │ ├── cert.pem │ ├── cert2.pem │ ├── key.pem │ ├── key2.pem │ └── spiffeIds.txt ├── lombok.config └── settings.gradle /.dockerignore: -------------------------------------------------------------------------------- 1 | ### Java template 2 | # Compiled class file 3 | *.class 4 | 5 | # Log file 6 | *.log 7 | 8 | # BlueJ files 9 | *.ctxt 10 | 11 | # Mobile Tools for Java (J2ME) 12 | .mtj.tmp/ 13 | 14 | # Package Files # 15 | *.jar 16 | *.war 17 | *.nar 18 | *.ear 19 | *.zip 20 | *.tar.gz 21 | *.rar 22 | 23 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 24 | hs_err_pid* 25 | replay_pid* 26 | 27 | ### JetBrains template 28 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 29 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 30 | 31 | # User-specific stuff 32 | .idea/**/workspace.xml 33 | .idea/**/tasks.xml 34 | .idea/**/usage.statistics.xml 35 | .idea/**/dictionaries 36 | .idea/**/shelf 37 | 38 | # AWS User-specific 39 | .idea/**/aws.xml 40 | 41 | # Generated files 42 | .idea/**/contentModel.xml 43 | 44 | # Sensitive or high-churn files 45 | .idea/**/dataSources/ 46 | .idea/**/dataSources.ids 47 | .idea/**/dataSources.local.xml 48 | .idea/**/sqlDataSources.xml 49 | .idea/**/dynamic.xml 50 | .idea/**/uiDesigner.xml 51 | .idea/**/dbnavigator.xml 52 | 53 | # Gradle 54 | .idea/**/gradle.xml 55 | .idea/**/libraries 56 | 57 | # Gradle and Maven with auto-import 58 | # When using Gradle or Maven with auto-import, you should exclude module files, 59 | # since they will be recreated, and may cause churn. Uncomment if using 60 | # auto-import. 61 | # .idea/artifacts 62 | # .idea/compiler.xml 63 | # .idea/jarRepositories.xml 64 | # .idea/modules.xml 65 | # .idea/*.iml 66 | # .idea/modules 67 | # *.iml 68 | # *.ipr 69 | 70 | # CMake 71 | cmake-build-*/ 72 | 73 | # Mongo Explorer plugin 74 | .idea/**/mongoSettings.xml 75 | 76 | # File-based project format 77 | *.iws 78 | 79 | # IntelliJ 80 | out/ 81 | 82 | # mpeltonen/sbt-idea plugin 83 | .idea_modules/ 84 | 85 | # JIRA plugin 86 | atlassian-ide-plugin.xml 87 | 88 | # Cursive Clojure plugin 89 | .idea/replstate.xml 90 | 91 | # SonarLint plugin 92 | .idea/sonarlint/ 93 | 94 | # Crashlytics plugin (for Android Studio and IntelliJ) 95 | com_crashlytics_export_strings.xml 96 | crashlytics.properties 97 | crashlytics-build.properties 98 | fabric.properties 99 | 100 | # Editor-based Rest Client 101 | .idea/httpRequests 102 | 103 | # Android studio 3.1+ serialized cache file 104 | .idea/caches/build_file_checksums.ser 105 | 106 | # GitHub 107 | .github 108 | 109 | # Git 110 | .git 111 | -------------------------------------------------------------------------------- /.github/ci-k8s-configs/java-spiffe-helper.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: java-spiffe-helper 6 | data: 7 | java-spiffe-helper.properties: | 8 | keyStorePath=/tmp/keystore.p12 9 | keyStorePass=password 10 | keyPass=password 11 | trustStorePath=/tmp/truststore.p12 12 | trustStorePass=password 13 | keyStoreType=pkcs12 14 | keyAlias=spiffe 15 | spiffeSocketPath=unix:/run/spire/agent-sockets/spire-agent.sock 16 | --- 17 | apiVersion: v1 18 | kind: Pod 19 | metadata: 20 | name: java-spiffe-helper 21 | labels: 22 | app: java-spiffe-helper 23 | spec: 24 | containers: 25 | - name: java-spiffe-helper 26 | image: java-spiffe-helper:test 27 | imagePullPolicy: IfNotPresent 28 | readinessProbe: 29 | initialDelaySeconds: 15 30 | exec: 31 | command: 32 | - ls 33 | - /tmp/truststore.p12 34 | volumeMounts: 35 | - name: properties 36 | mountPath: /app/java-spiffe-helper.properties 37 | subPath: java-spiffe-helper.properties 38 | - name: spire-sockets 39 | mountPath: /run/spire/agent-sockets 40 | readOnly: true 41 | restartPolicy: Never 42 | volumes: 43 | - name: properties 44 | configMap: 45 | name: java-spiffe-helper 46 | - name: spire-sockets 47 | hostPath: 48 | path: /run/spire/agent-sockets 49 | type: DirectoryOrCreate 50 | -------------------------------------------------------------------------------- /.github/ci-k8s-configs/spire-values.yaml: -------------------------------------------------------------------------------- 1 | spire-server: 2 | ca_subject: 3 | common_name: common_name 4 | controllerManager: 5 | identities: 6 | clusterSPIFFEIDs: 7 | default: 8 | enabled: false 9 | java-spiffe-helper: 10 | spiffeIDTemplate: spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }} 11 | namespaceSelector: 12 | matchLabels: 13 | kubernetes.io/metadata.name: default 14 | podSelector: 15 | matchLabels: 16 | app: java-spiffe-helper 17 | dnsNameTemplates: 18 | - dnsNameTemplate 19 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gradle" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | - package-ecosystem: "github-actions" 13 | directory: "/" 14 | schedule: 15 | interval: "daily" 16 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | 15 | build-and-test-on-linux: 16 | 17 | runs-on: ubuntu-latest 18 | strategy: 19 | matrix: 20 | java-version: [ 8, 11, 17, 21 ] 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | - name: Set up JDK 25 | uses: actions/setup-java@v4 26 | with: 27 | java-version: ${{ matrix.java-version }} 28 | distribution: 'adopt' 29 | - name: Cache Gradle packages 30 | uses: actions/cache@v4 31 | with: 32 | path: | 33 | ~/.gradle/caches 34 | ~/.gradle/wrapper 35 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} 36 | restore-keys: | 37 | ${{ runner.os }}-gradle- 38 | - name: Build with Gradle and generate the artifacts (also run the tests) 39 | run: ./gradlew build 40 | - name: Run integration tests 41 | run: ./.github/workflows/scripts/integration-tests.sh 42 | - name: Cleanup Gradle Cache 43 | # Remove some files from the Gradle cache, so they aren't cached by GitHub Actions. 44 | # Restoring these files from a GitHub Actions cache might cause problems for future builds. 45 | run: | 46 | rm -f ~/.gradle/caches/modules-2/modules-2.lock 47 | rm -f ~/.gradle/caches/modules-2/gc.properties 48 | 49 | build-and-test-on-macos: 50 | 51 | runs-on: macos-latest 52 | strategy: 53 | matrix: 54 | java-version: [ 8, 11, 17, 21 ] 55 | 56 | steps: 57 | - uses: actions/checkout@v4 58 | - name: Set up JDK 59 | uses: actions/setup-java@v4 60 | with: 61 | java-version: ${{ matrix.java-version }} 62 | distribution: 'zulu' 63 | - name: Cache Gradle packages 64 | uses: actions/cache@v4 65 | with: 66 | path: | 67 | ~/.gradle/caches 68 | ~/.gradle/wrapper 69 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} 70 | restore-keys: | 71 | ${{ runner.os }}-gradle- 72 | - name: Build with Gradle and generate the artifacts 73 | run: ./gradlew build 74 | - name: Cleanup Gradle Cache 75 | # Remove some files from the Gradle cache, so they aren't cached by GitHub Actions. 76 | # Restoring these files from a GitHub Actions cache might cause problems for future builds. 77 | run: | 78 | rm -f ~/.gradle/caches/modules-2/modules-2.lock 79 | rm -f ~/.gradle/caches/modules-2/gc.properties 80 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: coverage 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | jobs: 8 | create-coverage: 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Set up JDK 15 | uses: actions/setup-java@v4 16 | with: 17 | java-version: '16' 18 | distribution: 'adopt' 19 | - name: Generate and upload coverage report 20 | env: 21 | COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} 22 | run: ./gradlew jacocoTestReport coveralls 23 | - name: Cleanup Gradle Cache 24 | # Remove some files from the Gradle cache, so they aren't cached by GitHub Actions. 25 | # Restoring these files from a GitHub Actions cache might cause problems for future builds. 26 | run: | 27 | rm -f ~/.gradle/caches/modules-2/modules-2.lock 28 | rm -f ~/.gradle/caches/modules-2/gc.properties 29 | -------------------------------------------------------------------------------- /.github/workflows/release_build.yml: -------------------------------------------------------------------------------- 1 | name: Release Build 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v[0-9]+.[0-9]+.[0-9]+' 7 | 8 | env: 9 | REGISTRY: ghcr.io 10 | JAVA_VERSION: '17' 11 | 12 | jobs: 13 | publishToMaven: 14 | runs-on: ubuntu-latest 15 | env: 16 | NEXUS_USERNAME: ${{ secrets.NEXUS_USERNAME }} 17 | NEXUS_TOKEN: ${{ secrets.NEXUS_TOKEN }} 18 | PGP_PRIVATE_KEY: ${{ secrets.PGP_PRIVATE_KEY }} 19 | PGP_KEY_PASSPHRASE: ${{ secrets.PGP_KEY_PASSPHRASE }} 20 | steps: 21 | - name: Checkout code 22 | uses: actions/checkout@v4 23 | - name: Set up JDK 24 | uses: actions/setup-java@v4 25 | with: 26 | java-version: ${{ env.JAVA_VERSION }} 27 | distribution: 'adopt' 28 | - name: Publish to Nexus Maven Repository 29 | run: ./gradlew publish 30 | 31 | publishDockerImage: 32 | needs: publishToMaven 33 | runs-on: ubuntu-latest 34 | permissions: 35 | contents: read 36 | packages: write 37 | steps: 38 | - uses: actions/checkout@v4 39 | - name: Login to GitHub Container Registry 40 | uses: docker/login-action@v3 41 | with: 42 | registry: ${{ env.REGISTRY }} 43 | username: ${{ github.actor }} 44 | password: ${{ secrets.GITHUB_TOKEN }} 45 | - name: Set up QEMU 46 | uses: docker/setup-qemu-action@v3 47 | - name: Set up Docker Buildx 48 | uses: docker/setup-buildx-action@v3 49 | - name: Determine Docker Tag 50 | run: echo "DOCKER_TAG=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV 51 | - name: Publish java-spiffe-helper Docker Image 52 | uses: docker/build-push-action@v6 53 | with: 54 | context: . 55 | platforms: linux/amd64,linux/arm64 56 | push: true 57 | tags: ${{ env.REGISTRY }}/${{ github.repository }}-helper:${{ env.DOCKER_TAG }} 58 | cache-from: type=gha 59 | cache-to: type=gha,mode=max 60 | -------------------------------------------------------------------------------- /.github/workflows/scripts/integration-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Start a SPIRE Server and Agent and run the integration tests 4 | # Only works on Linux. 5 | 6 | set -euf -o pipefail 7 | 8 | export SPIFFE_ENDPOINT_SOCKET="unix:/tmp/spire-agent/public/api.sock" 9 | 10 | spire_version="1.11.0" 11 | spire_folder="spire-${spire_version}" 12 | spire_server_log_file="/tmp/spire-server/server.log" 13 | spire_agent_log_file="/tmp/spire-agent/agent.log" 14 | 15 | function cleanup() { 16 | killall -9 spire-agent || true 17 | killall -9 spire-server || true 18 | rm -f /tmp/spire-server/private/api.sock 19 | rm -f /tmp/spire-agent/public/api.sock 20 | rm -rf ${spire_folder} 21 | } 22 | 23 | # Some cleanup: kill spire processes that could have remained from previous run 24 | trap cleanup EXIT 25 | 26 | # Install and run a SPIRE server 27 | curl -s -N -L https://github.com/spiffe/spire/releases/download/v${spire_version}/spire-${spire_version}-linux-amd64-musl.tar.gz | tar xz 28 | pushd "${spire_folder}" 29 | mkdir -p /tmp/spire-server 30 | bin/spire-server run -config conf/server/server.conf > "${spire_server_log_file}" 2>&1 & 31 | 32 | spire_server_started=0 33 | for i in {1..10} 34 | do 35 | if bin/spire-server healthcheck >/dev/null 2>&1; then 36 | spire_server_started=1 37 | break 38 | fi 39 | sleep 1 40 | done 41 | 42 | if [ ${spire_server_started} -ne 1 ]; then 43 | cat ${spire_server_log_file} >&2 44 | echo 'SPIRE Server failed to start' >&2 45 | exit 1 46 | fi 47 | 48 | # Generate token and run Spire Agent 49 | agent_id="spiffe://example.org/myagent" 50 | bin/spire-server token generate -spiffeID ${agent_id} > token 51 | cut -d ' ' -f 2 token > token_stripped 52 | mkdir -p /tmp/spire-agent 53 | bin/spire-agent run -config conf/agent/agent.conf -joinToken "$(< token_stripped)" > "${spire_agent_log_file}" 2>&1 & 54 | 55 | spire_agent_started=0 56 | for i in {1..10} 57 | do 58 | if bin/spire-agent healthcheck >/dev/null 2>&1; then 59 | spire_agent_started=1 60 | break 61 | fi 62 | sleep 1 63 | done 64 | 65 | if [ ${spire_agent_started} -ne 1 ]; then 66 | cat ${spire_agent_log_file} >&2 67 | echo 'SPIRE Agent failed to start' >&2 68 | exit 1 69 | fi 70 | 71 | # Register the workload through UID with the SPIFFE ID "spiffe://example.org/myservice" 72 | bin/spire-server entry create -parentID ${agent_id} -spiffeID spiffe://example.org/myservice -selector unix:uid:$(id -u) 73 | sleep 10 # this value is derived from the default Agent sync interval 74 | popd 75 | 76 | # Run only the integration tests 77 | ./gradlew integrationTest 78 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea 3 | build 4 | out 5 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @maxlambrecht @rturner3 2 | 3 | ########################################## 4 | # Maintainers 5 | ########################################## 6 | 7 | # Max Lambrecht 8 | # Hewlett-Packard Enterprise 9 | # @maxlambrecht 10 | 11 | # Ryan Turner 12 | # Uber Technologies, Inc. 13 | # @rturner3 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM eclipse-temurin:17-jdk AS builder 2 | WORKDIR /builder 3 | COPY . /builder 4 | 5 | RUN ./gradlew dependencies 6 | RUN ./gradlew java-spiffe-helper:assemble -ParchiveClassifier=docker -Pversion=docker 7 | 8 | FROM eclipse-temurin:17-jre AS runner 9 | USER nobody 10 | 11 | COPY conf/java-spiffe-helper.properties /app/java-spiffe-helper.properties 12 | COPY --from=builder /builder/java-spiffe-helper/build/libs/java-spiffe-helper-docker-docker.jar /app/java-spiffe-helper.jar 13 | 14 | ENTRYPOINT ["java", "-jar", "/app/java-spiffe-helper.jar"] 15 | CMD ["--config", "/app/java-spiffe-helper.properties"] 16 | -------------------------------------------------------------------------------- /conf/java-spiffe-helper.properties: -------------------------------------------------------------------------------- 1 | # Example java-spiffe-helper configuration 2 | 3 | # KeyStore Path 4 | keyStorePath = keystore.p12 5 | 6 | # Password for the KeyStore 7 | keyStorePass = REPLACE_WITH_YOUR_KEYSTORE_PASSWORD 8 | 9 | # Password for the private key within the KeyStore 10 | keyPass = REPLACE_WITH_YOUR_PRIVATE_KEY_PASSWORD 11 | 12 | # Path to the TrustStore file 13 | trustStorePath = truststore.p12 14 | 15 | # TrustStore Password: Password for the TrustStore 16 | trustStorePass = REPLACE_WITH_YOUR_TRUSTSTORE_PASSWORD 17 | 18 | # KeyStore Type: 'pkcs12' (default) or 'jks' 19 | keyStoreType = pkcs12 20 | 21 | # Key Alias: Alias of the key within the KeyStore (Default: `spiffe`) 22 | keyAlias = spiffe 23 | 24 | # SPIFFE Socket Path: Path to the SPIRE Agent's public API socket 25 | spiffeSocketPath = unix:/tmp/spire-agent/public/api.sock 26 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | version=0.8.12 2 | mavenDeployUrl=https://oss.sonatype.org/service/local/staging/deploy/maven2 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spiffe/java-spiffe/c00a4d09b6abb59e7c51bc06ac1400deffc04ee4/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /java-spiffe-core/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | } 5 | 6 | dependencies { 7 | classpath group: 'com.google.protobuf', name: 'protobuf-gradle-plugin', version: '0.9.5' 8 | } 9 | } 10 | 11 | description = "Core functionality to fetch, process and validate X.509 and JWT SVIDs and Bundles from the Workload API." 12 | 13 | apply plugin: 'com.google.protobuf' 14 | apply plugin: 'java-test-fixtures' 15 | 16 | sourceSets { 17 | main { 18 | java { 19 | srcDirs 'build/generated/source/proto/main/grpc' 20 | srcDirs 'build/generated/source/proto/main/java' 21 | } 22 | } 23 | 24 | integrationTest { 25 | java { 26 | compileClasspath += main.output + test.output 27 | runtimeClasspath += main.output + test.output 28 | srcDir file('src/integrationTest/java') 29 | } 30 | resources.srcDir file('src/integrationTest/resources') 31 | } 32 | } 33 | 34 | sourcesJar.duplicatesStrategy = DuplicatesStrategy.INCLUDE 35 | 36 | configurations { 37 | integrationTestImplementation.extendsFrom testImplementation 38 | integrationTestCompile.extendsFrom testCompile 39 | integrationTestCompileOnly.extendsFrom testCompileOnly 40 | integrationTestRuntime.extendsFrom testRuntime 41 | integrationTestRuntimeOnly.extendsFrom testRuntimeOnly 42 | } 43 | 44 | task integrationTest(type: Test) { 45 | useJUnitPlatform() 46 | testClassesDirs = sourceSets.integrationTest.output.classesDirs 47 | classpath = sourceSets.integrationTest.runtimeClasspath 48 | outputs.upToDateWhen { false } 49 | } 50 | 51 | protobuf { 52 | protoc { 53 | artifact = 'com.google.protobuf:protoc:3.25.5' 54 | } 55 | plugins { 56 | grpc { 57 | artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}" 58 | } 59 | } 60 | generateProtoTasks { 61 | all()*.plugins { 62 | grpc {} 63 | } 64 | } 65 | } 66 | 67 | dependencies { 68 | if (osdetector.os.is('osx') ) { 69 | project.ext.osArch = System.getProperty("os.arch") 70 | if ("x86_64" == project.ext.osArch) { 71 | compileOnly(project('grpc-netty-macos')) 72 | testImplementation(project('grpc-netty-macos')) 73 | } else if ("aarch64" == project.ext.osArch) { 74 | compileOnly(project('grpc-netty-macos-aarch64')) 75 | testImplementation(project('grpc-netty-macos-aarch64')) 76 | } else { 77 | throw new GradleException("Architecture not supported: " + project.ext.osArch) 78 | } 79 | } else { 80 | compileOnly(project('grpc-netty-linux')) 81 | testImplementation(project('grpc-netty-linux')) 82 | } 83 | 84 | project.ext.osArch = System.getProperty("os.arch") 85 | 86 | 87 | implementation group: 'io.grpc', name: 'grpc-protobuf', version: "${grpcVersion}" 88 | implementation group: 'io.grpc', name: 'grpc-stub', version: "${grpcVersion}" 89 | testImplementation group: 'io.grpc', name: 'grpc-inprocess', version: "${grpcVersion}" 90 | testImplementation group: 'io.grpc', name: 'grpc-testing', version: "${grpcVersion}" 91 | compileOnly group: 'org.apache.tomcat', name: 'annotations-api', version: '6.0.53' // necessary for Java 9+ 92 | 93 | // library for processing JWT tokens and JOSE JWK bundles 94 | implementation group: 'com.nimbusds', name: 'nimbus-jose-jwt', version: "${nimbusVersion}" 95 | testFixturesImplementation group: 'com.nimbusds', name: 'nimbus-jose-jwt', version: "${nimbusVersion}" 96 | 97 | // using bouncy castle for generating X.509 certs for testing purposes 98 | testFixturesImplementation group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: '1.70' 99 | testFixturesImplementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.17.0' 100 | } 101 | 102 | -------------------------------------------------------------------------------- /java-spiffe-core/grpc-netty-linux/build.gradle: -------------------------------------------------------------------------------- 1 | description = "Java SPIFFE Library GRPC-Netty Linux support module" 2 | 3 | dependencies { 4 | implementation group: 'io.grpc', name: 'grpc-netty-shaded', version: "${grpcVersion}" 5 | } 6 | 7 | jar { 8 | archiveClassifier = "" 9 | } 10 | -------------------------------------------------------------------------------- /java-spiffe-core/grpc-netty-linux/src/main/java/io/spiffe/workloadapi/internal/GrpcManagedChannelFactory.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi.internal; 2 | 3 | import io.grpc.ManagedChannel; 4 | import io.grpc.netty.shaded.io.grpc.netty.NegotiationType; 5 | import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder; 6 | import io.grpc.netty.shaded.io.netty.channel.ChannelOption; 7 | import io.grpc.netty.shaded.io.netty.channel.EventLoopGroup; 8 | import io.grpc.netty.shaded.io.netty.channel.epoll.EpollDomainSocketChannel; 9 | import io.grpc.netty.shaded.io.netty.channel.epoll.EpollEventLoopGroup; 10 | import io.grpc.netty.shaded.io.netty.channel.unix.DomainSocketAddress; 11 | import lombok.NonNull; 12 | import lombok.val; 13 | import org.apache.commons.lang3.SystemUtils; 14 | 15 | import java.net.URI; 16 | import java.util.concurrent.ExecutorService; 17 | 18 | /** 19 | * Factory for creating ManagedChannel instances for Linux OS. 20 | */ 21 | public final class GrpcManagedChannelFactory { 22 | 23 | private static final String UNIX_SCHEME = "unix"; 24 | private static final String TCP_SCHEME = "tcp"; 25 | 26 | private GrpcManagedChannelFactory() { 27 | } 28 | 29 | /** 30 | * Returns a ManagedChannelWrapper that contains a {@link ManagedChannel} to the SPIFFE Socket Endpoint provided. 31 | * 32 | * @param address URI representing the Workload API endpoint. 33 | * @param executorService the executor to configure the event loop group 34 | * @return a instance of a {@link ManagedChannelWrapper} 35 | */ 36 | public static ManagedChannelWrapper newChannel(@NonNull URI address, ExecutorService executorService) { 37 | val scheme = address.getScheme(); 38 | ManagedChannelWrapper result; 39 | switch (scheme) { 40 | case UNIX_SCHEME: 41 | result = createNativeSocketChannel(address, executorService); 42 | break; 43 | case TCP_SCHEME: 44 | result = createTcpChannel(address); 45 | break; 46 | default: 47 | throw new IllegalArgumentException("Address Scheme not supported: "); 48 | } 49 | return result; 50 | } 51 | 52 | // Create a Native Socket Channel pointing to the spiffeSocketPath 53 | private static ManagedChannelWrapper createNativeSocketChannel(@NonNull URI address, ExecutorService executorService) { 54 | NettyChannelBuilder channelBuilder = NettyChannelBuilder. 55 | forAddress(new DomainSocketAddress(address.getPath())); 56 | EventLoopGroup eventLoopGroup = configureNativeSocketChannel(channelBuilder, executorService); 57 | ManagedChannel managedChannel = channelBuilder.usePlaintext().build(); 58 | return new ManagedChannelWrapper(managedChannel, eventLoopGroup); 59 | } 60 | 61 | private static ManagedChannelWrapper createTcpChannel(@NonNull URI address) { 62 | ManagedChannel managedChannel = NettyChannelBuilder.forAddress(address.getHost(), address.getPort()) 63 | .negotiationType(NegotiationType.PLAINTEXT) 64 | .build(); 65 | return new ManagedChannelWrapper(managedChannel); 66 | } 67 | 68 | private static EventLoopGroup configureNativeSocketChannel(@NonNull NettyChannelBuilder channelBuilder, ExecutorService executorService) { 69 | if (SystemUtils.IS_OS_LINUX) { 70 | // nThreads = 0 -> use Netty default 71 | EpollEventLoopGroup epollEventLoopGroup = new EpollEventLoopGroup(0, executorService); 72 | channelBuilder.eventLoopGroup(epollEventLoopGroup) 73 | // avoid warning Unknown channel option 'SO_KEEPALIVE' 74 | .withOption(ChannelOption.SO_KEEPALIVE, null) 75 | .channelType(EpollDomainSocketChannel.class); 76 | return epollEventLoopGroup; 77 | } 78 | 79 | throw new IllegalStateException("Operating System is not supported."); 80 | } 81 | } -------------------------------------------------------------------------------- /java-spiffe-core/grpc-netty-linux/src/main/java/io/spiffe/workloadapi/internal/ManagedChannelWrapper.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi.internal; 2 | 3 | import io.grpc.ManagedChannel; 4 | import io.grpc.netty.shaded.io.netty.channel.EventLoopGroup; 5 | 6 | import java.io.Closeable; 7 | 8 | /** 9 | * Wraps a {@link ManagedChannel} along with the {@link EventLoopGroup} in order to 10 | * have more control and be able to shutdown the channel properly 11 | * calling the shutdownGracefully method on the EventLoopGroup to prevent 12 | * that some threads remain active. 13 | */ 14 | public class ManagedChannelWrapper implements Closeable { 15 | 16 | private final ManagedChannel managedChannel; 17 | private final EventLoopGroup eventLoopGroup; 18 | 19 | /** 20 | * Constructor. 21 | * 22 | * @param managedChannel an instance of {@link ManagedChannel} 23 | * @param eventLoopGroup an instance of {@link EventLoopGroup} 24 | */ 25 | public ManagedChannelWrapper(ManagedChannel managedChannel, EventLoopGroup eventLoopGroup) { 26 | this.managedChannel = managedChannel; 27 | this.eventLoopGroup = eventLoopGroup; 28 | } 29 | 30 | /** 31 | * Constructor. 32 | * 33 | * @param managedChannel a {@link ManagedChannel} 34 | */ 35 | public ManagedChannelWrapper(ManagedChannel managedChannel) { 36 | this.managedChannel = managedChannel; 37 | this.eventLoopGroup = null; 38 | } 39 | 40 | @Override 41 | public void close() { 42 | if (eventLoopGroup != null) { 43 | eventLoopGroup.shutdownGracefully(); 44 | } 45 | managedChannel.shutdown(); 46 | } 47 | 48 | public ManagedChannel getChannel() { 49 | return managedChannel; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /java-spiffe-core/grpc-netty-macos-aarch64/build.gradle: -------------------------------------------------------------------------------- 1 | description = "Java SPIFFE Library GRPC-Netty MacOS module" 2 | 3 | dependencies { 4 | implementation group: 'io.grpc', name: 'grpc-netty', version: "${grpcVersion}" 5 | 6 | // version must match the one in grpc-netty 7 | implementation group: 'io.netty', name: 'netty-transport-native-kqueue', version: "${nettyVersion}", classifier: 'osx-aarch_64' 8 | } 9 | 10 | jar { 11 | archiveClassifier = "" 12 | } 13 | -------------------------------------------------------------------------------- /java-spiffe-core/grpc-netty-macos-aarch64/src/main/java/io/spiffe/workloadapi/internal/GrpcManagedChannelFactory.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi.internal; 2 | 3 | import io.grpc.ManagedChannel; 4 | import io.grpc.netty.NegotiationType; 5 | import io.grpc.netty.NettyChannelBuilder; 6 | import io.netty.channel.ChannelOption; 7 | import io.netty.channel.EventLoopGroup; 8 | import io.netty.channel.kqueue.KQueueDomainSocketChannel; 9 | import io.netty.channel.kqueue.KQueueEventLoopGroup; 10 | import io.netty.channel.unix.DomainSocketAddress; 11 | import lombok.NonNull; 12 | import lombok.val; 13 | import org.apache.commons.lang3.SystemUtils; 14 | 15 | import java.net.URI; 16 | import java.util.concurrent.ExecutorService; 17 | 18 | /** 19 | * Factory for creating ManagedChannel instances for Mac OS. 20 | */ 21 | public final class GrpcManagedChannelFactory { 22 | 23 | private static final String UNIX_SCHEME = "unix"; 24 | private static final String TCP_SCHEME = "tcp"; 25 | 26 | private GrpcManagedChannelFactory() { 27 | } 28 | 29 | /** 30 | * Returns a ManagedChannelWrapper that contains a {@link ManagedChannel} to the SPIFFE Socket Endpoint provided. 31 | * 32 | * @param address URI representing the Workload API endpoint. 33 | * @param executorService the executor to configure the event loop group 34 | * @return a instance of a {@link ManagedChannelWrapper} 35 | */ 36 | public static ManagedChannelWrapper newChannel(@NonNull URI address, ExecutorService executorService) { 37 | val scheme = address.getScheme(); 38 | ManagedChannelWrapper result; 39 | switch (scheme) { 40 | case UNIX_SCHEME: 41 | result = createNativeSocketChannel(address, executorService); 42 | break; 43 | case TCP_SCHEME: 44 | result = createTcpChannel(address); 45 | break; 46 | default: 47 | throw new IllegalArgumentException("Address Scheme not supported: "); 48 | } 49 | return result; 50 | } 51 | 52 | // Create a Native Socket Channel pointing to the spiffeSocketPath 53 | private static ManagedChannelWrapper createNativeSocketChannel(@NonNull URI address, ExecutorService executorService) { 54 | NettyChannelBuilder channelBuilder = NettyChannelBuilder. 55 | forAddress(new DomainSocketAddress(address.getPath())); 56 | EventLoopGroup eventLoopGroup = configureNativeSocketChannel(channelBuilder, executorService); 57 | ManagedChannel managedChannel = channelBuilder.usePlaintext().build(); 58 | return new ManagedChannelWrapper(managedChannel, eventLoopGroup); 59 | } 60 | 61 | private static ManagedChannelWrapper createTcpChannel(@NonNull URI address) { 62 | ManagedChannel managedChannel = NettyChannelBuilder.forAddress(address.getHost(), address.getPort()) 63 | .negotiationType(NegotiationType.PLAINTEXT) 64 | .build(); 65 | return new ManagedChannelWrapper(managedChannel); 66 | } 67 | 68 | private static EventLoopGroup configureNativeSocketChannel(@NonNull NettyChannelBuilder channelBuilder, ExecutorService executorService) { 69 | if (SystemUtils.IS_OS_MAC) { 70 | // nThreads = 0 -> use Netty default 71 | KQueueEventLoopGroup eventLoopGroup = new KQueueEventLoopGroup(0, executorService); 72 | channelBuilder.eventLoopGroup(eventLoopGroup) 73 | // avoid warning Unknown channel option 'SO_KEEPALIVE' 74 | .withOption(ChannelOption.SO_KEEPALIVE, null) 75 | .channelType(KQueueDomainSocketChannel.class); 76 | return eventLoopGroup; 77 | } 78 | 79 | throw new IllegalStateException("Operating System is not supported."); 80 | } 81 | } -------------------------------------------------------------------------------- /java-spiffe-core/grpc-netty-macos-aarch64/src/main/java/io/spiffe/workloadapi/internal/ManagedChannelWrapper.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi.internal; 2 | 3 | import io.grpc.ManagedChannel; 4 | import io.netty.channel.EventLoopGroup; 5 | 6 | import java.io.Closeable; 7 | 8 | /** 9 | * Wraps a {@link ManagedChannel} along with the {@link EventLoopGroup} in order to 10 | * have more control and be able to shutdown the channel properly 11 | * calling the shutdownGracefully method on the EventLoopGroup to prevent 12 | * that some threads remain active. 13 | */ 14 | public class ManagedChannelWrapper implements Closeable { 15 | 16 | private final ManagedChannel managedChannel; 17 | private final EventLoopGroup eventLoopGroup; 18 | 19 | /** 20 | * Constructor. 21 | * 22 | * @param managedChannel an instance of {@link ManagedChannel} 23 | * @param eventLoopGroup an instance of {@link EventLoopGroup} 24 | */ 25 | public ManagedChannelWrapper(ManagedChannel managedChannel, EventLoopGroup eventLoopGroup) { 26 | this.managedChannel = managedChannel; 27 | this.eventLoopGroup = eventLoopGroup; 28 | } 29 | 30 | /** 31 | * Constructor. 32 | * 33 | * @param managedChannel a {@link ManagedChannel} 34 | */ 35 | public ManagedChannelWrapper(ManagedChannel managedChannel) { 36 | this.managedChannel = managedChannel; 37 | this.eventLoopGroup = null; 38 | } 39 | 40 | @Override 41 | public void close() { 42 | if (eventLoopGroup != null) { 43 | eventLoopGroup.shutdownGracefully(); 44 | } 45 | managedChannel.shutdown(); 46 | } 47 | 48 | public ManagedChannel getChannel() { 49 | return managedChannel; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /java-spiffe-core/grpc-netty-macos/build.gradle: -------------------------------------------------------------------------------- 1 | description = "Java SPIFFE Library GRPC-Netty MacOS module" 2 | 3 | dependencies { 4 | implementation group: 'io.grpc', name: 'grpc-netty', version: "${grpcVersion}" 5 | implementation group: 'io.netty', name: 'netty-transport-native-kqueue', version: "${nettyVersion}", classifier: 'osx-x86_64' 6 | } 7 | 8 | jar { 9 | archiveClassifier = "" 10 | } 11 | -------------------------------------------------------------------------------- /java-spiffe-core/grpc-netty-macos/src/main/java/io/spiffe/workloadapi/internal/GrpcManagedChannelFactory.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi.internal; 2 | 3 | import io.grpc.ManagedChannel; 4 | import io.grpc.netty.NegotiationType; 5 | import io.grpc.netty.NettyChannelBuilder; 6 | import io.netty.channel.ChannelOption; 7 | import io.netty.channel.EventLoopGroup; 8 | import io.netty.channel.kqueue.KQueueDomainSocketChannel; 9 | import io.netty.channel.kqueue.KQueueEventLoopGroup; 10 | import io.netty.channel.unix.DomainSocketAddress; 11 | import lombok.NonNull; 12 | import lombok.val; 13 | import org.apache.commons.lang3.SystemUtils; 14 | 15 | import java.net.URI; 16 | import java.util.concurrent.ExecutorService; 17 | 18 | /** 19 | * Factory for creating ManagedChannel instances for Mac OS. 20 | */ 21 | public final class GrpcManagedChannelFactory { 22 | 23 | private static final String UNIX_SCHEME = "unix"; 24 | private static final String TCP_SCHEME = "tcp"; 25 | 26 | private GrpcManagedChannelFactory() { 27 | } 28 | 29 | /** 30 | * Returns a ManagedChannelWrapper that contains a {@link ManagedChannel} to the SPIFFE Socket Endpoint provided. 31 | * 32 | * @param address URI representing the Workload API endpoint. 33 | * @param executorService the executor to configure the event loop group 34 | * @return a instance of a {@link ManagedChannelWrapper} 35 | */ 36 | public static ManagedChannelWrapper newChannel(@NonNull URI address, ExecutorService executorService) { 37 | val scheme = address.getScheme(); 38 | ManagedChannelWrapper result; 39 | switch (scheme) { 40 | case UNIX_SCHEME: 41 | result = createNativeSocketChannel(address, executorService); 42 | break; 43 | case TCP_SCHEME: 44 | result = createTcpChannel(address); 45 | break; 46 | default: 47 | throw new IllegalArgumentException("Address Scheme not supported: "); 48 | } 49 | return result; 50 | } 51 | 52 | // Create a Native Socket Channel pointing to the spiffeSocketPath 53 | private static ManagedChannelWrapper createNativeSocketChannel(@NonNull URI address, ExecutorService executorService) { 54 | NettyChannelBuilder channelBuilder = NettyChannelBuilder. 55 | forAddress(new DomainSocketAddress(address.getPath())); 56 | EventLoopGroup eventLoopGroup = configureNativeSocketChannel(channelBuilder, executorService); 57 | ManagedChannel managedChannel = channelBuilder.usePlaintext().build(); 58 | return new ManagedChannelWrapper(managedChannel, eventLoopGroup); 59 | } 60 | 61 | private static ManagedChannelWrapper createTcpChannel(@NonNull URI address) { 62 | ManagedChannel managedChannel = NettyChannelBuilder.forAddress(address.getHost(), address.getPort()) 63 | .negotiationType(NegotiationType.PLAINTEXT) 64 | .build(); 65 | return new ManagedChannelWrapper(managedChannel); 66 | } 67 | 68 | private static EventLoopGroup configureNativeSocketChannel(@NonNull NettyChannelBuilder channelBuilder, ExecutorService executorService) { 69 | if (SystemUtils.IS_OS_MAC) { 70 | // nThreads = 0 -> use Netty default 71 | KQueueEventLoopGroup eventLoopGroup = new KQueueEventLoopGroup(0, executorService); 72 | channelBuilder.eventLoopGroup(eventLoopGroup) 73 | // avoid warning Unknown channel option 'SO_KEEPALIVE' 74 | .withOption(ChannelOption.SO_KEEPALIVE, null) 75 | .channelType(KQueueDomainSocketChannel.class); 76 | return eventLoopGroup; 77 | } 78 | 79 | throw new IllegalStateException("Operating System is not supported."); 80 | } 81 | } -------------------------------------------------------------------------------- /java-spiffe-core/grpc-netty-macos/src/main/java/io/spiffe/workloadapi/internal/ManagedChannelWrapper.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi.internal; 2 | 3 | import io.grpc.ManagedChannel; 4 | import io.netty.channel.EventLoopGroup; 5 | 6 | import java.io.Closeable; 7 | 8 | /** 9 | * Wraps a {@link ManagedChannel} along with the {@link EventLoopGroup} in order to 10 | * have more control and be able to shutdown the channel properly 11 | * calling the shutdownGracefully method on the EventLoopGroup to prevent 12 | * that some threads remain active. 13 | */ 14 | public class ManagedChannelWrapper implements Closeable { 15 | 16 | private final ManagedChannel managedChannel; 17 | private final EventLoopGroup eventLoopGroup; 18 | 19 | /** 20 | * Constructor. 21 | * 22 | * @param managedChannel an instance of {@link ManagedChannel} 23 | * @param eventLoopGroup an instance of {@link EventLoopGroup} 24 | */ 25 | public ManagedChannelWrapper(ManagedChannel managedChannel, EventLoopGroup eventLoopGroup) { 26 | this.managedChannel = managedChannel; 27 | this.eventLoopGroup = eventLoopGroup; 28 | } 29 | 30 | /** 31 | * Constructor. 32 | * 33 | * @param managedChannel a {@link ManagedChannel} 34 | */ 35 | public ManagedChannelWrapper(ManagedChannel managedChannel) { 36 | this.managedChannel = managedChannel; 37 | this.eventLoopGroup = null; 38 | } 39 | 40 | @Override 41 | public void close() { 42 | if (eventLoopGroup != null) { 43 | eventLoopGroup.shutdownGracefully(); 44 | } 45 | managedChannel.shutdown(); 46 | } 47 | 48 | public ManagedChannel getChannel() { 49 | return managedChannel; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /java-spiffe-core/src/integrationTest/java/io/spiffe/workloadapi/WorkloadApiIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi; 2 | 3 | import io.spiffe.bundle.jwtbundle.JwtBundleSet; 4 | import io.spiffe.bundle.x509bundle.X509BundleSet; 5 | import io.spiffe.exception.BundleNotFoundException; 6 | import io.spiffe.exception.JwtBundleException; 7 | import io.spiffe.exception.JwtSvidException; 8 | import io.spiffe.exception.SocketEndpointAddressException; 9 | import io.spiffe.exception.X509BundleException; 10 | import io.spiffe.exception.X509ContextException; 11 | import io.spiffe.spiffeid.SpiffeId; 12 | import io.spiffe.spiffeid.TrustDomain; 13 | import io.spiffe.svid.jwtsvid.JwtSvid; 14 | import org.junit.jupiter.api.Assertions; 15 | import org.junit.jupiter.api.BeforeEach; 16 | import org.junit.jupiter.api.Test; 17 | 18 | // To run these tests there should be a Workload API running, the SPIFFE_ENDPOINT_SOCKET env variable should be defined, 19 | // and there should be a registration entry with a SPIFFE-ID = 'spiffe://example.org/myservice' and a selector unix:uid with 20 | // the user id used to run the process. 21 | class WorkloadApiIntegrationTest { 22 | 23 | private WorkloadApiClient client; 24 | 25 | @BeforeEach 26 | void setup() throws SocketEndpointAddressException { 27 | client = DefaultWorkloadApiClient.newClient(); 28 | } 29 | 30 | @Test 31 | void testFetchX509Context() throws X509ContextException, BundleNotFoundException { 32 | X509Context response = client.fetchX509Context(); 33 | Assertions.assertEquals(response.getDefaultSvid().getSpiffeId(), SpiffeId.parse("spiffe://example.org/myservice")); 34 | Assertions.assertNotNull(response.getX509BundleSet().getBundleForTrustDomain(TrustDomain.parse("example.org"))); 35 | } 36 | 37 | @Test 38 | void testFetchJwtBundles() throws BundleNotFoundException, JwtBundleException { 39 | JwtBundleSet response = client.fetchJwtBundles(); 40 | Assertions.assertNotNull(response.getBundleForTrustDomain(TrustDomain.parse("example.org"))); 41 | } 42 | 43 | @Test 44 | void testFetchX509Bundles() throws BundleNotFoundException, X509BundleException { 45 | X509BundleSet response = client.fetchX509Bundles(); 46 | Assertions.assertNotNull(response.getBundleForTrustDomain(TrustDomain.parse("example.org"))); 47 | } 48 | 49 | @Test 50 | void testFetchJwtSvid() throws JwtSvidException { 51 | JwtSvid response = client.fetchJwtSvid("audience1", "audience2"); 52 | Assertions.assertEquals(response.getSpiffeId(), SpiffeId.parse("spiffe://example.org/myservice")); 53 | Assertions.assertTrue(response.getAudience().contains("audience1")); 54 | Assertions.assertTrue(response.getAudience().contains("audience2")); 55 | Assertions.assertNotNull(response.getToken()); 56 | } 57 | 58 | @Test 59 | void testValidateJwtSvid() throws JwtSvidException { 60 | String token = client.fetchJwtSvid("audience1", "audience2").getToken(); 61 | 62 | JwtSvid response = client.validateJwtSvid(token, "audience1"); 63 | Assertions.assertEquals(response.getSpiffeId(), SpiffeId.parse("spiffe://example.org/myservice")); 64 | Assertions.assertTrue(response.getAudience().contains("audience1")); 65 | Assertions.assertTrue(response.getAudience().contains("audience2")); 66 | } 67 | 68 | @Test 69 | void testValidateJwtSvid_invalid_audience() throws JwtSvidException { 70 | String token = client.fetchJwtSvid("audience1", "audience2").getToken(); 71 | 72 | try { 73 | client.validateJwtSvid(token, "other"); 74 | Assertions.fail(); 75 | } catch (JwtSvidException e) { 76 | Assertions.assertEquals("Error validating JWT SVID", e.getMessage()); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/bundle/BundleSource.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.bundle; 2 | 3 | 4 | import io.spiffe.exception.BundleNotFoundException; 5 | import io.spiffe.spiffeid.TrustDomain; 6 | import lombok.NonNull; 7 | 8 | /** 9 | * Represents a source of bundles of type T keyed by trust domain. 10 | */ 11 | public interface BundleSource { 12 | 13 | /** 14 | * Returns the bundle of type T associated to the given trust domain. 15 | * 16 | * @param trustDomain an instance of a {@link TrustDomain} 17 | * @return the a bundle of type T for the given trust domain 18 | * @throws BundleNotFoundException if no bundle is found for the given trust domain 19 | */ 20 | T getBundleForTrustDomain(@NonNull final TrustDomain trustDomain) throws BundleNotFoundException; 21 | } 22 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/bundle/jwtbundle/JwtBundleSet.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.bundle.jwtbundle; 2 | 3 | import io.spiffe.bundle.BundleSource; 4 | import io.spiffe.exception.BundleNotFoundException; 5 | import io.spiffe.spiffeid.TrustDomain; 6 | import lombok.NonNull; 7 | import lombok.Value; 8 | import lombok.val; 9 | 10 | import java.util.Collection; 11 | import java.util.Collections; 12 | import java.util.Map; 13 | import java.util.concurrent.ConcurrentHashMap; 14 | 15 | /** 16 | * Represents a set of JWT bundles keyed by trust domain. 17 | */ 18 | @Value 19 | public class JwtBundleSet implements BundleSource { 20 | 21 | Map bundles; 22 | 23 | private JwtBundleSet(final Map bundles) { 24 | this.bundles = new ConcurrentHashMap<>(bundles); 25 | } 26 | 27 | private JwtBundleSet() { 28 | this.bundles = new ConcurrentHashMap<>(); 29 | } 30 | 31 | /** 32 | * Creates a JWT bundle set from the list of JWT bundles. 33 | * 34 | * @param bundles Collection of {@link JwtBundle} 35 | * @return a {@link JwtBundleSet} 36 | */ 37 | public static JwtBundleSet of(@NonNull final Collection bundles) { 38 | if (bundles.size() == 0) { 39 | throw new IllegalArgumentException("JwtBundle collection is empty"); 40 | } 41 | final Map bundleMap = new ConcurrentHashMap<>(); 42 | for (JwtBundle bundle : bundles) { 43 | bundleMap.put(bundle.getTrustDomain(), bundle); 44 | } 45 | return new JwtBundleSet(bundleMap); 46 | } 47 | 48 | /** 49 | * Creates a JWT bundle set empty. 50 | * 51 | * @return a {@link JwtBundleSet} 52 | */ 53 | public static JwtBundleSet emptySet() { 54 | return new JwtBundleSet(); 55 | } 56 | 57 | /** 58 | * Gets the JWT bundle associated to a trust domain. 59 | * 60 | * @param trustDomain an instance of a {@link TrustDomain} 61 | * @return a {@link JwtBundle} associated to the given trust domain 62 | * @throws BundleNotFoundException if no bundle could be found for the given trust domain 63 | */ 64 | @Override 65 | public JwtBundle getBundleForTrustDomain(@NonNull final TrustDomain trustDomain) throws BundleNotFoundException { 66 | val bundle = bundles.get(trustDomain); 67 | if (bundle == null) { 68 | throw new BundleNotFoundException(String.format("No JWT bundle for trust domain %s", trustDomain)); 69 | } 70 | return bundle; 71 | } 72 | 73 | /** 74 | * Returns the map of JWT bundles keyed by trust domain. 75 | * 76 | * @return the map of JWT bundles keyed by trust domain 77 | */ 78 | public Map getBundles() { 79 | return Collections.unmodifiableMap(bundles); 80 | } 81 | 82 | /** 83 | * Adds JWT bundle to this set, if the trust domain already exists 84 | * replace the bundle. 85 | * 86 | * @param jwtBundle an instance of a JwtBundle. 87 | */ 88 | public void put(@NonNull final JwtBundle jwtBundle) { 89 | bundles.put(jwtBundle.getTrustDomain(), jwtBundle); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/bundle/x509bundle/X509BundleSet.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.bundle.x509bundle; 2 | 3 | import io.spiffe.bundle.BundleSource; 4 | import io.spiffe.exception.BundleNotFoundException; 5 | import io.spiffe.spiffeid.TrustDomain; 6 | import lombok.NonNull; 7 | import lombok.Value; 8 | import lombok.val; 9 | 10 | import java.util.Collection; 11 | import java.util.Collections; 12 | import java.util.Map; 13 | import java.util.concurrent.ConcurrentHashMap; 14 | 15 | /** 16 | * Represents a set of X.509 bundles keyed by trust domain. 17 | */ 18 | @Value 19 | public class X509BundleSet implements BundleSource { 20 | 21 | Map bundles; 22 | 23 | private X509BundleSet(final Map bundles) { 24 | this.bundles = new ConcurrentHashMap<>(bundles); 25 | } 26 | 27 | private X509BundleSet() { 28 | this.bundles = new ConcurrentHashMap<>(); 29 | } 30 | 31 | /** 32 | * Creates a new X.509 bundle set from a list of X.509 bundles. 33 | * 34 | * @param bundles Collection of {@link X509Bundle} 35 | * @return a {@link X509BundleSet} initialized with the list of bundles 36 | */ 37 | public static X509BundleSet of(@NonNull final Collection bundles) { 38 | if (bundles.size() == 0) { 39 | throw new IllegalArgumentException("X509Bundles collection is empty"); 40 | } 41 | 42 | final Map bundleMap = new ConcurrentHashMap<>(); 43 | for (X509Bundle bundle : bundles) { 44 | bundleMap.put(bundle.getTrustDomain(), bundle); 45 | } 46 | return new X509BundleSet(bundleMap); 47 | } 48 | 49 | /** 50 | * Creates a new X.509 bundle empty. 51 | * 52 | * @return a {@link X509BundleSet} 53 | */ 54 | public static X509BundleSet emptySet() { 55 | return new X509BundleSet(); 56 | } 57 | 58 | /** 59 | * Adds an X.509 bundle to this Set, if the trust domain already exists, 60 | * replaces the bundle. 61 | * 62 | * @param x509Bundle a {@link X509Bundle} 63 | */ 64 | public void put(@NonNull final X509Bundle x509Bundle){ 65 | bundles.put(x509Bundle.getTrustDomain(), x509Bundle); 66 | } 67 | 68 | /** 69 | * Returns the X.509 bundle associated to the trust domain. 70 | * 71 | * @param trustDomain an instance of a {@link TrustDomain} 72 | * @return the {@link X509Bundle} associated to the given trust domain 73 | * @throws BundleNotFoundException if no bundle could be found for the given trust domain 74 | */ 75 | @Override 76 | public X509Bundle getBundleForTrustDomain(@NonNull final TrustDomain trustDomain) throws BundleNotFoundException { 77 | val bundle = bundles.get(trustDomain); 78 | if (bundle == null) { 79 | throw new BundleNotFoundException(String.format("No X.509 bundle for trust domain %s", trustDomain)); 80 | } 81 | return bundle; 82 | } 83 | 84 | /** 85 | * Returns the X.509 bundles of this X.509 Bundle Set. 86 | * 87 | * @return the X.509 bundles of this X.509 Bundle Set 88 | */ 89 | public Map getBundles() { 90 | return Collections.unmodifiableMap(bundles); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/exception/AuthorityNotFoundException.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.exception; 2 | 3 | /** 4 | * Checked exception thrown to indicate that an Authority could not be 5 | * found in the Bundle Source. 6 | */ 7 | public class AuthorityNotFoundException extends Exception { 8 | public AuthorityNotFoundException(final String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/exception/BundleNotFoundException.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.exception; 2 | 3 | /** 4 | * Checked exception thrown to indicate that a Bundle could not be 5 | * found in the Bundle Source. 6 | */ 7 | public class BundleNotFoundException extends Exception { 8 | public BundleNotFoundException(final String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/exception/InvalidSpiffeIdException.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.exception; 2 | 3 | /** 4 | * Runtime exception thrown when there is a validation error on 5 | * a SpiffeId. 6 | */ 7 | public class InvalidSpiffeIdException extends RuntimeException { 8 | public InvalidSpiffeIdException(String s) { 9 | super(s); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/exception/JwtBundleException.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.exception; 2 | 3 | /** 4 | * Checked exception thrown when there is an error creating a JWT Bundle. 5 | */ 6 | public class JwtBundleException extends Exception { 7 | public JwtBundleException(final String message) { 8 | super(message); 9 | } 10 | 11 | public JwtBundleException(final String message, final Throwable cause) { 12 | super(message, cause); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/exception/JwtSourceException.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.exception; 2 | 3 | /** 4 | * Checked thrown when there is an error creating or initializing a JWT Source. 5 | */ 6 | public class JwtSourceException extends Exception { 7 | 8 | public JwtSourceException(final String message) { 9 | super(message); 10 | } 11 | 12 | public JwtSourceException(final String message, final Throwable cause) { 13 | super(message, cause); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/exception/JwtSvidException.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.exception; 2 | 3 | /** 4 | * Checked exception thrown when there is an error parsing 5 | * the components of an JWT SVID. 6 | */ 7 | public class JwtSvidException extends Exception { 8 | 9 | public JwtSvidException(final String message) { 10 | super(message); 11 | } 12 | 13 | public JwtSvidException(final String message, final Throwable cause) { 14 | super(message, cause); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/exception/SocketEndpointAddressException.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.exception; 2 | 3 | /** 4 | * Checked exception thrown to indicate that the socket endpoint address 5 | * could not be parsed or is not valid. 6 | */ 7 | public class SocketEndpointAddressException extends Exception { 8 | public SocketEndpointAddressException(final String message) { 9 | super(message); 10 | } 11 | 12 | public SocketEndpointAddressException(final String message, final Throwable cause) { 13 | super(message, cause); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/exception/WatcherException.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.exception; 2 | 3 | /** 4 | * Unchecked exception to be thrown by Watchers onError method. 5 | */ 6 | public class WatcherException extends RuntimeException { 7 | public WatcherException(String message, Throwable cause) { 8 | super(message, cause); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/exception/X509BundleException.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.exception; 2 | 3 | /** 4 | * Checked exception thrown when there is an error parsing 5 | * the components of an X.509 Bundle. 6 | */ 7 | public class X509BundleException extends Exception { 8 | public X509BundleException(final String message) { 9 | super(message); 10 | } 11 | 12 | public X509BundleException(final String message, final Throwable cause) { 13 | super(message, cause); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/exception/X509ContextException.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.exception; 2 | 3 | /** 4 | * Checked exception thrown when a there was an error retrieving 5 | * or processing an X.509 Context. 6 | */ 7 | public class X509ContextException extends Exception { 8 | public X509ContextException(final String message) { 9 | super(message); 10 | } 11 | 12 | public X509ContextException(final String message, final Throwable cause) { 13 | super(message, cause); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/exception/X509SourceException.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.exception; 2 | 3 | /** 4 | * Checked thrown when there is an error creating or initializing an X.509 Source. 5 | */ 6 | public class X509SourceException extends Exception { 7 | public X509SourceException(final String message) { 8 | super(message); 9 | } 10 | 11 | public X509SourceException(final String message, final Throwable cause) { 12 | super(message, cause); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/exception/X509SvidException.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.exception; 2 | 3 | /** 4 | * Checked exception thrown when there is an error parsing 5 | * the components of an X.509 SVID. 6 | */ 7 | public class X509SvidException extends Exception { 8 | public X509SvidException(final String message) { 9 | super(message); 10 | } 11 | 12 | public X509SvidException(final String message, final Throwable cause) { 13 | super(message, cause); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/internal/AsymmetricKeyAlgorithm.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.internal; 2 | 3 | public enum AsymmetricKeyAlgorithm { 4 | 5 | RSA("RSA"), 6 | EC("EC"); 7 | 8 | private final String value; 9 | 10 | AsymmetricKeyAlgorithm(final String value) { 11 | this.value = value; 12 | } 13 | 14 | public String value() { 15 | return value; 16 | } 17 | 18 | public static AsymmetricKeyAlgorithm parse(String a) { 19 | if ("RSA".equalsIgnoreCase(a)) { 20 | return RSA; 21 | } else if ("EC".equalsIgnoreCase(a)) { 22 | return EC; 23 | } else { 24 | throw new IllegalArgumentException(String.format("Algorithm not supported: %s", a)); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/internal/KeyFileFormat.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.internal; 2 | 3 | public enum KeyFileFormat { 4 | PEM, 5 | DER 6 | } 7 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/internal/KeyUsage.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.internal; 2 | 3 | /** 4 | * Key Usages associated to their index in the X.509 key usage array. 5 | */ 6 | public enum KeyUsage { 7 | 8 | DIGITAL_SIGNATURE(0), 9 | NON_REPUDIATION(1), 10 | KEY_ENCIPHERMENT(2), 11 | DATA_ENCIPHERMENT(3), 12 | KEY_AGREEMENT(4), 13 | KEY_CERT_SIGN(5), 14 | CRL_SIGN(6), 15 | ENCIPHER_ONLY(7), 16 | DECIPHER_ONLY(8); 17 | 18 | private final int index; 19 | 20 | public int index() { 21 | return index; 22 | } 23 | 24 | KeyUsage(final int index) { 25 | this.index = index; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/spiffeid/SpiffeIdUtils.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.spiffeid; 2 | 3 | import com.google.common.collect.Sets; 4 | import lombok.val; 5 | 6 | import java.io.IOException; 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | import java.util.Arrays; 10 | import java.util.Collections; 11 | import java.util.Set; 12 | import java.util.regex.Pattern; 13 | import java.util.stream.Collectors; 14 | 15 | import static org.apache.commons.lang3.StringUtils.isBlank; 16 | 17 | /** 18 | * Utility class with methods to read SPIFFE IDs using different mechanisms. 19 | */ 20 | public final class SpiffeIdUtils { 21 | 22 | private static final char DEFAULT_CHAR_SEPARATOR = '|'; 23 | 24 | private static final Set SUPPORTED_SEPARATORS = Sets.newHashSet(DEFAULT_CHAR_SEPARATOR, ' ', ','); 25 | 26 | private SpiffeIdUtils() { 27 | throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); 28 | } 29 | 30 | /** 31 | * Reads a file containing a list of SPIFFE IDs and parses them to {@link SpiffeId} instances. 32 | *

33 | * The file should have one SPIFFE ID per line. 34 | * 35 | * @param spiffeIdsFile the path to the file containing a list of SPIFFE IDs 36 | * @return a List of {@link SpiffeId} parsed from the file provided 37 | * @throws IOException if the given spiffeIdsFile cannot be read 38 | * @throws IllegalArgumentException if any of the SPIFFE IDs in the file cannot be parsed 39 | */ 40 | public static Set getSpiffeIdSetFromFile(final Path spiffeIdsFile) throws IOException { 41 | try (val lines = Files.lines(spiffeIdsFile)) { 42 | return lines 43 | .map(SpiffeId::parse) 44 | .collect(Collectors.toSet()); 45 | } 46 | } 47 | 48 | /** 49 | * Parses a string representing a list of SPIFFE IDs and returns a list of instances of {@link SpiffeId}. 50 | * 51 | * @param spiffeIds a list of SPIFFE IDs represented in a string 52 | * @param separator used to separate the SPIFFE IDs in the string. Only the pipe character and the blank space 53 | * are valid separators 54 | * @return a list of {@link SpiffeId} instances. 55 | * @throws IllegalArgumentException is the separator provided is not valid 56 | */ 57 | public static Set toSetOfSpiffeIds(final String spiffeIds, final char separator) { 58 | if (isBlank(spiffeIds)) { 59 | return Collections.emptySet(); 60 | } 61 | 62 | if (!SUPPORTED_SEPARATORS.contains(separator)) { 63 | throw new IllegalArgumentException("Separator character is not supported."); 64 | } 65 | 66 | val array = parseArray(spiffeIds, separator); 67 | return Arrays.stream(array) 68 | .map(SpiffeId::parse) 69 | .collect(Collectors.toSet()); 70 | } 71 | 72 | /** 73 | * Return the list of the SPIFFE IDs parsed from the String parameter, using the default separator (pipe character). 74 | * 75 | * @param spiffeIds a String representing a list of SPIFFE IDs separated by the pipe character 76 | * @return a list of {@link SpiffeId} instances 77 | * @throws IllegalArgumentException is the string provided is blank 78 | */ 79 | public static Set toSetOfSpiffeIds(final String spiffeIds) { 80 | return toSetOfSpiffeIds(spiffeIds, DEFAULT_CHAR_SEPARATOR); 81 | } 82 | 83 | private static String[] parseArray(final String spiffeIds, final char separator) { 84 | val regex = Pattern.quote(String.valueOf(separator)); 85 | return spiffeIds.trim().split(regex); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/spiffeid/TrustDomain.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.spiffeid; 2 | 3 | 4 | import io.spiffe.exception.InvalidSpiffeIdException; 5 | import lombok.NonNull; 6 | import lombok.Value; 7 | import org.apache.commons.lang3.StringUtils; 8 | 9 | import static io.spiffe.spiffeid.SpiffeId.BAD_TRUST_DOMAIN_CHAR; 10 | 11 | /** 12 | * Represents the name of a SPIFFE trust domain (e.g. 'domain.test'). 13 | */ 14 | @Value 15 | public class TrustDomain { 16 | 17 | String name; 18 | 19 | TrustDomain(final String trustDomain) { 20 | this.name = trustDomain; 21 | } 22 | 23 | /** 24 | * Creates a trust domain. 25 | * 26 | * @param idOrName the name of a Trust Domain or a string representing a SpiffeId. 27 | * 28 | * @return an instance of a {@link TrustDomain} 29 | * @throws IllegalArgumentException if the given string is empty. 30 | * @throws InvalidSpiffeIdException if the given string contains an invalid char. 31 | */ 32 | public static TrustDomain parse(@NonNull final String idOrName) { 33 | 34 | if (StringUtils.isBlank(idOrName)) { 35 | throw new IllegalArgumentException("Trust domain is missing"); 36 | } 37 | 38 | // Something looks kinda like a scheme separator, let's try to parse as 39 | // an ID. We use :/ instead of :// since the diagnostics are better for 40 | // a bad input like spiffe:/trustdomain. 41 | if (idOrName.contains(":/")) { 42 | SpiffeId spiffeId = SpiffeId.parse(idOrName); 43 | return spiffeId.getTrustDomain(); 44 | } 45 | 46 | validateTrustDomainName(idOrName); 47 | return new TrustDomain(idOrName); 48 | } 49 | 50 | /** 51 | * Creates a SPIFFE ID from this trust domain and the given path segments. 52 | * 53 | * @param segments path segments 54 | * @return a {@link SpiffeId} with the current trust domain and the given path segments 55 | * @throws InvalidSpiffeIdException if the given path segments contain invalid chars or empty or dot segments 56 | */ 57 | public SpiffeId newSpiffeId(final String... segments) { 58 | return SpiffeId.fromSegments(this, segments); 59 | } 60 | 61 | /** 62 | * Returns the trust domain as a String. 63 | * 64 | * @return a String with the trust domain 65 | */ 66 | @Override 67 | public String toString() { 68 | return name; 69 | } 70 | 71 | /** 72 | * Returns the trust domain as SPIFFE ID string (e.g. 'spiffe://example.org') 73 | * 74 | * @return a String formatted as a SPIFFE ID 75 | */ 76 | public String toIdString() { 77 | return SpiffeId.SPIFFE_SCHEME + "://" + name; 78 | } 79 | 80 | static void validateTrustDomainName(final String name) { 81 | for (char c : name.toCharArray()) { 82 | if (!isValidTrustDomainChar(c)) { 83 | throw new InvalidSpiffeIdException(BAD_TRUST_DOMAIN_CHAR); 84 | } 85 | } 86 | } 87 | 88 | static boolean isValidTrustDomainChar(char c) { 89 | if (c >= 'a' && c <= 'z') { 90 | return true; 91 | } 92 | 93 | if (c >= '0' && c <= '9') { 94 | return true; 95 | } 96 | 97 | return c == '-' || c == '.' || c == '_'; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/svid/jwtsvid/JwtSvidSource.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.svid.jwtsvid; 2 | 3 | import io.spiffe.exception.JwtSvidException; 4 | import io.spiffe.spiffeid.SpiffeId; 5 | import lombok.NonNull; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Represents a source of SPIFFE JWT-SVIDs. 11 | */ 12 | public interface JwtSvidSource { 13 | 14 | /** 15 | * Fetches a JWT-SVID from the source with the given audiences. 16 | * 17 | * @param audience the audience 18 | * @param extraAudiences a list of extra audiences as an array of String 19 | * @return a {@link JwtSvid} 20 | * @throws JwtSvidException when there is an error fetching the JWT SVID 21 | */ 22 | JwtSvid fetchJwtSvid(String audience, String... extraAudiences) throws JwtSvidException; 23 | 24 | /** 25 | * Fetches a JWT-SVID from the source with the given subject and audiences. 26 | * 27 | * @param subject a {@link SpiffeId} 28 | * @param audience the audience 29 | * @param extraAudiences a list of extra audiences as an array of String 30 | * @return a {@link JwtSvid} 31 | * @throws JwtSvidException when there is an error fetching the JWT SVID 32 | */ 33 | JwtSvid fetchJwtSvid(SpiffeId subject, String audience, String... extraAudiences) throws JwtSvidException; 34 | 35 | /** 36 | * Fetches all SPIFFE JWT-SVIDs on one-shot blocking call. 37 | * 38 | * @param audience the audience of the JWT-SVID 39 | * @param extraAudience the extra audience for the JWT_SVID 40 | * @return all of {@link JwtSvid} object 41 | * @throws JwtSvidException if there is an error fetching or processing the JWT from the Workload API 42 | */ 43 | List fetchJwtSvids(@NonNull String audience, String... extraAudience) throws JwtSvidException; 44 | 45 | /** 46 | * Fetches all SPIFFE JWT-SVIDs on one-shot blocking call. 47 | * 48 | * @param subject a SPIFFE ID 49 | * @param audience the audience of the JWT-SVID 50 | * @param extraAudience the extra audience for the JWT_SVID 51 | * @return all of {@link JwtSvid} object 52 | * @throws JwtSvidException if there is an error fetching or processing the JWT from the Workload API 53 | */ 54 | List fetchJwtSvids(@NonNull SpiffeId subject, @NonNull String audience, String... extraAudience) throws JwtSvidException; 55 | } 56 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/svid/x509svid/X509SvidSource.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.svid.x509svid; 2 | 3 | /** 4 | * Represents a source of X.509 SVIDs. 5 | */ 6 | public interface X509SvidSource { 7 | 8 | /** 9 | * Returns the X.509 SVID in the source. 10 | * 11 | * @return an instance of a {@link X509Svid} 12 | */ 13 | X509Svid getX509Svid(); 14 | } 15 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/svid/x509svid/X509SvidValidator.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.svid.x509svid; 2 | 3 | import io.spiffe.bundle.BundleSource; 4 | import io.spiffe.bundle.x509bundle.X509Bundle; 5 | import io.spiffe.exception.BundleNotFoundException; 6 | import io.spiffe.internal.CertificateUtils; 7 | import io.spiffe.spiffeid.SpiffeId; 8 | import lombok.NonNull; 9 | import lombok.extern.java.Log; 10 | import lombok.val; 11 | 12 | import java.security.cert.CertPathValidatorException; 13 | import java.security.cert.CertificateException; 14 | import java.security.cert.X509Certificate; 15 | import java.util.List; 16 | import java.util.Set; 17 | import java.util.function.Supplier; 18 | 19 | /** 20 | * Provides methods to validate a chain of X.509 certificates using an X.509 bundle source. 21 | */ 22 | @Log 23 | public final class X509SvidValidator { 24 | 25 | private X509SvidValidator() { 26 | } 27 | 28 | /** 29 | * Verifies that a chain of certificates can be chained to one authority in the given X.509 bundle source. 30 | * 31 | * @param chain a list representing the chain of X.509 certificates to be validated 32 | * @param x509BundleSource a {@link BundleSource } to provide the authorities 33 | * @throws CertificateException is the chain cannot be verified with an authority from the X.509 bundle source 34 | * @throws BundleNotFoundException if no X.509 bundle for the trust domain could be found in the X.509 bundle source 35 | * @throws NullPointerException if the given chain or 509BundleSource are null 36 | */ 37 | public static void verifyChain( 38 | @NonNull final List chain, 39 | @NonNull final BundleSource x509BundleSource) 40 | throws CertificateException, BundleNotFoundException { 41 | 42 | val trustDomain = CertificateUtils.getTrustDomain(chain); 43 | val x509Bundle = x509BundleSource.getBundleForTrustDomain(trustDomain); 44 | 45 | try { 46 | CertificateUtils.validate(chain, x509Bundle.getX509Authorities()); 47 | } catch (CertPathValidatorException e) { 48 | throw new CertificateException("Cert chain cannot be verified", e); 49 | } 50 | } 51 | 52 | /** 53 | * Checks that the X.509 SVID provided has a SPIFFE ID that is in the Set of accepted SPIFFE IDs supplied. 54 | * 55 | * @param x509Certificate a {@link X509Svid} with a SPIFFE ID to be verified 56 | * @param acceptedSpiffeIdsSupplier a {@link Supplier} of a Set of SPIFFE IDs that are accepted 57 | * @throws CertificateException if the SPIFFE ID in x509Certificate is not in the Set supplied by 58 | * acceptedSpiffeIdsSupplier, or if the SPIFFE ID cannot be parsed from the 59 | * x509Certificate 60 | * @throws NullPointerException if the given x509Certificate or acceptedSpiffeIdsSupplier are null 61 | */ 62 | public static void verifySpiffeId(@NonNull final X509Certificate x509Certificate, 63 | @NonNull final Supplier> acceptedSpiffeIdsSupplier) 64 | throws CertificateException { 65 | val spiffeIdSet = acceptedSpiffeIdsSupplier.get(); 66 | if (spiffeIdSet.isEmpty()) { 67 | String error = "The supplier of accepted SPIFFE IDs supplied an empty set"; 68 | log.warning(error); 69 | throw new CertificateException(error); 70 | } 71 | 72 | val spiffeId = CertificateUtils.getSpiffeId(x509Certificate); 73 | if (!spiffeIdSet.contains(spiffeId)) { 74 | val error = String.format("SPIFFE ID %s in X.509 certificate is not accepted", spiffeId); 75 | log.warning(String.format("Client SPIFFE ID validation failed: %s", error)); 76 | throw new CertificateException(String.format(error, spiffeId)); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/workloadapi/AddressScheme.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi; 2 | 3 | /** 4 | * Address Scheme names enum. 5 | */ 6 | public enum AddressScheme { 7 | UNIX_SCHEME("unix"), 8 | TCP_SCHEME("tcp"); 9 | 10 | private final String name; 11 | 12 | AddressScheme(final String scheme) { 13 | this.name = scheme; 14 | } 15 | 16 | /** 17 | * Parses and returns an AddressScheme instance. 18 | * 19 | * @param scheme a string representing an Address Scheme ('unix' or 'tcp') 20 | * @return the enum instance representing the scheme 21 | * @throws IllegalArgumentException if the scheme is not 'unix' or 'tcp' 22 | */ 23 | public static AddressScheme parseScheme(String scheme) { 24 | if ("unix".equals(scheme)) { 25 | return UNIX_SCHEME; 26 | } else if ("tcp".equals(scheme)) { 27 | return TCP_SCHEME; 28 | } else { 29 | throw new IllegalArgumentException("Address Scheme not supported: "); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/workloadapi/JwtSource.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi; 2 | 3 | import io.spiffe.bundle.BundleSource; 4 | import io.spiffe.bundle.jwtbundle.JwtBundle; 5 | import io.spiffe.svid.jwtsvid.JwtSvidSource; 6 | 7 | import java.io.Closeable; 8 | 9 | /** 10 | * Source of JWT SVIDs and Bundles. 11 | * @see JwtSvidSource 12 | * @see BundleSource 13 | * @see JwtBundle 14 | */ 15 | public interface JwtSource extends JwtSvidSource, BundleSource, Closeable { 16 | } 17 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/workloadapi/JwtSourceOptions.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi; 2 | 3 | 4 | import lombok.AccessLevel; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.Setter; 8 | 9 | import java.time.Duration; 10 | 11 | /** 12 | * Options to configure a {@link JwtSource}. 13 | *

14 | * spiffeSocketPath Address to the Workload API, if it is not set, the default address will be used. 15 | *

16 | * initTimeout Timeout for initializing the instance. If it is not defined, the timeout is read 17 | * from the System property `spiffe.newJwtSource.timeout'. If this is also not defined, no default timeout is applied. 18 | *

19 | * workloadApiClient A custom instance of a {@link WorkloadApiClient}, if it is not set, 20 | * a new client will be created. 21 | */ 22 | @Data 23 | public class JwtSourceOptions { 24 | 25 | @Setter(AccessLevel.PUBLIC) 26 | private String spiffeSocketPath; 27 | 28 | @Setter(AccessLevel.PUBLIC) 29 | private Duration initTimeout; 30 | 31 | @Setter(AccessLevel.PUBLIC) 32 | private WorkloadApiClient workloadApiClient; 33 | 34 | @Builder 35 | public JwtSourceOptions( 36 | final String spiffeSocketPath, 37 | final WorkloadApiClient workloadApiClient, 38 | final Duration initTimeout) { 39 | this.spiffeSocketPath = spiffeSocketPath; 40 | this.workloadApiClient = workloadApiClient; 41 | this.initTimeout = initTimeout; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/workloadapi/Watcher.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi; 2 | 3 | /** 4 | * Watches updates of type T. 5 | * 6 | * @param is the type of the updates. 7 | */ 8 | public interface Watcher { 9 | 10 | /** 11 | * Method called in case of success getting an update. 12 | * @param update the instance of type T 13 | */ 14 | void onUpdate(final T update); 15 | 16 | /** 17 | * Method called in case there is an error watching for updates. 18 | * @param e the throwable exception that was caught 19 | */ 20 | void onError(final Throwable e); 21 | } 22 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/workloadapi/X509Context.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi; 2 | 3 | import io.spiffe.bundle.x509bundle.X509BundleSet; 4 | import io.spiffe.svid.x509svid.X509Svid; 5 | import lombok.NonNull; 6 | import lombok.Value; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Represents the X.509 materials that are fetched from the Workload API. 12 | *

13 | * Contains a list of {@link X509Svid} and a {@link X509BundleSet}. 14 | */ 15 | @Value 16 | public class X509Context { 17 | 18 | List x509Svids; 19 | X509BundleSet x509BundleSet; 20 | 21 | X509Context(final List x509Svids, final X509BundleSet x509BundleSet) { 22 | this.x509Svids = x509Svids; 23 | this.x509BundleSet = x509BundleSet; 24 | } 25 | 26 | /** 27 | * Creates a new X509Context from the list of X.509 SVIDs and the X.509 Bundle set. 28 | * 29 | * @param x509Svids a list of {@link X509Svid} 30 | * @param x509BundleSet an instance of {@link X509BundleSet} 31 | * @return an instance of an X509Context 32 | */ 33 | public static X509Context of(@NonNull final List x509Svids, @NonNull final X509BundleSet x509BundleSet) { 34 | if (x509Svids.size() == 0) { 35 | throw new IllegalArgumentException("The X.509 Context must have a least one X.509 SVID"); 36 | } 37 | return new X509Context(x509Svids, x509BundleSet); 38 | } 39 | 40 | /** 41 | * Returns the default SVID (the first in the list). 42 | * 43 | * @return the default SVID (the first in the list) 44 | */ 45 | public X509Svid getDefaultSvid() { 46 | return x509Svids.get(0); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/workloadapi/X509Source.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi; 2 | 3 | import io.spiffe.bundle.BundleSource; 4 | import io.spiffe.bundle.x509bundle.X509Bundle; 5 | import io.spiffe.svid.x509svid.X509SvidSource; 6 | 7 | import java.io.Closeable; 8 | 9 | /** 10 | * Source of X.509 SVIDs and Bundles. 11 | * @see X509SvidSource 12 | * @see BundleSource 13 | * @see X509Bundle 14 | */ 15 | public interface X509Source extends X509SvidSource, BundleSource, Closeable { 16 | } 17 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/workloadapi/internal/SecurityHeaderInterceptor.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi.internal; 2 | 3 | import io.grpc.CallOptions; 4 | import io.grpc.Channel; 5 | import io.grpc.ClientCall; 6 | import io.grpc.ClientInterceptor; 7 | import io.grpc.ForwardingClientCall; 8 | import io.grpc.ForwardingClientCallListener; 9 | import io.grpc.Metadata; 10 | import io.grpc.MethodDescriptor; 11 | 12 | /** 13 | * ClientInterceptor implementation to add a security header required to connect to the Workload API. 14 | */ 15 | public class SecurityHeaderInterceptor implements ClientInterceptor { 16 | 17 | private static final String SECURITY_HEADER = "workload.spiffe.io"; 18 | 19 | /** 20 | * Intercepts the call to the Workload API and adds the required security header. 21 | */ 22 | @Override 23 | public ClientCall interceptCall(MethodDescriptor method, CallOptions callOptions, Channel next) { 24 | return new ForwardingClientCall.SimpleForwardingClientCall(next.newCall(method, callOptions)) { 25 | @Override 26 | public void start(Listener responseListener, Metadata headers) { 27 | Metadata.Key headerKey = Metadata.Key.of(SECURITY_HEADER, Metadata.ASCII_STRING_MARSHALLER); 28 | headers.put(headerKey, "true"); 29 | super.start(new ForwardingClientCallListener.SimpleForwardingClientCallListener(responseListener) {}, headers); 30 | } 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/workloadapi/internal/ThreadUtils.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi.internal; 2 | 3 | import java.util.concurrent.CountDownLatch; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | public final class ThreadUtils { 7 | 8 | private ThreadUtils() { 9 | } 10 | 11 | public static void await(CountDownLatch latch) { 12 | try { 13 | latch.await(); 14 | } catch (InterruptedException e) { 15 | Thread.currentThread().interrupt(); 16 | } 17 | } 18 | 19 | public static boolean await(CountDownLatch latch, long timeout, TimeUnit unit) { 20 | boolean result; 21 | try { 22 | result = latch.await(timeout, unit); 23 | } catch (InterruptedException e) { 24 | Thread.currentThread().interrupt(); 25 | result = false; 26 | } 27 | return result; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/workloadapi/retry/ExponentialBackoffPolicy.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi.retry; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.Setter; 7 | import lombok.val; 8 | 9 | import java.time.Duration; 10 | import java.util.function.UnaryOperator; 11 | 12 | /** 13 | * Represents a backoff policy for performing retries using exponential increasing delays. 14 | */ 15 | @Data 16 | public class ExponentialBackoffPolicy { 17 | 18 | public static final ExponentialBackoffPolicy DEFAULT = new ExponentialBackoffPolicy(); 19 | 20 | // Retry indefinitely, default behavior. 21 | public static final int UNLIMITED_RETRIES = 0; 22 | 23 | private static final int BACKOFF_MULTIPLIER = 2; 24 | 25 | // The first backoff delay period. 26 | @Setter(AccessLevel.NONE) 27 | private Duration initialDelay = Duration.ofSeconds(1); 28 | 29 | // Max time of delay for the backoff period. 30 | @Setter(AccessLevel.NONE) 31 | private Duration maxDelay = Duration.ofSeconds(60); 32 | 33 | // Max number of retries, unlimited by default. 34 | @Setter(AccessLevel.NONE) 35 | private int maxRetries = UNLIMITED_RETRIES; 36 | 37 | @Setter(AccessLevel.NONE) 38 | private int backoffMultiplier = BACKOFF_MULTIPLIER; 39 | 40 | @Setter(AccessLevel.NONE) 41 | private UnaryOperator backoffFunction = d -> d.multipliedBy(backoffMultiplier); 42 | 43 | @Builder 44 | public ExponentialBackoffPolicy(final Duration initialDelay, 45 | final Duration maxDelay, 46 | final int maxRetries, 47 | final int backoffMultiplier) { 48 | this.initialDelay = initialDelay != null ? initialDelay : Duration.ofSeconds(1); 49 | this.maxDelay = maxDelay != null ? maxDelay : Duration.ofSeconds(60); 50 | this.maxRetries = maxRetries != 0 ? maxRetries : UNLIMITED_RETRIES; 51 | this.backoffMultiplier = backoffMultiplier != 0 ? backoffMultiplier : BACKOFF_MULTIPLIER; 52 | } 53 | 54 | private ExponentialBackoffPolicy() { 55 | } 56 | 57 | /** 58 | * Calculate the nextDelay based on a currentDelay, applying the backoff function 59 | * If the calculated delay is greater than maxDelay, it returns maxDelay. 60 | * 61 | * @param currentDelay a {@link Duration} representing the current delay 62 | * @return a {@link Duration} representing the next delay 63 | */ 64 | public Duration nextDelay(final Duration currentDelay) { 65 | // current delay didn't exceed maxDelay already 66 | if (currentDelay.compareTo(maxDelay) < 0) { 67 | return calculateNextDelay(currentDelay); 68 | } 69 | return maxDelay; 70 | } 71 | 72 | /** 73 | * Returns false if the RetryPolicy is configured with UNLIMITED_RETRIES 74 | * or if the retriesCount param is lower than the maxRetries. 75 | * 76 | * @param retriesCount the current number of retries 77 | * @return true if the number of retries reached the max number of retries, false otherwise 78 | */ 79 | public boolean reachedMaxRetries(final int retriesCount) { 80 | return maxRetries != UNLIMITED_RETRIES && retriesCount >= maxRetries; 81 | } 82 | 83 | private Duration calculateNextDelay(final Duration currentDelay) { 84 | val next = backoffFunction.apply(currentDelay); 85 | if (next.compareTo(maxDelay) > 0) { 86 | return maxDelay; 87 | } 88 | return next; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /java-spiffe-core/src/main/java/io/spiffe/workloadapi/retry/RetryHandler.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi.retry; 2 | 3 | import java.time.Duration; 4 | import java.util.concurrent.ScheduledExecutorService; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | /** 8 | * Provides methods to schedule the execution of retries based on a backoff policy. 9 | */ 10 | public class RetryHandler { 11 | 12 | private final ScheduledExecutorService executor; 13 | private final ExponentialBackoffPolicy exponentialBackoffPolicy; 14 | private Duration nextDelay; 15 | 16 | private int retryCount; 17 | 18 | public RetryHandler(final ExponentialBackoffPolicy exponentialBackoffPolicy, final ScheduledExecutorService executor) { 19 | this.nextDelay = exponentialBackoffPolicy.getInitialDelay(); 20 | this.exponentialBackoffPolicy = exponentialBackoffPolicy; 21 | this.executor = executor; 22 | } 23 | 24 | /** 25 | * Schedule to execute a Runnable, based on the backoff policy 26 | * Updates the next delay and retries count. 27 | * 28 | * @param runnable the task to be scheduled for execution 29 | */ 30 | public void scheduleRetry(final Runnable runnable) { 31 | if (executor.isShutdown()) { 32 | return; 33 | } 34 | 35 | if (exponentialBackoffPolicy.reachedMaxRetries(retryCount)) { 36 | return; 37 | } 38 | executor.schedule(runnable, nextDelay.getSeconds(), TimeUnit.SECONDS); 39 | nextDelay = exponentialBackoffPolicy.nextDelay(nextDelay); 40 | retryCount++; 41 | } 42 | 43 | /** 44 | * Returns true is a new retry should be performs, according the the Retry Policy. 45 | * @return true is a new retry should be performs, according the the Retry Policy 46 | */ 47 | public boolean shouldRetry() { 48 | return !exponentialBackoffPolicy.reachedMaxRetries(retryCount); 49 | } 50 | 51 | /** 52 | * Reset state of RetryHandle to initial values. 53 | */ 54 | public void reset() { 55 | nextDelay = exponentialBackoffPolicy.getInitialDelay(); 56 | retryCount = 0; 57 | } 58 | 59 | public Duration getNextDelay() { 60 | return nextDelay; 61 | } 62 | 63 | public int getRetryCount() { 64 | return retryCount; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/java/io/spiffe/internal/AsymmetricKeyAlgorithmTest.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.internal; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.assertEquals; 6 | 7 | class AsymmetricKeyAlgorithmTest { 8 | 9 | @Test 10 | void parseRSA() { 11 | AsymmetricKeyAlgorithm algorithm = AsymmetricKeyAlgorithm.parse("RSA"); 12 | assertEquals(AsymmetricKeyAlgorithm.RSA, algorithm); 13 | } 14 | 15 | @Test 16 | void parseEC() { 17 | AsymmetricKeyAlgorithm algorithm = AsymmetricKeyAlgorithm.parse("EC"); 18 | assertEquals(AsymmetricKeyAlgorithm.EC, algorithm); 19 | } 20 | 21 | @Test 22 | void parseUnknown() { 23 | try { 24 | AsymmetricKeyAlgorithm.parse("unknown"); 25 | } catch (IllegalArgumentException e) { 26 | assertEquals("Algorithm not supported: unknown", e.getMessage()); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /java-spiffe-core/src/test/java/io/spiffe/internal/DummyPublicKey.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.internal; 2 | 3 | import java.security.PublicKey; 4 | 5 | public class DummyPublicKey implements PublicKey { 6 | @Override 7 | public String getAlgorithm() { 8 | return null; 9 | } 10 | 11 | @Override 12 | public String getFormat() { 13 | return null; 14 | } 15 | 16 | @Override 17 | public byte[] getEncoded() { 18 | return new byte[0]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/java/io/spiffe/workloadapi/DefaultWorkloadApiClientMismatchSpiffeIdTest.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi; 2 | 3 | import io.grpc.testing.GrpcCleanupRule; 4 | import io.spiffe.exception.X509ContextException; 5 | import org.junit.Rule; 6 | import org.junit.jupiter.api.AfterEach; 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.Test; 9 | 10 | import java.io.IOException; 11 | 12 | import static org.junit.jupiter.api.Assertions.assertEquals; 13 | import static org.junit.jupiter.api.Assertions.fail; 14 | 15 | class DefaultWorkloadApiClientMismatchSpiffeIdTest { 16 | 17 | @Rule 18 | public final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); 19 | private DefaultWorkloadApiClient workloadApiClient; 20 | 21 | @BeforeEach 22 | void setUp() throws IOException { 23 | workloadApiClient = WorkloadApiClientTestUtil.create(new FakeWorkloadApiMismatchSpiffeId(), grpcCleanup); 24 | } 25 | 26 | @AfterEach 27 | void tearDown() { 28 | workloadApiClient.close(); 29 | } 30 | 31 | 32 | @Test 33 | public void testFetchX509Context_throwsX509ContextException_whenSpiffeIdsMismatch() throws Exception { 34 | String expectedError = 35 | "SPIFFE ID in X509SVIDResponse (spiffe://wrong/domain/workload-server) does not match " + 36 | "SPIFFE ID in X.509 certificate (spiffe://example.org/workload-server)"; 37 | 38 | try { 39 | workloadApiClient.fetchX509Context(); 40 | fail(); 41 | } catch (X509ContextException e) { 42 | assertEquals("Error fetching X509Context", e.getMessage()); 43 | assertEquals(expectedError, e.getCause().getMessage()); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /java-spiffe-core/src/test/java/io/spiffe/workloadapi/DefaultWorkloadApiClientRetryableErrorTest.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi; 2 | 3 | import io.grpc.Status; 4 | import io.grpc.testing.GrpcCleanupRule; 5 | import io.spiffe.bundle.jwtbundle.JwtBundleSet; 6 | import io.spiffe.bundle.x509bundle.X509BundleSet; 7 | import io.spiffe.exception.X509ContextException; 8 | import org.junit.Rule; 9 | import org.junit.jupiter.api.AfterEach; 10 | import org.junit.jupiter.api.BeforeEach; 11 | import org.junit.jupiter.api.Test; 12 | 13 | import java.io.IOException; 14 | import java.util.concurrent.CountDownLatch; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | import static org.junit.jupiter.api.Assertions.assertEquals; 18 | import static org.junit.jupiter.api.Assertions.fail; 19 | 20 | class DefaultWorkloadApiClientRetryableErrorTest { 21 | 22 | @Rule 23 | public final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule(); 24 | private DefaultWorkloadApiClient workloadApiClient; 25 | 26 | @BeforeEach 27 | void setUp() throws IOException { 28 | workloadApiClient = WorkloadApiClientTestUtil.create(new FakeWorkloadApiExceptions(Status.UNAVAILABLE), grpcCleanup); 29 | } 30 | 31 | @AfterEach 32 | void tearDown() { 33 | workloadApiClient.close(); 34 | } 35 | 36 | 37 | @Test 38 | void testFetchX509Context_throwsX509ContextException() throws Exception { 39 | try { 40 | workloadApiClient.fetchX509Context(); 41 | fail(); 42 | } catch (X509ContextException e) { 43 | assertEquals("Error fetching X509Context", e.getMessage()); 44 | } 45 | } 46 | 47 | @Test 48 | void testWatchX509Context_onErrorIsCalledOnWatcher() throws Exception { 49 | CountDownLatch done = new CountDownLatch(1); 50 | Watcher contextWatcher = new Watcher() { 51 | @Override 52 | public void onUpdate(X509Context update) { 53 | fail(); 54 | } 55 | 56 | @Override 57 | public void onError(Throwable e) { 58 | assertEquals("Cancelling X.509 Context watch", e.getMessage()); 59 | done.countDown(); 60 | } 61 | }; 62 | workloadApiClient.watchX509Context(contextWatcher); 63 | done.await(); 64 | } 65 | 66 | @Test 67 | void testWatchX509Bundles_onErrorIsCalledOnWatched() throws InterruptedException { 68 | CountDownLatch done = new CountDownLatch(1); 69 | Watcher contextWatcher = new Watcher() { 70 | @Override 71 | public void onUpdate(X509BundleSet update) { 72 | fail(); 73 | } 74 | 75 | @Override 76 | public void onError(Throwable e) { 77 | assertEquals("Cancelling X.509 bundles watch", e.getMessage()); 78 | done.countDown(); 79 | } 80 | }; 81 | workloadApiClient.watchX509Bundles(contextWatcher); 82 | done.await(); 83 | } 84 | 85 | @Test 86 | void testWatchJwtBundles_onErrorIsCalledOnWatched() throws InterruptedException { 87 | CountDownLatch done = new CountDownLatch(1); 88 | Watcher contextWatcher = new Watcher() { 89 | @Override 90 | public void onUpdate(JwtBundleSet update) { 91 | fail(); 92 | } 93 | 94 | @Override 95 | public void onError(Throwable e) { 96 | assertEquals("Cancelling JWT Bundles watch", e.getMessage()); 97 | done.countDown(); 98 | } 99 | }; 100 | workloadApiClient.watchJwtBundles(contextWatcher); 101 | done.await(); 102 | } 103 | } -------------------------------------------------------------------------------- /java-spiffe-core/src/test/java/io/spiffe/workloadapi/FakeWorkloadApiEmptyResponse.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi; 2 | 3 | import io.grpc.stub.StreamObserver; 4 | import io.spiffe.workloadapi.grpc.SpiffeWorkloadAPIGrpc.SpiffeWorkloadAPIImplBase; 5 | import io.spiffe.workloadapi.grpc.Workload; 6 | 7 | class FakeWorkloadApiEmptyResponse extends SpiffeWorkloadAPIImplBase { 8 | 9 | @Override 10 | public void fetchX509SVID(Workload.X509SVIDRequest request, StreamObserver responseObserver) { 11 | responseObserver.onNext(Workload.X509SVIDResponse.newBuilder().build()); 12 | responseObserver.onCompleted(); 13 | } 14 | 15 | @Override 16 | public void fetchX509Bundles(Workload.X509BundlesRequest request, StreamObserver responseObserver) { 17 | responseObserver.onNext(Workload.X509BundlesResponse.newBuilder().build()); 18 | responseObserver.onCompleted(); 19 | } 20 | 21 | @Override 22 | public void fetchJWTSVID(Workload.JWTSVIDRequest request, StreamObserver responseObserver) { 23 | responseObserver.onNext(Workload.JWTSVIDResponse.newBuilder().build()); 24 | responseObserver.onCompleted(); 25 | } 26 | 27 | @Override 28 | public void fetchJWTBundles(Workload.JWTBundlesRequest request, StreamObserver responseObserver) { 29 | responseObserver.onNext(Workload.JWTBundlesResponse.newBuilder().build()); 30 | responseObserver.onCompleted(); 31 | } 32 | 33 | @Override 34 | public void validateJWTSVID(Workload.ValidateJWTSVIDRequest request, StreamObserver responseObserver) { 35 | responseObserver.onNext(Workload.ValidateJWTSVIDResponse.newBuilder().build()); 36 | responseObserver.onCompleted(); 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/java/io/spiffe/workloadapi/FakeWorkloadApiExceptions.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi; 2 | 3 | import io.grpc.Status; 4 | import io.grpc.StatusException; 5 | import io.grpc.stub.StreamObserver; 6 | import io.spiffe.workloadapi.grpc.SpiffeWorkloadAPIGrpc.SpiffeWorkloadAPIImplBase; 7 | import io.spiffe.workloadapi.grpc.Workload; 8 | 9 | class FakeWorkloadApiExceptions extends SpiffeWorkloadAPIImplBase { 10 | 11 | private final StatusException exception; 12 | 13 | FakeWorkloadApiExceptions(Status errorStatus) { 14 | exception = new StatusException(errorStatus); 15 | } 16 | 17 | 18 | @Override 19 | public void fetchX509SVID(Workload.X509SVIDRequest request, StreamObserver responseObserver) { 20 | responseObserver.onError(exception); 21 | } 22 | 23 | @Override 24 | public void fetchX509Bundles(Workload.X509BundlesRequest request, StreamObserver responseObserver) { 25 | responseObserver.onError(exception); 26 | } 27 | 28 | @Override 29 | public void fetchJWTSVID(Workload.JWTSVIDRequest request, StreamObserver responseObserver) { 30 | responseObserver.onError(exception); 31 | } 32 | 33 | @Override 34 | public void fetchJWTBundles(Workload.JWTBundlesRequest request, StreamObserver responseObserver) { 35 | responseObserver.onError(exception); 36 | } 37 | 38 | @Override 39 | public void validateJWTSVID(Workload.ValidateJWTSVIDRequest request, StreamObserver responseObserver) { 40 | responseObserver.onError(exception); 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/java/io/spiffe/workloadapi/FakeWorkloadApiMismatchSpiffeId.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi; 2 | 3 | import com.google.protobuf.ByteString; 4 | import io.grpc.stub.StreamObserver; 5 | import io.spiffe.workloadapi.grpc.SpiffeWorkloadAPIGrpc.SpiffeWorkloadAPIImplBase; 6 | import io.spiffe.workloadapi.grpc.Workload; 7 | 8 | import java.io.IOException; 9 | import java.net.URI; 10 | import java.net.URISyntaxException; 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | import java.nio.file.Paths; 14 | 15 | class FakeWorkloadApiMismatchSpiffeId extends SpiffeWorkloadAPIImplBase { 16 | 17 | final String privateKey = "testdata/workloadapi/svid.key.der"; 18 | final String svid = "testdata/workloadapi/svid.der"; 19 | final String x509Bundle = "testdata/workloadapi/bundle.der"; 20 | final String jwtBundle = "testdata/workloadapi/bundle.json"; 21 | 22 | @Override 23 | public void fetchX509SVID(Workload.X509SVIDRequest request, StreamObserver responseObserver) { 24 | try { 25 | Path pathCert = Paths.get(toUri(svid)); 26 | byte[] svidBytes = Files.readAllBytes(pathCert); 27 | ByteString svidByteString = ByteString.copyFrom(svidBytes); 28 | 29 | Path pathKey = Paths.get(toUri(privateKey)); 30 | byte[] keyBytes = Files.readAllBytes(pathKey); 31 | ByteString keyByteString = ByteString.copyFrom(keyBytes); 32 | 33 | Path pathBundle = Paths.get(toUri(x509Bundle)); 34 | byte[] bundleBytes = Files.readAllBytes(pathBundle); 35 | ByteString bundleByteString = ByteString.copyFrom(bundleBytes); 36 | 37 | Workload.X509SVID svid = Workload.X509SVID 38 | .newBuilder() 39 | .setSpiffeId("spiffe://wrong/domain/workload-server") 40 | .setX509Svid(svidByteString) 41 | .setX509SvidKey(keyByteString) 42 | .setBundle(bundleByteString) 43 | .build(); 44 | 45 | Workload.X509SVIDResponse response = Workload.X509SVIDResponse 46 | .newBuilder() 47 | .addSvids(svid) 48 | .build(); 49 | 50 | responseObserver.onNext(response); 51 | responseObserver.onCompleted(); 52 | } catch (URISyntaxException | IOException e) { 53 | throw new Error("Failed FakeSpiffeWorkloadApiService.fetchX509SVID", e); 54 | } 55 | } 56 | 57 | 58 | @Override 59 | public void fetchJWTSVID(Workload.JWTSVIDRequest request, StreamObserver responseObserver) { 60 | responseObserver.onNext(Workload.JWTSVIDResponse.newBuilder().build()); 61 | responseObserver.onCompleted(); 62 | } 63 | 64 | @Override 65 | public void fetchJWTBundles(Workload.JWTBundlesRequest request, StreamObserver responseObserver) { 66 | responseObserver.onNext(Workload.JWTBundlesResponse.newBuilder().build()); 67 | responseObserver.onCompleted(); 68 | } 69 | 70 | @Override 71 | public void validateJWTSVID(Workload.ValidateJWTSVIDRequest request, StreamObserver responseObserver) { 72 | responseObserver.onNext(Workload.ValidateJWTSVIDResponse.newBuilder().build()); 73 | responseObserver.onCompleted(); 74 | } 75 | 76 | private URI toUri(String path) throws URISyntaxException { 77 | return Thread.currentThread().getContextClassLoader().getResource(path).toURI(); 78 | } 79 | } 80 | 81 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/java/io/spiffe/workloadapi/WorkloadApiClientErrorStub.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi; 2 | 3 | import io.spiffe.bundle.jwtbundle.JwtBundleSet; 4 | import io.spiffe.bundle.x509bundle.X509BundleSet; 5 | import io.spiffe.exception.JwtBundleException; 6 | import io.spiffe.exception.JwtSvidException; 7 | import io.spiffe.exception.X509BundleException; 8 | import io.spiffe.exception.X509ContextException; 9 | import io.spiffe.spiffeid.SpiffeId; 10 | import io.spiffe.svid.jwtsvid.JwtSvid; 11 | import lombok.NonNull; 12 | 13 | import java.io.IOException; 14 | import java.util.List; 15 | 16 | public class WorkloadApiClientErrorStub implements WorkloadApiClient { 17 | 18 | @Override 19 | public X509Context fetchX509Context() throws X509ContextException { 20 | throw new X509ContextException("Testing exception"); 21 | } 22 | 23 | @Override 24 | public void watchX509Context(@NonNull final Watcher watcher) { 25 | watcher.onError(new X509ContextException("Testing exception")); 26 | } 27 | 28 | @Override 29 | public X509BundleSet fetchX509Bundles() throws X509BundleException { 30 | throw new X509BundleException("Testing exception"); 31 | } 32 | 33 | @Override 34 | public void watchX509Bundles(@NonNull Watcher watcher) { 35 | watcher.onError(new X509BundleException("Testing exception")); 36 | } 37 | 38 | @Override 39 | public JwtSvid fetchJwtSvid(@NonNull final String audience, final String... extraAudience) throws JwtSvidException { 40 | throw new JwtSvidException("Testing exception"); 41 | } 42 | 43 | @Override 44 | public JwtSvid fetchJwtSvid(@NonNull final SpiffeId subject, @NonNull final String audience, final String... extraAudience) throws JwtSvidException { 45 | throw new JwtSvidException("Testing exception"); 46 | } 47 | 48 | @Override 49 | public List fetchJwtSvids(@NonNull String audience, String... extraAudience) throws JwtSvidException { 50 | throw new JwtSvidException("Testing exception"); 51 | } 52 | 53 | @Override 54 | public List fetchJwtSvids(@NonNull SpiffeId subject, @NonNull String audience, String... extraAudience) throws JwtSvidException { 55 | throw new JwtSvidException("Testing exception"); 56 | } 57 | 58 | @Override 59 | public JwtBundleSet fetchJwtBundles() throws JwtBundleException { 60 | throw new JwtBundleException("Testing exception"); 61 | } 62 | 63 | @Override 64 | public JwtSvid validateJwtSvid(@NonNull final String token, @NonNull final String audience) throws JwtSvidException { 65 | return null; 66 | } 67 | 68 | @Override 69 | public void watchJwtBundles(@NonNull final Watcher watcher) { 70 | watcher.onError(new JwtBundleException("Testing exception")); 71 | } 72 | 73 | @Override 74 | public void close() throws IOException { 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/java/io/spiffe/workloadapi/WorkloadApiClientTestUtil.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi; 2 | 3 | import io.grpc.ManagedChannel; 4 | import io.grpc.Server; 5 | import io.grpc.inprocess.InProcessChannelBuilder; 6 | import io.grpc.inprocess.InProcessServerBuilder; 7 | import io.grpc.testing.GrpcCleanupRule; 8 | import io.spiffe.workloadapi.grpc.SpiffeWorkloadAPIGrpc; 9 | import io.spiffe.workloadapi.internal.ManagedChannelWrapper; 10 | import io.spiffe.workloadapi.internal.SecurityHeaderInterceptor; 11 | import io.spiffe.workloadapi.retry.ExponentialBackoffPolicy; 12 | 13 | import java.io.IOException; 14 | import java.time.Duration; 15 | 16 | public class WorkloadApiClientTestUtil { 17 | 18 | static DefaultWorkloadApiClient create(SpiffeWorkloadAPIGrpc.SpiffeWorkloadAPIImplBase fakeWorkloadApi, GrpcCleanupRule grpcCleanup) throws IOException { 19 | // Generate a unique in-process server name. 20 | String serverName = InProcessServerBuilder.generateName(); 21 | 22 | Server server = InProcessServerBuilder.forName(serverName).directExecutor().addService(fakeWorkloadApi).build().start(); 23 | grpcCleanup.register(server); 24 | 25 | // Create WorkloadApiClient using Stubs that will connect to the fake WorkloadApiService. 26 | final ManagedChannel inProcessChannel = InProcessChannelBuilder.forName(serverName).directExecutor().build(); 27 | grpcCleanup.register(inProcessChannel); 28 | 29 | SecurityHeaderInterceptor securityHeaderInterceptor = new SecurityHeaderInterceptor(); 30 | SpiffeWorkloadAPIGrpc.SpiffeWorkloadAPIBlockingStub workloadApiBlockingStub = SpiffeWorkloadAPIGrpc 31 | .newBlockingStub(inProcessChannel) 32 | .withInterceptors(securityHeaderInterceptor); 33 | 34 | SpiffeWorkloadAPIGrpc.SpiffeWorkloadAPIStub workloadAPIStub = SpiffeWorkloadAPIGrpc 35 | .newStub(inProcessChannel) 36 | .withInterceptors(securityHeaderInterceptor); 37 | 38 | return new DefaultWorkloadApiClient(workloadAPIStub, 39 | workloadApiBlockingStub, 40 | new ManagedChannelWrapper(inProcessChannel), 41 | ExponentialBackoffPolicy.builder().maxRetries(1).maxDelay(Duration.ofMillis(1)).build()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/java/io/spiffe/workloadapi/X509ContextTest.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi; 2 | 3 | import io.spiffe.bundle.x509bundle.X509Bundle; 4 | import io.spiffe.bundle.x509bundle.X509BundleSet; 5 | import io.spiffe.spiffeid.TrustDomain; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import java.util.Arrays; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | import static org.junit.jupiter.api.Assertions.assertEquals; 13 | import static org.junit.jupiter.api.Assertions.fail; 14 | 15 | class X509ContextTest { 16 | 17 | @Test 18 | void of_NullListOfX509SvidsParameter() { 19 | try { 20 | X509Context.of(null, createBundleSet()); 21 | fail(); 22 | } catch (NullPointerException e) { 23 | assertEquals("x509Svids is marked non-null but is null", e.getMessage()); 24 | } 25 | } 26 | 27 | @Test 28 | void of_EmtpyListOfX509SvidsParameter() { 29 | try { 30 | X509Context.of(Collections.emptyList(), createBundleSet()); 31 | fail(); 32 | } catch (IllegalArgumentException e) { 33 | assertEquals("The X.509 Context must have a least one X.509 SVID", e.getMessage()); 34 | } 35 | } 36 | 37 | @Test 38 | void getNewllX509BundleSet() { 39 | try { 40 | X509Context.of(Collections.emptyList(), null); 41 | fail(); 42 | } catch (NullPointerException e) { 43 | assertEquals("x509BundleSet is marked non-null but is null", e.getMessage()); 44 | } 45 | } 46 | 47 | private X509BundleSet createBundleSet() { 48 | X509Bundle x509Bundle1 = new X509Bundle(TrustDomain.parse("example.org")); 49 | X509Bundle x509Bundle2 = new X509Bundle(TrustDomain.parse("other.org")); 50 | List bundleList = Arrays.asList(x509Bundle1, x509Bundle2); 51 | return X509BundleSet.of(bundleList); 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /java-spiffe-core/src/test/java/io/spiffe/workloadapi/retry/ExponentialBackoffPolicyTest.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.workloadapi.retry; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.time.Duration; 6 | 7 | import static org.junit.jupiter.api.Assertions.assertEquals; 8 | import static org.junit.jupiter.api.Assertions.assertFalse; 9 | import static org.junit.jupiter.api.Assertions.assertTrue; 10 | 11 | class ExponentialBackoffPolicyTest { 12 | 13 | @Test 14 | void testNextDelayDefaultPolicy_returnsDouble() { 15 | ExponentialBackoffPolicy exponentialBackoffPolicy = ExponentialBackoffPolicy.DEFAULT; 16 | assertEquals(Duration.ofSeconds(10), exponentialBackoffPolicy.nextDelay(Duration.ofSeconds(5))); 17 | } 18 | 19 | @Test 20 | void testNextDelayDefaultPolicy_exceedsMaxDelay_returnsMaxDelay() { 21 | ExponentialBackoffPolicy exponentialBackoffPolicy = ExponentialBackoffPolicy 22 | .builder() 23 | .initialDelay(Duration.ofSeconds(1)) 24 | .maxDelay(Duration.ofSeconds(60)) 25 | .build(); 26 | 27 | assertEquals(Duration.ofSeconds(60), exponentialBackoffPolicy.nextDelay(Duration.ofSeconds(50))); 28 | } 29 | 30 | @Test 31 | void testNextDelayDefaultPolicy_currentDelayExceedsMaxDelay_returnsMaxDelay() { 32 | ExponentialBackoffPolicy exponentialBackoffPolicy = ExponentialBackoffPolicy 33 | .builder() 34 | .initialDelay(Duration.ofSeconds(1)) 35 | .maxDelay(Duration.ofSeconds(60)) 36 | .build(); 37 | 38 | assertEquals(Duration.ofSeconds(60), exponentialBackoffPolicy.nextDelay(Duration.ofSeconds(70))); 39 | } 40 | 41 | @Test 42 | void testNextDelayCustomPolicy() { 43 | ExponentialBackoffPolicy exponentialBackoffPolicy = ExponentialBackoffPolicy 44 | .builder() 45 | .maxDelay(Duration.ofSeconds(60)) 46 | .backoffMultiplier(5) 47 | .build(); 48 | 49 | assertEquals(Duration.ofSeconds(50), exponentialBackoffPolicy.nextDelay(Duration.ofSeconds(10))); 50 | } 51 | 52 | @Test 53 | void testDidNotReachMaxRetries() { 54 | ExponentialBackoffPolicy exponentialBackoffPolicy = ExponentialBackoffPolicy 55 | .builder() 56 | .maxRetries(5) 57 | .build(); 58 | 59 | assertFalse(exponentialBackoffPolicy.reachedMaxRetries(4)); 60 | } 61 | 62 | @Test 63 | void testDidNotReachMaxRetries_retriesCountEqualsMaxRetries_returnsFalse() { 64 | ExponentialBackoffPolicy exponentialBackoffPolicy = ExponentialBackoffPolicy 65 | .builder() 66 | .maxRetries(5) 67 | .build(); 68 | 69 | assertTrue(exponentialBackoffPolicy.reachedMaxRetries(5)); 70 | } 71 | 72 | @Test 73 | void testDidNotReachMaxRetries_UnlimitedRetries() { 74 | ExponentialBackoffPolicy exponentialBackoffPolicy = ExponentialBackoffPolicy.DEFAULT; 75 | assertFalse(exponentialBackoffPolicy.reachedMaxRetries(Integer.MAX_VALUE)); 76 | } 77 | } -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/internal/bundle.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIByjCCAU+gAwIBAgIBADAKBggqhkjOPQQDAzAeMQswCQYDVQQGEwJVUzEPMA0G 3 | A1UEChMGU1BJRkZFMB4XDTIwMDMxOTE5MjgzMVoXDTIwMDMyNjE5Mjg0MVowHjEL 4 | MAkGA1UEBhMCVVMxDzANBgNVBAoTBlNQSUZGRTB2MBAGByqGSM49AgEGBSuBBAAi 5 | A2IABADlhh52EsAilaE7KfVdnWoxX4xEI9NQEfrsqDPC69j4b+6j1zh7pS51a0jM 6 | i+ua4a8Su7udFYWJ92t/B9RO4mzz1fRnn57BNIPhPChWhUAwVSEaWe3JSTYgUYz3 7 | 6iWnTaNhMF8wDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O 8 | BBYEFNEafrZejn4vo7x0pFOzvJJcvmuQMB0GA1UdEQQWMBSGEnNwaWZmZTovL290 9 | aGVyLm9yZzAKBggqhkjOPQQDAwNpADBmAjEAigR8L3rZ/3LvRKu5sHRdFV8/w6aD 10 | uYK6PxR9IYhWbxsEfzSsqyd2im93GkLFZrBBAjEAvdQuJqs6lzWuoCtvMdvTm51t 11 | II7EW/OZl2OKIGrFupgpKPK+qBhzn/QU/C6jsIdC 12 | -----END CERTIFICATE----- 13 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/internal/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICADCCAYagAwIBAgIQJqqLnXOR3tjUV43PpGW4YDAKBggqhkjOPQQDAzAeMQsw 3 | CQYDVQQGEwJVUzEPMA0GA1UEChMGU1BJRkZFMB4XDTIwMDMxODE5MDc1MloXDTIw 4 | MDMyMzE4MDUwN1owHTELMAkGA1UEBhMCVVMxDjAMBgNVBAoTBVNQSVJFMFkwEwYH 5 | KoZIzj0CAQYIKoZIzj0DAQcDQgAE4/NA2umeK9j1U5+egPqfzomjnpnLz68jNvUN 6 | tdA0Lg6E7/nsmvqoNbVVbaD84Jplfg6/6HWSnoO7K7A+oZ1g+qOBpjCBozAOBgNV 7 | HQ8BAf8EBAMCA6gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1Ud 8 | EwEB/wQCMAAwHQYDVR0OBBYEFPLf59AkFCva6ehKx8L4i+pjCU1CMB8GA1UdIwQY 9 | MBaAFCMzt4WGhen9N2N+MwzUkwEtG6YLMCQGA1UdEQQdMBuGGXNwaWZmZTovL2V4 10 | YW1wbGUub3JnL3Rlc3QwCgYIKoZIzj0EAwMDaAAwZQIwRzrN6Rh8X28UYJEuql/1 11 | GZEeto7zzj0UtjZFwQy2ODl48nFFRGKUnq8mc4cIMI/kAjEAlixJBUHqb4ty8Ff+ 12 | d0XHzA5duE1hzFxd2feRppjqiOHKJu7Rh2dzZd3rZhMZWrrd 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/internal/cert2.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIB/TCCAYSgAwIBAgIQRWYURsMOUzpIOD9/uyhK/zAKBggqhkjOPQQDAzAeMQsw 3 | CQYDVQQGEwJVUzEPMA0GA1UEChMGU1BJRkZFMB4XDTIwMDMxOTE5MzAyM1oXDTIw 4 | MDMxOTIwMzAzM1owHTELMAkGA1UEBhMCVVMxDjAMBgNVBAoTBVNQSVJFMFkwEwYH 5 | KoZIzj0CAQYIKoZIzj0DAQcDQgAEdJg/X2P9fl6miUX/WtVj/Aeen0urPUI5rwI4 6 | 0q21AugzjbXbk+XzXj7VfUNyqHsfWx4VZo9sTg3zYgnTTdQhvKOBpDCBoTAOBgNV 7 | HQ8BAf8EBAMCA6gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1Ud 8 | EwEB/wQCMAAwHQYDVR0OBBYEFO/QWXfD2Rkf2SttF7fdaYfY8a/8MB8GA1UdIwQY 9 | MBaAFNEafrZejn4vo7x0pFOzvJJcvmuQMCIGA1UdEQQbMBmGF3NwaWZmZTovL290 10 | aGVyLm9yZy90ZXN0MAoGCCqGSM49BAMDA2cAMGQCMALzVBadygedI0YSE1VFDEtW 11 | D+QVECNd4ax+3nKswJk3bLj+9hBswLajuKHVULOgLgIwW85beZr9jklhz6MvdbM5 12 | TeE8X+rXG9fdELMClZI/k2+IwtFua4la7ikb+xh3ZUnA 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/internal/privateKeyRsa.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIB5QIBADANBgkqhkiG9w0BAQEFAASCAc8wggHLAgEAAmEA0EdYWa7LZ/ZA2sKk 3 | JsREA2TkXDIq01JIRsVetPFRqRW5GEmkwwk0qThebp/ofZt8oLkMwp06BLaQC3tH 4 | QGkK31jh5Djd1NHA8CpU4ByObgGeY75dhwFEzI4YwXM+e0n/AgMBAAECYFD4S4qh 5 | /4WtIE1refFwP5iqMnT9M9TvmhWZSVZCsqJvRYQBrUH9ZDGdLmkHVZTvSvKKmkoZ 6 | VvXDlpmW4Eaed8xXqsLYplMrVo6WkvdtvlvfIwP69PGFmWwKgFBe2aLHsQIxAOoT 7 | dwmlr/dNNu2MjyjcvTK0lCn6vexp6k8MaXTEsTvG0kBmVDZGuSXcKzbBoAZLxQIx 8 | AOPJU65HnDpcOM+qLH3jahTnbrg4C0BO0mj1OusLcSUnA6bFP2NkZ9LyWfMerbvG 9 | 8wIxAI7Iyt8mo50+C5iCGj250OtiPdMRsdLJlPUdRCLHbLljAZPpF8t3/q66i929 10 | 5MiSZQIwE3wXQmMxw/Q7j9f4slQPsPYTDIMOw1N6wCup/I0gApORxmQ9Bd2C3BKL 11 | CzbmmZdtAjEA2v1fSN4DPcQW2bgmoE0GoNEMYGfSza7jBGOiKkqm4p2hAjaur174 12 | U2t9BPJHk+Xh 13 | -----END PRIVATE KEY----- 14 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_invalid_keytype.json: -------------------------------------------------------------------------------- 1 | { 2 | "keys": [ 3 | { 4 | "kty": "OKP", 5 | "crv": "Ed25519", 6 | "kid": "1", 7 | "x": "c2Rmc2Zk", 8 | "alg": "EdDSA" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_missing_kid.json: -------------------------------------------------------------------------------- 1 | { 2 | "keys": [ 3 | { 4 | "kty": "EC", 5 | "kid": "C6vs25welZOx6WksNYfbMfiw9l96pMnD", 6 | "crv": "P-256", 7 | "x": "ngLYQnlfF6GsojUwqtcEE3WgTNG2RUlsGhK73RNEl5k", 8 | "y": "tKbiDSUSsQ3F1P7wteeHNXIcU-cx6CgSbroeQrQHTLM" 9 | }, 10 | { 11 | "kty": "EC", 12 | "crv": "P-256", 13 | "x": "7MGOl06DP9df2u8oHY6lqYFIoQWzCj9UYlp-MFeEYeY", 14 | "y": "PSLLy5Pg0_kNGFFXq_eeq9kYcGDM3MPHJ6ncteNOr6w" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_valid_EC_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "keys": [ 3 | { 4 | "kty": "EC", 5 | "kid": "C6vs25welZOx6WksNYfbMfiw9l96pMnD", 6 | "crv": "P-256", 7 | "x": "ngLYQnlfF6GsojUwqtcEE3WgTNG2RUlsGhK73RNEl5k", 8 | "y": "tKbiDSUSsQ3F1P7wteeHNXIcU-cx6CgSbroeQrQHTLM" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_valid_RSA_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "keys": [ 3 | { 4 | "p": "4aCuWvrpN7CeAqmXUWoQHex9l0ePELKe9AzJ2jcnDPEqP_CO9jlL7V_ZlMFLXg8oSFMkx59woD3BCjdZqCDaaiWA8L3fFmcLXLj9LrTz0X_p_Tb1cPbCa3Z5MqtuhC5yg5Po8uuDOUrF4FdKcU_YtCYbFwsr4HfViakKlk64Qt8", 5 | "kty": "RSA", 6 | "q": "o7Enkd4yxw9tl-jHQXw38GjItzdbFLmkeppHSIzv02Zs5XS0CQyb-jFrUXStpO3UiNrRXm1YUCSUwRDj81ATilrIpPTTH6qC2Fu_E5eEEDx79W5p0oj3SbN2BQS5-MkhVvMrrDGkIAmRmLcE9SH-eIreYgB6XW0yrbHTwbUWV98", 7 | "d": "EpfAjuifddpKISal0znpNhlfVkRDyEdd6_CBlh6lLJdU8dflcqyWFhmJ8pEXsnwC4-DBkkDKt57HcIq3MQ8Q_IKlhLPexugNr3QJxbA1DCbXagsIvh4-QcBQp-4LOZv1T3T4-lywkRzX0qp9yjySIGkT6OmAfN97Q-_NPhOdlHJn5feQfJhxWIMaWhQnomjmMP40FApRdk-gOmNx-w2pLWHVjnibbfI9SijUKg4ZqW3MvwNbSuM-EPyetRuaJRW892h7kxGE2wV_oqGRbqasPqVJN9SiNAEWwzcs645NwPEC48XUK0Q4eUyd9ra_YFv9HGgZ1Yo0gaD0BnRMLxcm-Q", 8 | "e": "AQAB", 9 | "use": "sig", 10 | "kid": "14cc39cd-838d-426d-9bb1-77f3468fba96", 11 | "qi": "kly84cu6D3sy64HfRpXfIYuNZTcJxlfdkLcLY-ZHkY5oVUiDBb6VrKwkQq3e5UbxF0a6qzwCk_B0Bn6ChHXLorVLnlx8cUG2TP3C72J_iHpE8Bn3wend7ZqLfcceh19zPOe13352RurPXbewzf1tYVji-OPzJr05fUDrqmesXvI", 12 | "dp": "XTZnsbiFDvfNX6Y2mHDr9aDVBeGPTkOs_YAnCBrO7D7ZyI6WUUy8fHWjyxvMCjDS4IZQ5JOPEPRSQuk5BgeElGOoEE0w0-2AOS9Hkbs6G2vv_CdvYNrg2UqZqYA_aSZTMt6xV2JK1Sl59EO7wnJNQaeYe32nA9YeBwAqGoys_Rs", 13 | "dq": "GeTJLKLoh2KiZHhXJL3An5ADyC_CganIIfjLs-dPfLJkIXvvisrq1Y4BuvXpDgDtMOTkX7qOUMconM3OMUwGe0lXGfj8eLLhVdZViITcSDE5Lp7TsJEoBQmVbr_Lp1Yxpu56hxenLcY1uOGisCA7f9f_y6LluGewr5dEtwytRyM", 14 | "n": "kEVx_IUSr30PMuJTWWg4QYxhOWU80Zl_OKHnGp6PxLqD0wuOmLQv2a0xprzXMIxROl3iYyAY_PQmiLQn4aMR_c55V6N6O_qP5d09tArAipMsVyAICW0RRtJtAL-n9_ktghmYUaVHhq5AXS7flxO2b8KKZWZuKJ1f8clbK16eOJ_-NURjqz90zpuafIA1nzCdNk9AE1NOuXdW81FIjC-82abgDhgyNgNnL33z8BXbwKdog0Hu9BFoyIBXIA8HLzaj-KmyqTY9ewu2JVI8XVLuM_zy-6FmzKGanw0bhZRAFNHc-eraMP0HLJuWf0R9waiKmrPy0i6zISayo4_rm2YJQQ" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/jwtbundle/jwks_valid_RSA_EC.json: -------------------------------------------------------------------------------- 1 | { 2 | "keys": [ 3 | { 4 | "p": "4aCuWvrpN7CeAqmXUWoQHex9l0ePELKe9AzJ2jcnDPEqP_CO9jlL7V_ZlMFLXg8oSFMkx59woD3BCjdZqCDaaiWA8L3fFmcLXLj9LrTz0X_p_Tb1cPbCa3Z5MqtuhC5yg5Po8uuDOUrF4FdKcU_YtCYbFwsr4HfViakKlk64Qt8", 5 | "kty": "RSA", 6 | "q": "o7Enkd4yxw9tl-jHQXw38GjItzdbFLmkeppHSIzv02Zs5XS0CQyb-jFrUXStpO3UiNrRXm1YUCSUwRDj81ATilrIpPTTH6qC2Fu_E5eEEDx79W5p0oj3SbN2BQS5-MkhVvMrrDGkIAmRmLcE9SH-eIreYgB6XW0yrbHTwbUWV98", 7 | "d": "EpfAjuifddpKISal0znpNhlfVkRDyEdd6_CBlh6lLJdU8dflcqyWFhmJ8pEXsnwC4-DBkkDKt57HcIq3MQ8Q_IKlhLPexugNr3QJxbA1DCbXagsIvh4-QcBQp-4LOZv1T3T4-lywkRzX0qp9yjySIGkT6OmAfN97Q-_NPhOdlHJn5feQfJhxWIMaWhQnomjmMP40FApRdk-gOmNx-w2pLWHVjnibbfI9SijUKg4ZqW3MvwNbSuM-EPyetRuaJRW892h7kxGE2wV_oqGRbqasPqVJN9SiNAEWwzcs645NwPEC48XUK0Q4eUyd9ra_YFv9HGgZ1Yo0gaD0BnRMLxcm-Q", 8 | "e": "AQAB", 9 | "use": "sig", 10 | "kid": "14cc39cd-838d-426d-9bb1-77f3468fba96", 11 | "qi": "kly84cu6D3sy64HfRpXfIYuNZTcJxlfdkLcLY-ZHkY5oVUiDBb6VrKwkQq3e5UbxF0a6qzwCk_B0Bn6ChHXLorVLnlx8cUG2TP3C72J_iHpE8Bn3wend7ZqLfcceh19zPOe13352RurPXbewzf1tYVji-OPzJr05fUDrqmesXvI", 12 | "dp": "XTZnsbiFDvfNX6Y2mHDr9aDVBeGPTkOs_YAnCBrO7D7ZyI6WUUy8fHWjyxvMCjDS4IZQ5JOPEPRSQuk5BgeElGOoEE0w0-2AOS9Hkbs6G2vv_CdvYNrg2UqZqYA_aSZTMt6xV2JK1Sl59EO7wnJNQaeYe32nA9YeBwAqGoys_Rs", 13 | "dq": "GeTJLKLoh2KiZHhXJL3An5ADyC_CganIIfjLs-dPfLJkIXvvisrq1Y4BuvXpDgDtMOTkX7qOUMconM3OMUwGe0lXGfj8eLLhVdZViITcSDE5Lp7TsJEoBQmVbr_Lp1Yxpu56hxenLcY1uOGisCA7f9f_y6LluGewr5dEtwytRyM", 14 | "n": "kEVx_IUSr30PMuJTWWg4QYxhOWU80Zl_OKHnGp6PxLqD0wuOmLQv2a0xprzXMIxROl3iYyAY_PQmiLQn4aMR_c55V6N6O_qP5d09tArAipMsVyAICW0RRtJtAL-n9_ktghmYUaVHhq5AXS7flxO2b8KKZWZuKJ1f8clbK16eOJ_-NURjqz90zpuafIA1nzCdNk9AE1NOuXdW81FIjC-82abgDhgyNgNnL33z8BXbwKdog0Hu9BFoyIBXIA8HLzaj-KmyqTY9ewu2JVI8XVLuM_zy-6FmzKGanw0bhZRAFNHc-eraMP0HLJuWf0R9waiKmrPy0i6zISayo4_rm2YJQQ" 15 | }, 16 | { 17 | "kty": "EC", 18 | "kid": "C6vs25welZOx6WksNYfbMfiw9l96pMnD", 19 | "crv": "P-256", 20 | "x": "ngLYQnlfF6GsojUwqtcEE3WgTNG2RUlsGhK73RNEl5k", 21 | "y": "tKbiDSUSsQ3F1P7wteeHNXIcU-cx6CgSbroeQrQHTLM" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/spiffeid/spiffeIds.txt: -------------------------------------------------------------------------------- 1 | spiffe://example.org/workload1 2 | spiffe://example.org/workload2 3 | spiffe://example2.org/workload1 4 | spiffe://example.org/workload1 5 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/workloadapi/bundle.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spiffe/java-spiffe/c00a4d09b6abb59e7c51bc06ac1400deffc04ee4/java-spiffe-core/src/test/resources/testdata/workloadapi/bundle.der -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/workloadapi/bundle.json: -------------------------------------------------------------------------------- 1 | { 2 | "keys": [ 3 | { 4 | "use": "jwt-svid", 5 | "kty": "EC", 6 | "kid": "IZwJU1pKXnlj8RtsCSxTiW4p3PaS3LA0", 7 | "crv": "P-256", 8 | "x": "mjBLEiXTajKogbCbRrsCTO5ztSPbPbDlCEj9U7UyCik", 9 | "y": "UqtfGGr8ILjW_r_XhZg2pixq1ZhsY0KeN45smdlJ4Ag" 10 | }, 11 | { 12 | "use": "jwt-svid", 13 | "kty": "EC", 14 | "kid": "thuC5aKXK0xEijJBImdcNE8GRneaQF8a", 15 | "crv": "P-256", 16 | "x": "xaOEUedrdEjVxhT5sJ7lwNgsvEfBKNgfYfyNjXyAyys", 17 | "y": "p0ZWrr9ZFx3tjvBHxScidaqzeNJwwvQV-f8gi6qIGB4" 18 | }, 19 | { 20 | "use": "jwt-svid", 21 | "kty": "EC", 22 | "kid": "lD6u4YqXBvqZkJv9IkakyjTPMOlmYdho", 23 | "crv": "P-256", 24 | "x": "e1b14XBkpFRG5aALyveYz0g1Gql_zT_Mxyx_4mWIyyY", 25 | "y": "K-7zKhCJReI9Mv-VqxIv9CV6cMj20qkT29H35uTtrTM" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/workloadapi/corrupted: -------------------------------------------------------------------------------- 1 | not-pem not-der -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/workloadapi/federated-bundle.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBzDCCAVOgAwIBAgIJAJM4DhRH0vmuMAoGCCqGSM49BAMEMB4xCzAJBgNVBAYT 3 | AlVTMQ8wDQYDVQQKDAZTUElGRkUwHhcNMTgwNTEzMTkzMzQ3WhcNMjMwNTEyMTkz 4 | MzQ3WjAeMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMHYwEAYHKoZIzj0C 5 | AQYFK4EEACIDYgAEWjB+nSGSxIYiznb84xu5WGDZj80nL7W1c3zf48Why0ma7Y7m 6 | CBKzfQkrgDguI4j0Z+0/tDH/r8gtOtLLrIpuMwWHoe4vbVBFte1vj6Xt6WeE8lXw 7 | cCvLs/mcmvPqVK9jo10wWzAdBgNVHQ4EFgQUh6XzV6LwNazA+GTEVOdu07o5yOgw 8 | DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwGQYDVR0RBBIwEIYOc3Bp 9 | ZmZlOi8vbG9jYWwwCgYIKoZIzj0EAwQDZwAwZAIwE4Me13qMC9i6Fkx0h26y09QZ 10 | IbuRqA9puLg9AeeAAyo5tBzRl1YL0KNEp02VKSYJAjBdeJvqjJ9wW55OGj1JQwDF 11 | D7kWeEB6oMlwPbI/5hEY3azJi16I0uN1JSYTSWGSqWc= 12 | -----END CERTIFICATE----- 13 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/workloadapi/svid.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spiffe/java-spiffe/c00a4d09b6abb59e7c51bc06ac1400deffc04ee4/java-spiffe-core/src/test/resources/testdata/workloadapi/svid.der -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/workloadapi/svid.key.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spiffe/java-spiffe/c00a4d09b6abb59e7c51bc06ac1400deffc04ee4/java-spiffe-core/src/test/resources/testdata/workloadapi/svid.key.der -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509bundle/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBXzCB6gIJANXCDoURTF5MMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNVBAMMDFBF 3 | TVVUSUxURVNUMTAeFw0xODA3MTYyMzU5NTZaFw00NTEyMDEyMzU5NTZaMBcxFTAT 4 | BgNVBAMMDFBFTVVUSUxURVNUMTB8MA0GCSqGSIb3DQEBAQUAA2sAMGgCYQDMfDxC 5 | DcBTMAjrmo+yNBuYjavI47dPGPrqIXzfAx7L6M2Bg1ZYDaO8xXgc0+7aZZRg7Fe1 6 | Gt0EJEourKA6qN0z4gTU5KWZrPLPwPHU75F90jgThdkmHdO7j3lr2MPjsvUCAwEA 7 | ATANBgkqhkiG9w0BAQsFAANhAEsa1QiHgPwW0V4VLtRk7xyKIyCo+D0rgQA1qLmW 8 | 69aMW12GE+sxGo7INDP2bdQGB/udG5V6FnWNTP89VwakKjU4l6LoqtUtncwoGNgT 9 | U2aPnxQpNXW7pWdBVSIBhSnptw== 10 | -----END CERTIFICATE----- 11 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509bundle/certs.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBXzCB6gIJANXCDoURTF5MMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNVBAMMDFBF 3 | TVVUSUxURVNUMTAeFw0xODA3MTYyMzU5NTZaFw00NTEyMDEyMzU5NTZaMBcxFTAT 4 | BgNVBAMMDFBFTVVUSUxURVNUMTB8MA0GCSqGSIb3DQEBAQUAA2sAMGgCYQDMfDxC 5 | DcBTMAjrmo+yNBuYjavI47dPGPrqIXzfAx7L6M2Bg1ZYDaO8xXgc0+7aZZRg7Fe1 6 | Gt0EJEourKA6qN0z4gTU5KWZrPLPwPHU75F90jgThdkmHdO7j3lr2MPjsvUCAwEA 7 | ATANBgkqhkiG9w0BAQsFAANhAEsa1QiHgPwW0V4VLtRk7xyKIyCo+D0rgQA1qLmW 8 | 69aMW12GE+sxGo7INDP2bdQGB/udG5V6FnWNTP89VwakKjU4l6LoqtUtncwoGNgT 9 | U2aPnxQpNXW7pWdBVSIBhSnptw== 10 | -----END CERTIFICATE----- 11 | -----BEGIN CERTIFICATE----- 12 | MIIBXzCB6gIJAMbKbzUVGQTBMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNVBAMMDFBF 13 | TVVUSUxURVNUMjAeFw0xODA3MTYyMzU5NDNaFw00NTEyMDEyMzU5NDNaMBcxFTAT 14 | BgNVBAMMDFBFTVVUSUxURVNUMjB8MA0GCSqGSIb3DQEBAQUAA2sAMGgCYQCuUQFO 15 | blDXlrJF45Hn86Mb+UAjwnECaaG9Uj7oldNwEwCimhbCQsDYTRzlAFRbdm+S6Lri 16 | 0KbhKsqDz2V4n3scLnigsLU9pLGGtAF2W/pONUIEBOwsNVL8qGW1oy6A3V0CAwEA 17 | ATANBgkqhkiG9w0BAQsFAANhACjrgsP630Mgyj7LDcyV9/tIr+f3ghjyVIyedFQo 18 | MJ0if+4o9MKN/7ius4hvI+L6M9aXGyFp/JlRK4p5upqiG6J/vrG3TNPjZMD5wen8 19 | /oMJ7lk8yNVYR9zZQgfVzUPlcA== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509bundle/corrupted.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBXzCB6gIJANXCDoURTF5MMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNVBAMMDFBF 3 | TVVUSUxURVNUMTAeFw0xODA3MTYyMzU5NTZaFw00NTEyMDEyMzU5NTZaMBcxFTAT 4 | BgNVBAMMDFBFTVVUSUxURVNUMTB8MA0GCSqGSIb3DQEBAQUAA2sAMGgCYQDMfDxC 5 | DcBTMAjrmo+yNBuYjavI47dPGPrqIXzfAx7L6M2Bg1ZYDaO8xXgc0+7aZZRg7Fe1 6 | Gt0EJEourKA6qN0z4gTU5KWZrPLPwPHU75F90jgThdkmHdO7j3lr2MPjsvUCAwEA 7 | ATANBgkqhkiG9w0BAQsFAANhAEsa1QiHgPwW0V4VLtRk7xyKIyCo+D0rgQA1qLmW 8 | 69aMW12GE+sxGo7INDP2bdQGB/udG5V6FnWNTP89VwakKjU4l6LoqtUtncwoGNgT 9 | 69aMW12GE+sxGo7INDP2bdQGB/udG5V6FnWNTP89VwakKjU4l6LoqtUtncwoGNgT 10 | U2aPnxQpNXW7pWdBVSIBhSnptw== 11 | -----END CERTIFICATE----- 12 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509bundle/empty.pem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spiffe/java-spiffe/c00a4d09b6abb59e7c51bc06ac1400deffc04ee4/java-spiffe-core/src/test/resources/testdata/x509bundle/empty.pem -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509bundle/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIB5QIBADANBgkqhkiG9w0BAQEFAASCAc8wggHLAgEAAmEA0EdYWa7LZ/ZA2sKk 3 | JsREA2TkXDIq01JIRsVetPFRqRW5GEmkwwk0qThebp/ofZt8oLkMwp06BLaQC3tH 4 | QGkK31jh5Djd1NHA8CpU4ByObgGeY75dhwFEzI4YwXM+e0n/AgMBAAECYFD4S4qh 5 | /4WtIE1refFwP5iqMnT9M9TvmhWZSVZCsqJvRYQBrUH9ZDGdLmkHVZTvSvKKmkoZ 6 | VvXDlpmW4Eaed8xXqsLYplMrVo6WkvdtvlvfIwP69PGFmWwKgFBe2aLHsQIxAOoT 7 | dwmlr/dNNu2MjyjcvTK0lCn6vexp6k8MaXTEsTvG0kBmVDZGuSXcKzbBoAZLxQIx 8 | AOPJU65HnDpcOM+qLH3jahTnbrg4C0BO0mj1OusLcSUnA6bFP2NkZ9LyWfMerbvG 9 | 8wIxAI7Iyt8mo50+C5iCGj250OtiPdMRsdLJlPUdRCLHbLljAZPpF8t3/q66i929 10 | 5MiSZQIwE3wXQmMxw/Q7j9f4slQPsPYTDIMOw1N6wCup/I0gApORxmQ9Bd2C3BKL 11 | CzbmmZdtAjEA2v1fSN4DPcQW2bgmoE0GoNEMYGfSza7jBGOiKkqm4p2hAjaur174 12 | U2t9BPJHk+Xh 13 | -----END PRIVATE KEY----- 14 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509bundle/not-pem.pem: -------------------------------------------------------------------------------- 1 | Not PEM encoded file -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509svid/cert.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spiffe/java-spiffe/c00a4d09b6abb59e7c51bc06ac1400deffc04ee4/java-spiffe-core/src/test/resources/testdata/x509svid/cert.der -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509svid/corrupted: -------------------------------------------------------------------------------- 1 | not-pem not-der -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509svid/good-leaf-and-intermediate.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICBzCCAY2gAwIBAgIRAJ4TY883AKQzW4gEzxTP5ekwCgYIKoZIzj0EAwMwHjEL 3 | MAkGA1UEBhMCVVMxDzANBgNVBAoTBlNQSUZGRTAeFw0yMDAzMjQxNDA3MzBaFw0y 4 | MDAzMjQxNTA3NDBaMB0xCzAJBgNVBAYTAlVTMQ4wDAYDVQQKEwVTUElSRTBZMBMG 5 | ByqGSM49AgEGCCqGSM49AwEHA0IABI6NiQ4HU4ZS8koPLevFZOzNPJRBGmsr6CMj 6 | qww2LVQDxF2/QiJUtVf6yPhtXYI/uWh8yBvRNxLfMmscAYf1gBOjgawwgakwDgYD 7 | VR0PAQH/BAQDAgOoMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNV 8 | HRMBAf8EAjAAMB0GA1UdDgQWBBSyUx1gdahNKPD13hnr5SJq7QdHqzAfBgNVHSME 9 | GDAWgBR1/vyENH1J5W0G0zexqR4Q2UVIGjAqBgNVHREEIzAhhh9zcGlmZmU6Ly9l 10 | eGFtcGxlLm9yZy93b3JrbG9hZC0xMAoGCCqGSM49BAMDA2gAMGUCMQDsFcGtMDZi 11 | w2aypdxvr1tvN/Opahi4zJ3DIlfAIhSNQ8gDp7LS7u06Ob/6ouh/1c4CMEpVoyS4 12 | ZTnENACY3TXXmRt/mZsXyyHSgQGyEFqmvehpsDLIAL2+nKLfcyzENZn4Rg== 13 | -----END CERTIFICATE----- 14 | -----BEGIN CERTIFICATE----- 15 | MIIB/DCCAYOgAwIBAgIQBRQ/CSzrgkGFpQ7mVSr7yjAKBggqhkjOPQQDAzAeMQsw 16 | CQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMB4XDTIwMDMyNDE0MDUwM1oXDTIw 17 | MDMyNTE0MDUxM1owHjELMAkGA1UEBhMCVVMxDzANBgNVBAoTBlNQSUZGRTB2MBAG 18 | ByqGSM49AgEGBSuBBAAiA2IABExZXvtfcorJWkVs8pdriln6Y5aewz+r0ibFXdKt 19 | lHOg31MQsnZkh3wlOxuVwwyuuTlpb8LwIyOhuYbb6lbWHDDhSHXh3ye021PifZGc 20 | X8pHXRQk6D8SP1+260sOmCTI1qOBhTCBgjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0T 21 | AQH/BAUwAwEB/zAdBgNVHQ4EFgQUdf78hDR9SeVtBtM3sakeENlFSBowHwYDVR0j 22 | BBgwFoAUh6XzV6LwNazA+GTEVOdu07o5yOgwHwYDVR0RBBgwFoYUc3BpZmZlOi8v 23 | ZXhhbXBsZS5vcmcwCgYIKoZIzj0EAwMDZwAwZAIwApQtPxHc1mx+aOES1D0RFttH 24 | IyVP9szoPz1wjSFXnxA8zY8ikVGx6FdviaHe4RXaAjBA3tN4AJf/yBzJU7cStXR8 25 | Y8l67Q87PUKX5SyAdGgoqVX2V+i/kH1ZFt0BikmRWtA= 26 | -----END CERTIFICATE----- 27 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509svid/good-leaf-only.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDKjCCAhKgAwIBAgIJAPKy5MTuxkq7MA0GCSqGSIb3DQEBDQUAMB4xCzAJBgNV 3 | BAYTAlVTMQ8wDQYDVQQKDAZTUElGRkUwHhcNMjAwMzMwMTkwMDM3WhcNMjUwMzI5 4 | MTkwMDM3WjAeMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMIIBIjANBgkq 5 | hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvudksUDCwrAb5Ag5zbRaMv0OWcehfN/1 6 | ejrDJUu0gmnYGOrs0xdMPCIacHkOEm0nSJ2b35REBbw13FTCHHaFH9tC0EZrz+Fk 7 | mhoP2HTcGRsiibfBvRpyd346sP6JqocpTNljhQxbhKCy4WkAS/KKjrD8NdkQ8zdM 8 | h8vBzEkzDwjbjbn3rWgvHcBOU3Zu2mn5/LP4B8oXlGM4n7ZMWK35s+yXKzmwkltp 9 | QtVVTmtkCmTLfckBsSgBESnRgIjY/jfwJqrugvPdmx0ToINljiahz0Uci3hRLhYc 10 | 3zAsQJoFXaNrm6qFCtOR/xHK/RCKY9bHKEvavVx+F2Wvx3MphddztQIDAQABo2sw 11 | aTAdBgNVHQ4EFgQUy4VolgQJBL0rLYyI/JQHlBB0whEwDAYDVR0TAQH/BAIwADAO 12 | BgNVHQ8BAf8EBAMCB4AwKgYDVR0RBCMwIYYfc3BpZmZlOi8vZXhhbXBsZS5vcmcv 13 | d29ya2xvYWQtMTANBgkqhkiG9w0BAQ0FAAOCAQEAsgNGb0lDyvPYsOMtNCZxpEQh 14 | WDM4nPc/fWmRQv2GE24VPDxTa+KF8QASuK4SK3aHgixIAUiugOpFr5ZuyX7nWhcb 15 | 5lPXOfuAB7SL5rq9E4kaikhNiJOCDxMORNZmpe9fii+l4clrmqIaryDHOhCGZFvr 16 | ppPOxLFPY1pnFBUtEjrM8TnpVMdV6jB4im7lKfOse/1BhzRQQ/dJg4EFk1S9pHse 17 | ryc0wJMM685dgAmELRigWx2IpU7Ma/09WlcCG7AmToeXPcEOa0xFS3CNEat8+bv+ 18 | BaWSJ31+se8tDTjjEfV3M23OdUmyv29aTb74wd33u6+jsliFK5psZKbcg3hGCA== 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509svid/key-ecdsa-other.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg0qLRTkQJHk3tEetA 3 | RWnvP1YPit0RSNcXAOrUeQxjkPWhRANCAATBv8BVtGzfQZ/NNqxoTfPlabccu6a3 4 | xCl1CpS1OAepfamJIDGBoHupcn8N/6xwBd4Ydws/wM5nZwqPSm73OCPa 5 | -----END PRIVATE KEY----- -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509svid/key-pkcs8-ecdsa.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgmgpKax+BCcPM3gcZ 3 | 1zvl2zquf76KwRhQWfQJQTKhGfKhRANCAASOjYkOB1OGUvJKDy3rxWTszTyUQRpr 4 | K+gjI6sMNi1UA8Rdv0IiVLVX+sj4bV2CP7lofMgb0TcS3zJrHAGH9YAT 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509svid/key-pkcs8-rsa.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC+52SxQMLCsBvk 3 | CDnNtFoy/Q5Zx6F83/V6OsMlS7SCadgY6uzTF0w8IhpweQ4SbSdInZvflEQFvDXc 4 | VMIcdoUf20LQRmvP4WSaGg/YdNwZGyKJt8G9GnJ3fjqw/omqhylM2WOFDFuEoLLh 5 | aQBL8oqOsPw12RDzN0yHy8HMSTMPCNuNufetaC8dwE5Tdm7aafn8s/gHyheUYzif 6 | tkxYrfmz7JcrObCSW2lC1VVOa2QKZMt9yQGxKAERKdGAiNj+N/Amqu6C892bHROg 7 | g2WOJqHPRRyLeFEuFhzfMCxAmgVdo2ubqoUK05H/Ecr9EIpj1scoS9q9XH4XZa/H 8 | cymF13O1AgMBAAECggEAKb363G8mpt9PGetaiEoZNvDyRFtMSjvGNzXGGc+V5rYy 9 | FDC6G+YKO1PRqCowE4NehZhAzwBiZ0aiGE1ILGUV8sNEtrnPNSM5liCAunnC7pJq 10 | WOafLDQuN0aYmr7tZyYqt60I/7yV/kgNFRCaigC8nzq/yx5tgGDlEQRSmdKfoXeS 11 | 2mmHtpB06bMDU+cptfsG1S/eChVAngJcKAYdIukud5GGgsYp3TiXxM/IZ2wapclT 12 | T4ATAERvysT58sYR4sCGpphFzsAkjd6oFZoADSw58bKrq8Afb/txjHdXwhV+crPu 13 | BDbozuk5F2r9U/cGtpTI3Ay1T+VHOHkKuUJCYz97gQKBgQD72Hr+DxpBkn4cgPGx 14 | jBe7/Uixs5+cQL44lqIEOd6uLLE+xrPOZgpuOqz7FPVcAnBgh+knf1Kz5SgQwrTU 15 | 22SlYPsS2+D3Rcnp5UMfqfwFq1/X9UGF62l3VHwgs40iP4k28H9Y4GYeXRNIigyu 16 | CDImn1FU6cJx1w1aUnYrIoEDMQKBgQDCDY/T/mm8DmLeV6wxAzSJYfKhgrvZ/Jc0 17 | qGQdp9yGpZDqV1zRtoJE7jno1YRt01ye0xXLZYsgYbG04kWNSTDy9yfLPBDN28m/ 18 | yiYNz6ckYI2HNts8BSIGjF3cuF4XGaEkaMRlnx5iAb9BZ6692itWGJ4skGuUdKXh 19 | cFNt1hovxQKBgQDz6QW1aQsU1rws/vUV74hNGVF+SWkb+9g/FiRz01hHeCFxmyAp 20 | lcokV8+QnQvEwNf1pau6BSYj9JyJHwnj3Vfsk7CW4z1OPtj/HnuT/x2GoCODFJUR 21 | Dp5mY/yT71GRCdfqzaGIgkxku+AhMRJ2uplXrpUml/8qIg1cnOC1hgVNAQKBgDQs 22 | 21AfpqRGpfSsVAL1nqmVqVwdv45z6N/iqtCCcrvNRnKLvMwyK0KHXxCoYCv7WXrm 23 | vIRssASr39EHybWcSUn6hDuT0dzXzJ4Bp0utWn5ga41AhZ/UrXpfQVl4ROwnGvmk 24 | JbJBHzUwzRCz5Prs7xv+EIFg71wCJRvBTN1KZM4VAoGBAK7ud6KNqLtesyFQxmlA 25 | ccHLqIFDVZy30nghXEHGIhUwEXstA6wb0R563svaBVFd+q5SAtfRKPoWNz+yxnUn 26 | r9DvXKB/A2jFzmZ5HKQoc6YD5U+jIwWX9HBw/UK1pNIemIbnhkJCbqaC09nZ03nM 27 | 7OCe8CKM5vdVTa2hHowYf3ci 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509svid/key-rsa-other.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDLGXezXK6MT3Ay 3 | AMh9gQqUhpuZoaQKVKEqAgEb9MQ0ObTNBkIgOWZRcpMasmuPSG4UCHirwhSzFT3r 4 | Mp2BszqtqgcRWyuB1zD3orbcYpv9lWIogaoG88UfUIBJn+0WnlMsdCUEVs5c0iSp 5 | 5VgrwmIzxYwSffQKe/CTkjVG9L3TNPV+B4b/HLEuTbfetB00kX/Upf+hFkxY1eAD 6 | jUonlFZaFbOHk7o1tJI8HRWvOWRfrB7vPwoLRaSwVIY/Vxte4u1IxJGehwRFwX4C 7 | s2FAyPRUoWaZT/qZ5P6tSfp1+DyhcrD4q0Kfu5z+z0afFYlQ5A26NsZKSLrzYv81 8 | PVgKJygPAgMBAAECggEAcoR8qwTSYskT8Q3h/mo5Zf/0Uo8C6NRJP1978qBz1SLI 9 | oGYr+PGEkN4ytfXukg78Co2wROCdPVD35dtXVl+R8YQSkiW/BWop8SetRdzQKg8G 10 | FuLt1Re0iXChtWHYlE1XhpRCnwy3NT1EcQuhSKNFLf3weijM5vB1eTASnFy5gBG2 11 | SFbqqL8ijnaghWcF+GYQa17S5Iio/JJWtTud45/pifA392NEyEoVvd+SdnxE8xGd 12 | Y2189MImphHYDVKOOyNn6pzHAOv0wJqS5rpp6yjH6HtuiUGV+u8gEuTmkK/KkxjZ 13 | 4NQTfSr9upgFTWqffLAI8D0QjYpAvg2S7iqH/quFuQKBgQDlpZZYun4LTT3XEpW3 14 | AFcWhOVxz62S+NA/oV8/2K5lTDSRrFrR9o56Jwif6HMtRFeB/dZroVwE856eTYKS 15 | QnhlR2AgglfoZox8+Usgd/P5zZedrCtMGeD+/tjzuim36rU7fXiLfXjIYIWHbdoC 16 | 2xFivJ2kUdkCtW2JgzZ/eoMnDQKBgQDiZ/1weL7MIwUCNaNTYqoYKgWpv4LNqi5T 17 | LVN2hHqDKS0jDPYxLYaOL7b+8eTuBFjtBSyfQdeIartA3iFonX0uKOOvl+Dz3gxA 18 | b+hXlGQlej9O7KhSDY/iGZQwL12S8bLtz56ei7DQ0yX3ROhi57CybdkVe4YoGEig 19 | i4+TxHfEiwKBgQDXpsrr4Z1RsaRb3pj3M/uUKxJn8RVXig3NYxXWF/2VJJ3NGmRi 20 | /TzImvUvbRMTtDTU3nFHu0x1Xe1bTQBLpt32hbNQc/ZnSD2vck9JWzuiHp29NBuo 21 | qDiwXNOQ9QYOLvsntWjI3Pfae0GJbvAmdF/8L5F96Fp4f7UwSCaaSYr11QKBgQCk 22 | tNw2P1Emdu+E3PqVhfiSHbgC9pS1A/Ixho7qcJQj6QeyMRLFUgZKWEH7lIRj93ut 23 | 6o68yQyAbI0PMsoBLllMBGknbAk1wDsPgZn4ZLa6YQW/aAFHuCANb2hT6ZFcfkMa 24 | KGmPk7YtJP7AtTuOAFmWZL2BmFxPvOLRENaBvHMY4wKBgBmzwRPIMuNY1h1OXL4+ 25 | IwGUlqUoP+VbOjRgKIvnNlPeaMjhN6d51DIOdWsBrYmWDwPdb8725NhaSNfyoOlQ 26 | O765/exSw1UXb/HJkcUPy4fWBWH8OvEjnBALhf19QmvnwvaLKgx3t5yaX99BaWv6 27 | SKBQvAJLuL0Y/+QqGfQK89EP 28 | -----END PRIVATE KEY----- -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509svid/keyEC.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spiffe/java-spiffe/c00a4d09b6abb59e7c51bc06ac1400deffc04ee4/java-spiffe-core/src/test/resources/testdata/x509svid/keyEC.der -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509svid/wrong-intermediate-no-ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDKjCCAhKgAwIBAgIJAPKy5MTuxkq7MA0GCSqGSIb3DQEBDQUAMB4xCzAJBgNV 3 | BAYTAlVTMQ8wDQYDVQQKDAZTUElGRkUwHhcNMjAwMzMwMTkwMDM3WhcNMjUwMzI5 4 | MTkwMDM3WjAeMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMIIBIjANBgkq 5 | hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvudksUDCwrAb5Ag5zbRaMv0OWcehfN/1 6 | ejrDJUu0gmnYGOrs0xdMPCIacHkOEm0nSJ2b35REBbw13FTCHHaFH9tC0EZrz+Fk 7 | mhoP2HTcGRsiibfBvRpyd346sP6JqocpTNljhQxbhKCy4WkAS/KKjrD8NdkQ8zdM 8 | h8vBzEkzDwjbjbn3rWgvHcBOU3Zu2mn5/LP4B8oXlGM4n7ZMWK35s+yXKzmwkltp 9 | QtVVTmtkCmTLfckBsSgBESnRgIjY/jfwJqrugvPdmx0ToINljiahz0Uci3hRLhYc 10 | 3zAsQJoFXaNrm6qFCtOR/xHK/RCKY9bHKEvavVx+F2Wvx3MphddztQIDAQABo2sw 11 | aTAdBgNVHQ4EFgQUy4VolgQJBL0rLYyI/JQHlBB0whEwDAYDVR0TAQH/BAIwADAO 12 | BgNVHQ8BAf8EBAMCB4AwKgYDVR0RBCMwIYYfc3BpZmZlOi8vZXhhbXBsZS5vcmcv 13 | d29ya2xvYWQtMTANBgkqhkiG9w0BAQ0FAAOCAQEAsgNGb0lDyvPYsOMtNCZxpEQh 14 | WDM4nPc/fWmRQv2GE24VPDxTa+KF8QASuK4SK3aHgixIAUiugOpFr5ZuyX7nWhcb 15 | 5lPXOfuAB7SL5rq9E4kaikhNiJOCDxMORNZmpe9fii+l4clrmqIaryDHOhCGZFvr 16 | ppPOxLFPY1pnFBUtEjrM8TnpVMdV6jB4im7lKfOse/1BhzRQQ/dJg4EFk1S9pHse 17 | ryc0wJMM685dgAmELRigWx2IpU7Ma/09WlcCG7AmToeXPcEOa0xFS3CNEat8+bv+ 18 | BaWSJ31+se8tDTjjEfV3M23OdUmyv29aTb74wd33u6+jsliFK5psZKbcg3hGCA== 19 | -----END CERTIFICATE----- 20 | -----BEGIN CERTIFICATE----- 21 | MIIDHzCCAgegAwIBAgIJAKs91lAuGzzQMA0GCSqGSIb3DQEBDQUAMB4xCzAJBgNV 22 | BAYTAlVTMQ8wDQYDVQQKDAZTUElGRkUwHhcNMjAwMzMxMTUxMjU3WhcNMjUwMzMw 23 | MTUxMjU3WjAeMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMIIBIjANBgkq 24 | hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvudksUDCwrAb5Ag5zbRaMv0OWcehfN/1 25 | ejrDJUu0gmnYGOrs0xdMPCIacHkOEm0nSJ2b35REBbw13FTCHHaFH9tC0EZrz+Fk 26 | mhoP2HTcGRsiibfBvRpyd346sP6JqocpTNljhQxbhKCy4WkAS/KKjrD8NdkQ8zdM 27 | h8vBzEkzDwjbjbn3rWgvHcBOU3Zu2mn5/LP4B8oXlGM4n7ZMWK35s+yXKzmwkltp 28 | QtVVTmtkCmTLfckBsSgBESnRgIjY/jfwJqrugvPdmx0ToINljiahz0Uci3hRLhYc 29 | 3zAsQJoFXaNrm6qFCtOR/xHK/RCKY9bHKEvavVx+F2Wvx3MphddztQIDAQABo2Aw 30 | XjAdBgNVHQ4EFgQUy4VolgQJBL0rLYyI/JQHlBB0whEwDAYDVR0TAQH/BAIwADAO 31 | BgNVHQ8BAf8EBAMCAQYwHwYDVR0RBBgwFoYUc3BpZmZlOi8vZXhhbXBsZS5vcmcw 32 | DQYJKoZIhvcNAQENBQADggEBAHO2gZiRBlhbCsBSbpaAujVauM4IHhEtoZP1b+cu 33 | 22bAm21PTC3T4XAfDj286PrdOYL91FNmbpvR2hL+MrMKH+SBFQCnvNE8FYnMkNRx 34 | ysJItGTHdB2yXlBZbpZeRoOCL5oiS5vpQebHU6+AD6hsyM+9rSu4z6gwM2xFLQYQ 35 | Dc2IIV8LoB6s/9BB5rLcjVkjdhOR7spsAgFdK6ZYU0K8FoEPvBeEY7CORzenAbhI 36 | 8ExVH/3aNB2dfhIu4gFVGVnBh+UQ43YPR6Qs/ON5CS91xpUI0U0tJZgqMfD6kn3A 37 | 2U9PgiZyKoKrt+AyLe9OTg1Kb6QoxEFOeCljNp5+J+YPVjU= 38 | -----END CERTIFICATE----- 39 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509svid/wrong-intermediate-no-key-cert-sign.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDKjCCAhKgAwIBAgIJAPKy5MTuxkq7MA0GCSqGSIb3DQEBDQUAMB4xCzAJBgNV 3 | BAYTAlVTMQ8wDQYDVQQKDAZTUElGRkUwHhcNMjAwMzMwMTkwMDM3WhcNMjUwMzI5 4 | MTkwMDM3WjAeMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMIIBIjANBgkq 5 | hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvudksUDCwrAb5Ag5zbRaMv0OWcehfN/1 6 | ejrDJUu0gmnYGOrs0xdMPCIacHkOEm0nSJ2b35REBbw13FTCHHaFH9tC0EZrz+Fk 7 | mhoP2HTcGRsiibfBvRpyd346sP6JqocpTNljhQxbhKCy4WkAS/KKjrD8NdkQ8zdM 8 | h8vBzEkzDwjbjbn3rWgvHcBOU3Zu2mn5/LP4B8oXlGM4n7ZMWK35s+yXKzmwkltp 9 | QtVVTmtkCmTLfckBsSgBESnRgIjY/jfwJqrugvPdmx0ToINljiahz0Uci3hRLhYc 10 | 3zAsQJoFXaNrm6qFCtOR/xHK/RCKY9bHKEvavVx+F2Wvx3MphddztQIDAQABo2sw 11 | aTAdBgNVHQ4EFgQUy4VolgQJBL0rLYyI/JQHlBB0whEwDAYDVR0TAQH/BAIwADAO 12 | BgNVHQ8BAf8EBAMCB4AwKgYDVR0RBCMwIYYfc3BpZmZlOi8vZXhhbXBsZS5vcmcv 13 | d29ya2xvYWQtMTANBgkqhkiG9w0BAQ0FAAOCAQEAsgNGb0lDyvPYsOMtNCZxpEQh 14 | WDM4nPc/fWmRQv2GE24VPDxTa+KF8QASuK4SK3aHgixIAUiugOpFr5ZuyX7nWhcb 15 | 5lPXOfuAB7SL5rq9E4kaikhNiJOCDxMORNZmpe9fii+l4clrmqIaryDHOhCGZFvr 16 | ppPOxLFPY1pnFBUtEjrM8TnpVMdV6jB4im7lKfOse/1BhzRQQ/dJg4EFk1S9pHse 17 | ryc0wJMM685dgAmELRigWx2IpU7Ma/09WlcCG7AmToeXPcEOa0xFS3CNEat8+bv+ 18 | BaWSJ31+se8tDTjjEfV3M23OdUmyv29aTb74wd33u6+jsliFK5psZKbcg3hGCA== 19 | -----END CERTIFICATE----- 20 | -----BEGIN CERTIFICATE----- 21 | MIIDIjCCAgqgAwIBAgIJAIzsgdc6VayiMA0GCSqGSIb3DQEBDQUAMB4xCzAJBgNV 22 | BAYTAlVTMQ8wDQYDVQQKDAZTUElGRkUwHhcNMjAwMzMxMTUxNjU0WhcNMjUwMzMw 23 | MTUxNjU0WjAeMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMIIBIjANBgkq 24 | hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvudksUDCwrAb5Ag5zbRaMv0OWcehfN/1 25 | ejrDJUu0gmnYGOrs0xdMPCIacHkOEm0nSJ2b35REBbw13FTCHHaFH9tC0EZrz+Fk 26 | mhoP2HTcGRsiibfBvRpyd346sP6JqocpTNljhQxbhKCy4WkAS/KKjrD8NdkQ8zdM 27 | h8vBzEkzDwjbjbn3rWgvHcBOU3Zu2mn5/LP4B8oXlGM4n7ZMWK35s+yXKzmwkltp 28 | QtVVTmtkCmTLfckBsSgBESnRgIjY/jfwJqrugvPdmx0ToINljiahz0Uci3hRLhYc 29 | 3zAsQJoFXaNrm6qFCtOR/xHK/RCKY9bHKEvavVx+F2Wvx3MphddztQIDAQABo2Mw 30 | YTAdBgNVHQ4EFgQUy4VolgQJBL0rLYyI/JQHlBB0whEwDwYDVR0TAQH/BAUwAwEB 31 | /zAOBgNVHQ8BAf8EBAMCAQIwHwYDVR0RBBgwFoYUc3BpZmZlOi8vZXhhbXBsZS5v 32 | cmcwDQYJKoZIhvcNAQENBQADggEBAHzixgdMjrXVEtBfLNTdLrdiBLjlNRcmLbgq 33 | 6yBZInw4UieIhj5wnkYlFiZAt3l+v4BxHHBVpW0FFEgXJSUBcyHSiqIY4myiNpGW 34 | GsU7rsy3XdmM37y8vc69lhGpaKjDqyN5NWBaPS1N6ZXZAfrCgbzzA20lo9Kebo0/ 35 | rItx6r+Q7TQbIdPUwWv5vIms25JODwtmiXxZ3GTOdc79pb396HO0azm4mEPSeadk 36 | dYrgeviEsManUlhlGfKREONAMkY1DG5cRoyWLFKSvgFtGu24qLsg2CSReM4nFhcf 37 | T8+sSz+RFBwJkb1pfra4IGdIrJtWAQY9TYnVzeV3xroqtQDbeS4= 38 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509svid/wrong-leaf-ca-true.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDLTCCAhWgAwIBAgIJAIATiUHEG6XPMA0GCSqGSIb3DQEBDQUAMB4xCzAJBgNV 3 | BAYTAlVTMQ8wDQYDVQQKDAZTUElGRkUwHhcNMjAwMzMwMTg1MDAzWhcNMjUwMzI5 4 | MTg1MDAzWjAeMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMIIBIjANBgkq 5 | hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvudksUDCwrAb5Ag5zbRaMv0OWcehfN/1 6 | ejrDJUu0gmnYGOrs0xdMPCIacHkOEm0nSJ2b35REBbw13FTCHHaFH9tC0EZrz+Fk 7 | mhoP2HTcGRsiibfBvRpyd346sP6JqocpTNljhQxbhKCy4WkAS/KKjrD8NdkQ8zdM 8 | h8vBzEkzDwjbjbn3rWgvHcBOU3Zu2mn5/LP4B8oXlGM4n7ZMWK35s+yXKzmwkltp 9 | QtVVTmtkCmTLfckBsSgBESnRgIjY/jfwJqrugvPdmx0ToINljiahz0Uci3hRLhYc 10 | 3zAsQJoFXaNrm6qFCtOR/xHK/RCKY9bHKEvavVx+F2Wvx3MphddztQIDAQABo24w 11 | bDAdBgNVHQ4EFgQUy4VolgQJBL0rLYyI/JQHlBB0whEwDwYDVR0TAQH/BAUwAwEB 12 | /zAOBgNVHQ8BAf8EBAMCB4AwKgYDVR0RBCMwIYYfc3BpZmZlOi8vZXhhbXBsZS5v 13 | cmcvd29ya2xvYWQtMTANBgkqhkiG9w0BAQ0FAAOCAQEAPWpG81EVmPVbb99qEVnc 14 | yxPVy524isxJkx2OAOUXcBmYlZEKa6qEnZh02Qs+8nuBIH6cSwrFqLupycwbW9wo 15 | 32fe2Y+UuVEG7b9+vjFofzeVePx0dX5gk9WTb0b+OesmnF/m9uJFZ8ZDw/DFKO1y 16 | 0gcqJKPl3HHAz5s83mnBwiugy/G22iorO7iJBkChQrAxdpN2hvsEo88kn1wmopLq 17 | kFSeMVc3vrLI9/FL2WnNOYUKbM1J/xrMQFPgs2y9Md/ZZfBQMTQSn1MyRFhLNfq7 18 | vFH1bKDl8R1F6c8n/cU1ZTS84kWpCg5bkpJTKLESbgUj4I6ULxREeNIvNddyU6Qf 19 | AA== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509svid/wrong-leaf-cert-sign.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDKjCCAhKgAwIBAgIJAP/AfgtzAqe1MA0GCSqGSIb3DQEBDQUAMB4xCzAJBgNV 3 | BAYTAlVTMQ8wDQYDVQQKDAZTUElGRkUwHhcNMjAwMzMwMTk1ODQxWhcNMjUwMzI5 4 | MTk1ODQxWjAeMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMIIBIjANBgkq 5 | hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvudksUDCwrAb5Ag5zbRaMv0OWcehfN/1 6 | ejrDJUu0gmnYGOrs0xdMPCIacHkOEm0nSJ2b35REBbw13FTCHHaFH9tC0EZrz+Fk 7 | mhoP2HTcGRsiibfBvRpyd346sP6JqocpTNljhQxbhKCy4WkAS/KKjrD8NdkQ8zdM 8 | h8vBzEkzDwjbjbn3rWgvHcBOU3Zu2mn5/LP4B8oXlGM4n7ZMWK35s+yXKzmwkltp 9 | QtVVTmtkCmTLfckBsSgBESnRgIjY/jfwJqrugvPdmx0ToINljiahz0Uci3hRLhYc 10 | 3zAsQJoFXaNrm6qFCtOR/xHK/RCKY9bHKEvavVx+F2Wvx3MphddztQIDAQABo2sw 11 | aTAdBgNVHQ4EFgQUy4VolgQJBL0rLYyI/JQHlBB0whEwDAYDVR0TAQH/BAIwADAO 12 | BgNVHQ8BAf8EBAMCAoQwKgYDVR0RBCMwIYYfc3BpZmZlOi8vZXhhbXBsZS5vcmcv 13 | d29ya2xvYWQtMTANBgkqhkiG9w0BAQ0FAAOCAQEAsBgFKbbIo4hJMcgcRqLstLUU 14 | rzuyePs9Q7jIMr0dnriy++c+3DfNLQj3jnnhTaTMWoFyLL1tab6xanhxE0+/TkU6 15 | h4Z8hIsFcvZ0GJQ4MFxYpo4i7rly2OfbGXVKCc9Ho5hOcOhiwCpcbRxljVfdlK1x 16 | U03VBhgGN4yTxBZv7f9RhqiUo9KTWW1LaTwV0e/B+plMiXWZsDiILndM/1YVvnFU 17 | FIvABFDquhkIzN65WMBpUm1U8sjrgGdpAQxJw//dyb59A/HtfAM8tkLiZ4Teuh43 18 | gt88H1PK/f8ksL5rHZcCcvzoq6Dfzfu6QjWNAJg7d6nv3kOd2G+dAGEau3SmPQ== 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509svid/wrong-leaf-crl-sign.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDKjCCAhKgAwIBAgIJAMhI+pO57LUFMA0GCSqGSIb3DQEBDQUAMB4xCzAJBgNV 3 | BAYTAlVTMQ8wDQYDVQQKDAZTUElGRkUwHhcNMjAwMzMwMjAwNDEzWhcNMjUwMzI5 4 | MjAwNDEzWjAeMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMIIBIjANBgkq 5 | hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvudksUDCwrAb5Ag5zbRaMv0OWcehfN/1 6 | ejrDJUu0gmnYGOrs0xdMPCIacHkOEm0nSJ2b35REBbw13FTCHHaFH9tC0EZrz+Fk 7 | mhoP2HTcGRsiibfBvRpyd346sP6JqocpTNljhQxbhKCy4WkAS/KKjrD8NdkQ8zdM 8 | h8vBzEkzDwjbjbn3rWgvHcBOU3Zu2mn5/LP4B8oXlGM4n7ZMWK35s+yXKzmwkltp 9 | QtVVTmtkCmTLfckBsSgBESnRgIjY/jfwJqrugvPdmx0ToINljiahz0Uci3hRLhYc 10 | 3zAsQJoFXaNrm6qFCtOR/xHK/RCKY9bHKEvavVx+F2Wvx3MphddztQIDAQABo2sw 11 | aTAdBgNVHQ4EFgQUy4VolgQJBL0rLYyI/JQHlBB0whEwDAYDVR0TAQH/BAIwADAO 12 | BgNVHQ8BAf8EBAMCAYIwKgYDVR0RBCMwIYYfc3BpZmZlOi8vZXhhbXBsZS5vcmcv 13 | d29ya2xvYWQtMTANBgkqhkiG9w0BAQ0FAAOCAQEAS/j2y8U79cAY6WxVvd3TgPSy 14 | Uh56MbuS9pVKnRMLtwlRK8HoiMeqVYFXDu0xjz+7inVq6xtsr3SV8vy4uYLEr+SA 15 | qqQbw3rEWxph6oahFNkc9LOw9c3RHA8cH6izWYtQFsG2TxtMR3fvCQx7x/hxeTD0 16 | xfJEd4LPHfoiVFAtFn1CmglShNp0DA9Y+83s7QfBMfwCc7ih0d79903gpY9o5IVU 17 | SNk6Dd7trgkkoEN7P8pq5Rqx4M8XKv4Q1w9lAbL1wQJceM3ANtPhxANXazOhYLfS 18 | jH1L7u4I/Kp61hRcdqux/2lNGimka1b0W6TmAiGEu1m2AvIE2sy2P9+L3UfeSA== 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509svid/wrong-leaf-empty-id.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDFjCCAf6gAwIBAgIJAOMjYtQS4O0MMA0GCSqGSIb3DQEBDQUAMB4xCzAJBgNV 3 | BAYTAlVTMQ8wDQYDVQQKDAZTUElGRkUwHhcNMjAwMzMxMTI0NjI2WhcNMjUwMzMw 4 | MTI0NjI2WjAeMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMIIBIjANBgkq 5 | hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvudksUDCwrAb5Ag5zbRaMv0OWcehfN/1 6 | ejrDJUu0gmnYGOrs0xdMPCIacHkOEm0nSJ2b35REBbw13FTCHHaFH9tC0EZrz+Fk 7 | mhoP2HTcGRsiibfBvRpyd346sP6JqocpTNljhQxbhKCy4WkAS/KKjrD8NdkQ8zdM 8 | h8vBzEkzDwjbjbn3rWgvHcBOU3Zu2mn5/LP4B8oXlGM4n7ZMWK35s+yXKzmwkltp 9 | QtVVTmtkCmTLfckBsSgBESnRgIjY/jfwJqrugvPdmx0ToINljiahz0Uci3hRLhYc 10 | 3zAsQJoFXaNrm6qFCtOR/xHK/RCKY9bHKEvavVx+F2Wvx3MphddztQIDAQABo1cw 11 | VTAdBgNVHQ4EFgQUy4VolgQJBL0rLYyI/JQHlBB0whEwDAYDVR0TAQH/BAIwADAO 12 | BgNVHQ8BAf8EBAMCB4AwFgYDVR0RBA8wDYILZXhhbXBsZS5vcmcwDQYJKoZIhvcN 13 | AQENBQADggEBADJgmurhe2YuHULCYsmDPNQO1hJ79tzVwSZmHo9vLejv3zx7w25z 14 | 983CiqbeMRWHh+9gfXsdcwJnn4AQU2VfdYboXchBcA8tK4w8Bpev6DvOketrZ/KR 15 | l7LrTv2VV4+eigdXl5dSAQ/4mnLoICvksrKZllxCZWXyay3ctwIa75HCc4xYKGXT 16 | WuDNF7jSb21gB4K38pugwQhkH1eMdYvu2zAsBN0ClU7VEEsu1NEXO91+74CHCfDO 17 | e/+MPfqspoYrPrDsvkVGjYKxG3IxwsV3XdqB0ofvjoEv8ZqpPmrrfVYc11WBXFIY 18 | musYpNAVVBBkn1ILoeJarftnZCZU6VG18mo= 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /java-spiffe-core/src/test/resources/testdata/x509svid/wrong-leaf-no-digital-signature.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDKjCCAhKgAwIBAgIJAK/ds1rcEPTnMA0GCSqGSIb3DQEBDQUAMB4xCzAJBgNV 3 | BAYTAlVTMQ8wDQYDVQQKDAZTUElGRkUwHhcNMjAwMzMwMTk1MzU1WhcNMjUwMzI5 4 | MTk1MzU1WjAeMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMIIBIjANBgkq 5 | hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvudksUDCwrAb5Ag5zbRaMv0OWcehfN/1 6 | ejrDJUu0gmnYGOrs0xdMPCIacHkOEm0nSJ2b35REBbw13FTCHHaFH9tC0EZrz+Fk 7 | mhoP2HTcGRsiibfBvRpyd346sP6JqocpTNljhQxbhKCy4WkAS/KKjrD8NdkQ8zdM 8 | h8vBzEkzDwjbjbn3rWgvHcBOU3Zu2mn5/LP4B8oXlGM4n7ZMWK35s+yXKzmwkltp 9 | QtVVTmtkCmTLfckBsSgBESnRgIjY/jfwJqrugvPdmx0ToINljiahz0Uci3hRLhYc 10 | 3zAsQJoFXaNrm6qFCtOR/xHK/RCKY9bHKEvavVx+F2Wvx3MphddztQIDAQABo2sw 11 | aTAdBgNVHQ4EFgQUy4VolgQJBL0rLYyI/JQHlBB0whEwDAYDVR0TAQH/BAIwADAO 12 | BgNVHQ8BAf8EBAMCAQYwKgYDVR0RBCMwIYYfc3BpZmZlOi8vZXhhbXBsZS5vcmcv 13 | d29ya2xvYWQtMTANBgkqhkiG9w0BAQ0FAAOCAQEAlYTex0bigAfH4BPtLiGXUsDB 14 | Tw8p1ztElGHxBHyHs+WAC2Tm2Mlnpxa5e7457WQnta93IuzoU6Ws+TkPFy7IhA8z 15 | kjT4LYJGnDGOZ5te6epMpv+1Dul1aVwLTpm3oXmTBRtw2fGubexhj6UVFr9/dqae 16 | OihD5OmOTpMzs40SGCibqsKWEUIFtRjtN91kzzbwgAGLbHWrgBmNEimGZ8ASTWs7 17 | PdtnPGqdfKBGT1oHSlnDj3yCeNf9j5isirp2vtaYxVBz1P2wMrUdCKScOc23m2Sm 18 | tS2EsyMmLP3GncYiSjDXT4rtNr/NKG3n6g/Km5ZhPWDXtwOz0ur6lovKQyz+wg== 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /java-spiffe-core/src/testFixtures/java/io/spiffe/utils/CertAndKeyPair.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.utils; 2 | 3 | import java.security.KeyPair; 4 | import java.security.cert.X509Certificate; 5 | 6 | public class CertAndKeyPair { 7 | KeyPair keyPair; 8 | X509Certificate certificate; 9 | 10 | public CertAndKeyPair(X509Certificate certificate, KeyPair keyPair) { 11 | this.keyPair = keyPair; 12 | this.certificate = certificate; 13 | } 14 | 15 | public KeyPair getKeyPair() { 16 | return keyPair; 17 | } 18 | 19 | public X509Certificate getCertificate() { 20 | return certificate; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /java-spiffe-helper/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.github.johnrengelman.shadow" version "${shadowVersion}" 3 | } 4 | 5 | description = "Java SPIFFE Library Helper module to store X.509 SVIDs and Bundles in a Java KeyStore in disk" 6 | 7 | apply plugin: 'com.github.johnrengelman.shadow' 8 | 9 | assemble.dependsOn shadowJar 10 | 11 | shadowJar { 12 | mergeServiceFiles() 13 | archiveClassifier = project.hasProperty('archiveClassifier') && project.archiveClassifier != "" ? project.archiveClassifier : osdetector.classifier 14 | manifest { 15 | attributes 'Main-Class': 'io.spiffe.helper.cli.Runner' 16 | } 17 | } 18 | 19 | dependencies { 20 | api(project(':java-spiffe-core')) 21 | 22 | // runtimeOnly grpc-netty dependency module will be included in the shadowJar 23 | if (osdetector.os.is('osx') ) { 24 | project.ext.osArch = System.getProperty("os.arch") 25 | if ("x86_64" == project.ext.osArch) { 26 | runtimeOnly(project(':java-spiffe-core:grpc-netty-macos')) 27 | } else if ("aarch64" == project.ext.osArch) { 28 | runtimeOnly(project(':java-spiffe-core:grpc-netty-macos-aarch64')) 29 | } else { 30 | throw new GradleException("Architecture not supported: " + project.ext.osArch) 31 | } 32 | } else { 33 | runtimeOnly(project(':java-spiffe-core:grpc-netty-linux')) 34 | } 35 | 36 | implementation group: 'commons-cli', name: 'commons-cli', version: '1.9.0' 37 | 38 | testImplementation(testFixtures(project(":java-spiffe-core"))) 39 | } 40 | -------------------------------------------------------------------------------- /java-spiffe-helper/gradle.properties: -------------------------------------------------------------------------------- 1 | archiveClassifier= 2 | -------------------------------------------------------------------------------- /java-spiffe-helper/src/main/java/io/spiffe/helper/cli/Config.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.helper.cli; 2 | 3 | import io.spiffe.helper.exception.RunnerException; 4 | import io.spiffe.helper.keystore.KeyStoreHelper.KeyStoreOptions; 5 | import io.spiffe.helper.keystore.KeyStoreType; 6 | import lombok.val; 7 | import org.apache.commons.cli.*; 8 | import org.apache.commons.lang3.StringUtils; 9 | 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.nio.file.Files; 13 | import java.nio.file.Path; 14 | import java.nio.file.Paths; 15 | import java.util.Properties; 16 | 17 | class Config { 18 | 19 | private static final String DEFAULT_CONFIG_FILENAME = "conf/java-spiffe-helper.properties"; 20 | 21 | static final Option CONFIG_FILE_OPTION = 22 | Option.builder("c") 23 | .longOpt("config") 24 | .hasArg(true) 25 | .required(false) 26 | .build(); 27 | 28 | private Config() { 29 | } 30 | 31 | static Properties parseConfigFileProperties(final Path configFilePath) throws RunnerException { 32 | Properties properties = new Properties(); 33 | try (InputStream in = Files.newInputStream(configFilePath)) { 34 | properties.load(in); 35 | } catch (IOException e) { 36 | val error = String.format("Cannot open config file: %s", configFilePath); 37 | throw new RunnerException(error, e); 38 | } 39 | return properties; 40 | } 41 | 42 | static String getCliConfigOption(final String... args) throws ParseException { 43 | final Options cliOptions = new Options(); 44 | cliOptions.addOption(CONFIG_FILE_OPTION); 45 | CommandLineParser parser = new DefaultParser(); 46 | 47 | CommandLine cmd = parser.parse(cliOptions, args); 48 | return cmd.getOptionValue("config", getDefaultConfigPath()); 49 | } 50 | 51 | private static String getDefaultConfigPath() { 52 | return Paths.get(System.getProperty("user.dir"), DEFAULT_CONFIG_FILENAME).toString(); 53 | } 54 | 55 | static KeyStoreOptions createKeyStoreOptions(final Properties properties) { 56 | val keyStorePath = getProperty(properties, "keyStorePath"); 57 | val keyStorePass = getProperty(properties, "keyStorePass"); 58 | val keyPass = getProperty(properties, "keyPass"); 59 | val trustStorePath = getProperty(properties, "trustStorePath"); 60 | val trustStorePass = getProperty(properties, "trustStorePass"); 61 | 62 | val keyAlias = properties.getProperty("keyAlias", null); 63 | val spiffeSocketPath = properties.getProperty("spiffeSocketPath", null); 64 | val keyStoreTypeProp = properties.getProperty("keyStoreType", null); 65 | 66 | KeyStoreType keyStoreType; 67 | if (StringUtils.isNotBlank(keyStoreTypeProp)) { 68 | keyStoreType = KeyStoreType.parse(keyStoreTypeProp); 69 | } else { 70 | keyStoreType = KeyStoreType.getDefaultType(); 71 | } 72 | 73 | return KeyStoreOptions.builder() 74 | .keyStorePath(Paths.get(keyStorePath)) 75 | .keyStorePass(keyStorePass) 76 | .keyPass(keyPass) 77 | .trustStorePath(Paths.get(trustStorePath)) 78 | .trustStorePass(trustStorePass) 79 | .keyAlias(keyAlias) 80 | .spiffeSocketPath(spiffeSocketPath) 81 | .keyStoreType(keyStoreType) 82 | .build(); 83 | 84 | } 85 | 86 | static String getProperty(final Properties properties, final String key) { 87 | final String value = properties.getProperty(key); 88 | if (StringUtils.isBlank(value)) { 89 | throw new IllegalArgumentException(String.format("Missing value for config property: %s", key)); 90 | } 91 | return value; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /java-spiffe-helper/src/main/java/io/spiffe/helper/cli/Runner.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.helper.cli; 2 | 3 | import io.spiffe.exception.SocketEndpointAddressException; 4 | import io.spiffe.helper.exception.KeyStoreHelperException; 5 | import io.spiffe.helper.exception.RunnerException; 6 | import io.spiffe.helper.keystore.KeyStoreHelper; 7 | import lombok.extern.java.Log; 8 | import lombok.val; 9 | import org.apache.commons.cli.ParseException; 10 | import org.apache.commons.lang3.exception.ExceptionUtils; 11 | 12 | import java.nio.file.Paths; 13 | import java.security.KeyStoreException; 14 | 15 | /** 16 | * Entry point of the java-spiffe-helper CLI application. 17 | */ 18 | @Log 19 | public class Runner { 20 | 21 | private Runner() { 22 | } 23 | 24 | public static void main(final String... args) { 25 | try { 26 | runApplication(args); 27 | } catch (RunnerException e) { 28 | log.severe(ExceptionUtils.getStackTrace(e)); 29 | System.exit(1); 30 | } catch (ParseException | IllegalArgumentException e) { 31 | log.severe(e.getMessage()); 32 | System.exit(1); 33 | } 34 | } 35 | 36 | static void runApplication(final String... args) throws RunnerException, ParseException { 37 | try { 38 | val configFilePath = Config.getCliConfigOption(args); 39 | val properties = Config.parseConfigFileProperties(Paths.get(configFilePath)); 40 | val options = Config.createKeyStoreOptions(properties); 41 | try (val keyStoreHelper = KeyStoreHelper.create(options)) { 42 | keyStoreHelper.run(true); 43 | } 44 | } catch (SocketEndpointAddressException | KeyStoreHelperException | KeyStoreException e) { 45 | throw new RunnerException(e); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /java-spiffe-helper/src/main/java/io/spiffe/helper/exception/KeyStoreHelperException.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.helper.exception; 2 | 3 | /** 4 | * Checked exception to be thrown when there is an error creating or initializing a KeyStoreHelper. 5 | */ 6 | public class KeyStoreHelperException extends Exception { 7 | 8 | public KeyStoreHelperException(String message) { 9 | super(message); 10 | } 11 | 12 | public KeyStoreHelperException(String message, Throwable cause) { 13 | super(message, cause); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /java-spiffe-helper/src/main/java/io/spiffe/helper/exception/RunnerException.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.helper.exception; 2 | 3 | /** 4 | * Checked exception to be thrown when there are errors configuring the cli Runner. 5 | */ 6 | public class RunnerException extends Exception { 7 | public RunnerException(String message) { 8 | super(message); 9 | } 10 | 11 | public RunnerException(Throwable cause) { 12 | super(cause); 13 | } 14 | 15 | public RunnerException(String message, Throwable cause) { 16 | super(message, cause); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /java-spiffe-helper/src/main/java/io/spiffe/helper/keystore/AuthorityEntry.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.helper.keystore; 2 | 3 | import lombok.Builder; 4 | import lombok.Value; 5 | 6 | import java.security.cert.X509Certificate; 7 | 8 | @Value 9 | class AuthorityEntry { 10 | String alias; 11 | X509Certificate certificate; 12 | 13 | @Builder 14 | AuthorityEntry( 15 | final String alias, 16 | final X509Certificate certificate) { 17 | this.alias = alias; 18 | this.certificate = certificate; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /java-spiffe-helper/src/main/java/io/spiffe/helper/keystore/KeyStoreType.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.helper.keystore; 2 | 3 | /** 4 | * KeyStore types supported by the KeyStoreHelper. 5 | */ 6 | public enum KeyStoreType { 7 | 8 | JKS("jks"), 9 | 10 | PKCS12("pkcs12"); 11 | 12 | private final String value; 13 | 14 | KeyStoreType(final String value) { 15 | this.value = value; 16 | } 17 | 18 | public String value() { 19 | return value; 20 | } 21 | 22 | public static KeyStoreType getDefaultType() { 23 | return PKCS12; 24 | } 25 | 26 | /** 27 | * Parses an object to a KeyStoreType 28 | * 29 | * @param type an object representing a keystore type 30 | * @return an instance of a KeyStoreType 31 | * @throws IllegalArgumentException if the keystore type is unknown 32 | */ 33 | public static KeyStoreType parse(final Object type) { 34 | KeyStoreType keyStoreType = null; 35 | if (String.valueOf(type).equalsIgnoreCase(JKS.value)) { 36 | keyStoreType = JKS; 37 | } else if (String.valueOf(type).equalsIgnoreCase(PKCS12.value)) { 38 | keyStoreType = PKCS12; 39 | } 40 | 41 | if (keyStoreType == null) { 42 | throw new IllegalArgumentException(String.format("KeyStore type not supported: %s", type)); 43 | } 44 | return keyStoreType; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /java-spiffe-helper/src/main/java/io/spiffe/helper/keystore/PrivateKeyEntry.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.helper.keystore; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.Setter; 7 | 8 | import java.security.Key; 9 | import java.security.cert.X509Certificate; 10 | 11 | @Data 12 | class PrivateKeyEntry { 13 | 14 | @Setter(AccessLevel.NONE) 15 | private String alias; 16 | 17 | @Setter(AccessLevel.NONE) 18 | private Key privateKey; 19 | 20 | @Setter(AccessLevel.NONE) 21 | private String password; 22 | 23 | @Setter(AccessLevel.NONE) 24 | private X509Certificate[] certificateChain; 25 | 26 | @Builder 27 | PrivateKeyEntry( 28 | final String alias, 29 | final Key privateKey, 30 | final String password, 31 | final X509Certificate... certificateChain) { 32 | this.alias = alias; 33 | this.privateKey = privateKey; 34 | this.password = password; 35 | this.certificateChain = certificateChain.clone(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /java-spiffe-helper/src/test/java/io/spiffe/helper/cli/RunnerTest.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.helper.cli; 2 | 3 | import io.spiffe.helper.exception.RunnerException; 4 | import lombok.val; 5 | import org.apache.commons.cli.ParseException; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.net.URISyntaxException; 11 | import java.nio.file.Path; 12 | import java.nio.file.Paths; 13 | 14 | import static io.spiffe.utils.TestUtils.toUri; 15 | import static org.junit.jupiter.api.Assertions.assertEquals; 16 | import static org.junit.jupiter.api.Assertions.fail; 17 | 18 | class RunnerTest { 19 | 20 | @Test 21 | void test_Main_KeyStorePathIsMissing() throws URISyntaxException, RunnerException, ParseException { 22 | final Path path = Paths.get(toUri("testdata/cli/missing-keystorepath.conf")); 23 | try { 24 | Runner.runApplication("-c", path.toString()); 25 | fail("expected exception: property is missing"); 26 | } catch (IllegalArgumentException e) { 27 | assertEquals("Missing value for config property: keyStorePath", e.getMessage()); 28 | } 29 | } 30 | 31 | @Test 32 | void test_Main_KeyStorePassIsMissing() throws URISyntaxException, RunnerException, ParseException { 33 | final Path path = Paths.get(toUri("testdata/cli/missing-keystorepass.conf")); 34 | try { 35 | Runner.runApplication("-c", path.toString()); 36 | fail("expected exception: property is missing"); 37 | } catch (IllegalArgumentException e) { 38 | assertEquals("Missing value for config property: keyStorePass", e.getMessage()); 39 | } 40 | } 41 | 42 | @Test 43 | void test_Main_KeyPassIsMissing() throws URISyntaxException, RunnerException, ParseException { 44 | final Path path = Paths.get(toUri("testdata/cli/missing-keypass.conf")); 45 | try { 46 | Runner.runApplication("-c", path.toString()); 47 | fail("expected exception: property is missing"); 48 | } catch (IllegalArgumentException e) { 49 | assertEquals("Missing value for config property: keyPass", e.getMessage()); 50 | } 51 | } 52 | 53 | @Test 54 | void test_Main_TrustStorePathIsMissing() throws URISyntaxException, RunnerException, ParseException { 55 | final Path path = Paths.get(toUri("testdata/cli/missing-truststorepath.conf")); 56 | try { 57 | Runner.runApplication("-c", path.toString()); 58 | fail("expected exception: property is missing"); 59 | } catch (IllegalArgumentException e) { 60 | assertEquals("Missing value for config property: trustStorePath", e.getMessage()); 61 | } 62 | } 63 | 64 | @Test 65 | void test_Main_TrustStorePassIsMissing() throws URISyntaxException, RunnerException, ParseException { 66 | final Path path = Paths.get(toUri("testdata/cli/missing-truststorepass.conf")); 67 | try { 68 | Runner.runApplication("-c", path.toString()); 69 | fail("expected exception: property is missing"); 70 | } catch (IllegalArgumentException e) { 71 | assertEquals("Missing value for config property: trustStorePass", e.getMessage()); 72 | } 73 | } 74 | 75 | @Test 76 | void test_Main_throwsExceptionIfTheKeystoreCannotBeCreated() throws URISyntaxException, IOException, ParseException { 77 | val file = new File("keystore123.p12"); 78 | file.createNewFile(); 79 | 80 | val configPath = Paths.get(toUri("testdata/cli/correct.conf")); 81 | try { 82 | Runner.runApplication("-c", configPath.toString()); 83 | } catch (RunnerException e) { 84 | assertEquals("KeyStore cannot be opened", e.getCause().getMessage()); 85 | } finally { 86 | file.delete(); 87 | } 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /java-spiffe-helper/src/test/java/io/spiffe/helper/keystore/KeyStoreTypeTest.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.helper.keystore; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.assertEquals; 6 | import static org.junit.jupiter.api.Assertions.fail; 7 | 8 | class KeyStoreTypeTest { 9 | 10 | @Test 11 | void value() { 12 | assertEquals("jks", KeyStoreType.JKS.value()); 13 | assertEquals("pkcs12", KeyStoreType.PKCS12.value()); 14 | } 15 | 16 | @Test 17 | void testGetDefaultType() { 18 | assertEquals(KeyStoreType.PKCS12, KeyStoreType.getDefaultType()); 19 | } 20 | 21 | @Test 22 | void testParseJKS() { 23 | final KeyStoreType type = KeyStoreType.parse("jks"); 24 | assertEquals(KeyStoreType.JKS, type); 25 | } 26 | 27 | @Test 28 | void testParsePKCS12() { 29 | final KeyStoreType type = KeyStoreType.parse("pkcs12"); 30 | assertEquals(KeyStoreType.PKCS12, type); 31 | } 32 | 33 | @Test 34 | void testParseUnknownType() { 35 | try { 36 | KeyStoreType.parse("other_unknown"); 37 | fail("expected error: KeyStore type not supported"); 38 | } catch (IllegalArgumentException e) { 39 | assertEquals("KeyStore type not supported: other_unknown", e.getMessage()); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /java-spiffe-helper/src/test/java/io/spiffe/helper/keystore/WorkloadApiClientErrorStub.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.helper.keystore; 2 | 3 | import io.spiffe.bundle.jwtbundle.JwtBundleSet; 4 | import io.spiffe.bundle.x509bundle.X509BundleSet; 5 | import io.spiffe.exception.JwtBundleException; 6 | import io.spiffe.exception.JwtSvidException; 7 | import io.spiffe.exception.X509BundleException; 8 | import io.spiffe.exception.X509ContextException; 9 | import io.spiffe.spiffeid.SpiffeId; 10 | import io.spiffe.svid.jwtsvid.JwtSvid; 11 | import io.spiffe.workloadapi.Watcher; 12 | import io.spiffe.workloadapi.WorkloadApiClient; 13 | import io.spiffe.workloadapi.X509Context; 14 | import lombok.NonNull; 15 | 16 | import java.util.List; 17 | import java.io.IOException; 18 | 19 | public class WorkloadApiClientErrorStub implements WorkloadApiClient { 20 | 21 | @Override 22 | public X509Context fetchX509Context() throws X509ContextException { 23 | throw new X509ContextException("Testing exception"); 24 | } 25 | 26 | @Override 27 | public void watchX509Context(@NonNull final Watcher watcher) { 28 | watcher.onError(new X509ContextException("Testing exception")); 29 | } 30 | 31 | @Override 32 | public X509BundleSet fetchX509Bundles() throws X509BundleException { 33 | throw new X509BundleException("Testing exception"); 34 | } 35 | 36 | @Override 37 | public void watchX509Bundles(@NonNull Watcher watcher) { 38 | watcher.onError(new X509BundleException("Testing exception")); 39 | } 40 | 41 | @Override 42 | public JwtSvid fetchJwtSvid(@NonNull final String audience, final String... extraAudience) throws JwtSvidException { 43 | throw new JwtSvidException("Testing exception"); 44 | } 45 | 46 | @Override 47 | public JwtSvid fetchJwtSvid(@NonNull final SpiffeId subject, @NonNull final String audience, final String... extraAudience) throws JwtSvidException { 48 | throw new JwtSvidException("Testing exception"); 49 | } 50 | @Override 51 | public List fetchJwtSvids(@NonNull final String audience, final String... extraAudience) throws JwtSvidException { 52 | throw new JwtSvidException("Testing exception"); 53 | } 54 | 55 | @Override 56 | public List fetchJwtSvids(@NonNull final SpiffeId subject, @NonNull final String audience, final String... extraAudience) throws JwtSvidException { 57 | throw new JwtSvidException("Testing exception"); 58 | } 59 | @Override 60 | public JwtBundleSet fetchJwtBundles() throws JwtBundleException { 61 | throw new JwtBundleException("Testing exception"); 62 | } 63 | 64 | @Override 65 | public JwtSvid validateJwtSvid(@NonNull final String token, @NonNull final String audience) throws JwtSvidException { 66 | return null; 67 | } 68 | 69 | @Override 70 | public void watchJwtBundles(@NonNull final Watcher watcher) { 71 | watcher.onError(new JwtBundleException("Testing exception")); 72 | } 73 | 74 | @Override 75 | public void close() throws IOException { 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /java-spiffe-helper/src/test/resources/testdata/bundle.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spiffe/java-spiffe/c00a4d09b6abb59e7c51bc06ac1400deffc04ee4/java-spiffe-helper/src/test/resources/testdata/bundle.der -------------------------------------------------------------------------------- /java-spiffe-helper/src/test/resources/testdata/bundle.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBjjCCATSgAwIBAgIBADAKBggqhkjOPQQDAjAeMQswCQYDVQQGEwJVUzEPMA0G 3 | A1UEChMGU1BJRkZFMB4XDTIwMDUyMDE3MDc1N1oXDTIwMDUyNzE3MDgwN1owHjEL 4 | MAkGA1UEBhMCVVMxDzANBgNVBAoTBlNQSUZGRTBZMBMGByqGSM49AgEGCCqGSM49 5 | AwEHA0IABO3/qXKapLzDi3wgqW8Lkjm35WrJclRr8aN7IF8Px2jeJpV4KG+wdLa7 6 | rXSOJH8xCotu9QnQcGo4FuinMsJPlZKjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNV 7 | HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQEOa83CNDa8BcLL/mU3ep//rxyNjAfBgNV 8 | HREEGDAWhhRzcGlmZmU6Ly9leGFtcGxlLm9yZzAKBggqhkjOPQQDAgNIADBFAiBC 9 | RTRaKR1nphUMjFcLfopHk+VJgB97yZ8TEZRlNF8vLQIhAJchfcPmlOk9OFiAnSoU 10 | th2m6yJcLC3axw94n1fg0qcd 11 | -----END CERTIFICATE----- 12 | -----BEGIN CERTIFICATE----- 13 | MIIBjjCCATSgAwIBAgIBADAKBggqhkjOPQQDAjAeMQswCQYDVQQGEwJVUzEPMA0G 14 | A1UEChMGU1BJRkZFMB4XDTIwMDUyNTExNDEyMVoXDTIwMDYwMTExNDEzMVowHjEL 15 | MAkGA1UEBhMCVVMxDzANBgNVBAoTBlNQSUZGRTBZMBMGByqGSM49AgEGCCqGSM49 16 | AwEHA0IABAED6MJ6JluKjEVjKiOP8gPgcqxdJpQKI7iJLDTTd8Ums1/bXTvUxQXG 17 | PmMcqYAtEvTgs1ew/FDSh5L8XNvaghWjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNV 18 | HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQtAHWFv+CwKHD7G/VNm6oke6CTtTAfBgNV 19 | HREEGDAWhhRzcGlmZmU6Ly9leGFtcGxlLm9yZzAKBggqhkjOPQQDAgNIADBFAiAn 20 | VJkxslbz+KJMvsenGo9id3FllKxK1edi2gdyQay62gIhANK6B1ExwDYzUOB5KQUH 21 | XZg4m88DL41Jn2b6k+fQggVh 22 | -----END CERTIFICATE----- 23 | -----BEGIN CERTIFICATE----- 24 | MIIBjjCCATSgAwIBAgIBADAKBggqhkjOPQQDAjAeMQswCQYDVQQGEwJVUzEPMA0G 25 | A1UEChMGU1BJRkZFMB4XDTIwMDYwMTE2NTAyM1oXDTIwMDYwODE2NTAzM1owHjEL 26 | MAkGA1UEBhMCVVMxDzANBgNVBAoTBlNQSUZGRTBZMBMGByqGSM49AgEGCCqGSM49 27 | AwEHA0IABPRnZa26tZSfHZ3XrrFmX1xCoU7VzoftkcaIfInGWdEYi3PyyBlJnYgY 28 | DsirlAz1Ia+zHZyH4784BjfZErRLocejYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNV 29 | HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQeeQ48k+lNLMAH0aQcdi8Ka6Op3TAfBgNV 30 | HREEGDAWhhRzcGlmZmU6Ly9leGFtcGxlLm9yZzAKBggqhkjOPQQDAgNIADBFAiEA 31 | 6DAO2Yi+zwJrv1awYIlzZ1yJCwGam9MT6kI+lE94Xs8CIDjmFSRHj4Kr1McIvxPH 32 | P5gGQxQNbXxm9iIyoZShXdcb 33 | -----END CERTIFICATE----- 34 | -------------------------------------------------------------------------------- /java-spiffe-helper/src/test/resources/testdata/cli/correct.conf: -------------------------------------------------------------------------------- 1 | keyStorePath = keystore123.p12 2 | keyStorePass = example123 3 | keyPass = pass123 4 | trustStorePath = truststore123.p12 5 | trustStorePass = otherpass123 6 | keyStoreType = jks 7 | keyAlias = other_alias 8 | spiffeSocketPath = unix:/tmp/test 9 | -------------------------------------------------------------------------------- /java-spiffe-helper/src/test/resources/testdata/cli/missing-keypass.conf: -------------------------------------------------------------------------------- 1 | keyStorePath = keystore123.p12 2 | keyStorePass = example123 3 | trustStorePath = truststore123.p12 4 | trustStorePass = otherpass123 5 | keyStoreType = jks 6 | keyAlias = other_alias 7 | spiffeSocketPath = unix:/tmp/agent.sock 8 | -------------------------------------------------------------------------------- /java-spiffe-helper/src/test/resources/testdata/cli/missing-keystorepass.conf: -------------------------------------------------------------------------------- 1 | keyStorePath = keystore123.p12 2 | keyPass = pass123 3 | trustStorePath = truststore123.p12 4 | trustStorePass = otherpass123 5 | keyStoreType = jks 6 | keyAlias = other_alias 7 | spiffeSocketPath = unix:/tmp/agent.sock 8 | -------------------------------------------------------------------------------- /java-spiffe-helper/src/test/resources/testdata/cli/missing-keystorepath.conf: -------------------------------------------------------------------------------- 1 | keyStorePass = example123 2 | keyPass = pass123 3 | trustStorePath = truststore123.p12 4 | trustStorePass = otherpass123 5 | keyStoreType = jks 6 | keyAlias = other_alias 7 | spiffeSocketPath = unix:/tmp/agent.sock 8 | -------------------------------------------------------------------------------- /java-spiffe-helper/src/test/resources/testdata/cli/missing-truststorepass.conf: -------------------------------------------------------------------------------- 1 | keyStorePath = keystore123.p12 2 | keyStorePass = example123 3 | keyPass = pass123 4 | trustStorePath = truststore123.p12 5 | keyStoreType = jks 6 | keyAlias = other_alias 7 | spiffeSocketPath = unix:/tmp/agent.sock 8 | -------------------------------------------------------------------------------- /java-spiffe-helper/src/test/resources/testdata/cli/missing-truststorepath.conf: -------------------------------------------------------------------------------- 1 | keyStorePath = keystore123.p12 2 | keyStorePass = example123 3 | keyPass = pass123 4 | trustStorePass = otherpass123 5 | keyStoreType = jks 6 | keyAlias = other_alias 7 | spiffeSocketPath = unix:/tmp/agent.sock 8 | -------------------------------------------------------------------------------- /java-spiffe-helper/src/test/resources/testdata/svid.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spiffe/java-spiffe/c00a4d09b6abb59e7c51bc06ac1400deffc04ee4/java-spiffe-helper/src/test/resources/testdata/svid.der -------------------------------------------------------------------------------- /java-spiffe-helper/src/test/resources/testdata/svid.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgXNOpBRBUIzE4VRvy 3 | nfxWNboXxrdVFTPesmpmp8V8D5+hRANCAAR0nJ2niXQw2PL8p97m3OyprAKEIA9R 4 | LoH21QBHWxCkQ+gMS8ZPWfqz3io+r78HcYmLgadwqQGEd4ydpK5zOWfE 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /java-spiffe-helper/src/test/resources/testdata/svid.key.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spiffe/java-spiffe/c00a4d09b6abb59e7c51bc06ac1400deffc04ee4/java-spiffe-helper/src/test/resources/testdata/svid.key.der -------------------------------------------------------------------------------- /java-spiffe-helper/src/test/resources/testdata/svid.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIB6jCCAZGgAwIBAgIQEu8Zv0CRj/f7IlVC+MFgjDAKBggqhkjOPQQDAjAeMQsw 3 | CQYDVQQGEwJVUzEPMA0GA1UEChMGU1BJRkZFMB4XDTIwMDYwMTE2NTExNloXDTIw 4 | MDYwMTE3NTEyNlowHTELMAkGA1UEBhMCVVMxDjAMBgNVBAoTBVNQSVJFMFkwEwYH 5 | KoZIzj0CAQYIKoZIzj0DAQcDQgAEdJydp4l0MNjy/Kfe5tzsqawChCAPUS6B9tUA 6 | R1sQpEPoDEvGT1n6s94qPq+/B3GJi4GncKkBhHeMnaSuczlnxKOBsTCBrjAOBgNV 7 | HQ8BAf8EBAMCA6gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1Ud 8 | EwEB/wQCMAAwHQYDVR0OBBYEFLDXoDiQmFWIBeqPukkAhIhbfS2TMB8GA1UdIwQY 9 | MBaAFB55DjyT6U0swAfRpBx2Lwpro6ndMC8GA1UdEQQoMCaGJHNwaWZmZTovL2V4 10 | YW1wbGUub3JnL3dvcmtsb2FkLXNlcnZlcjAKBggqhkjOPQQDAgNHADBEAiBT2Lzq 11 | RDMkptYCU2TK8v9htJ/4/aD3SxSqPglK//ENawIgODWEA/wmjdcO/bGK4pfZIwfN 12 | NkaX3AN5M+Gu37K409I= 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /java-spiffe-provider/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.github.johnrengelman.shadow" version "${shadowVersion}" 3 | } 4 | 5 | description = "Java Security Provider implementation supporting X.509-SVIDs and methods for " + 6 | "creating SSLContexts that are backed by the Workload API." 7 | 8 | apply plugin: 'com.github.johnrengelman.shadow' 9 | 10 | assemble.dependsOn shadowJar 11 | 12 | shadowJar { 13 | mergeServiceFiles() 14 | archiveClassifier = "all-".concat(osdetector.classifier) 15 | } 16 | 17 | dependencies { 18 | api(project(":java-spiffe-core")) 19 | 20 | // runtimeOnly grpc-netty dependency module will be included in the shadowJar 21 | if (osdetector.os.is('osx') ) { 22 | project.ext.osArch = System.getProperty("os.arch") 23 | if ("x86_64" == project.ext.osArch) { 24 | runtimeOnly(project(':java-spiffe-core:grpc-netty-macos')) 25 | } else if ("aarch64" == project.ext.osArch) { 26 | runtimeOnly(project(':java-spiffe-core:grpc-netty-macos-aarch64')) 27 | } else { 28 | throw new GradleException("Architecture not supported: " + project.ext.osArch) 29 | } 30 | } else { 31 | runtimeOnly(project(':java-spiffe-core:grpc-netty-linux')) 32 | } 33 | 34 | testImplementation(testFixtures(project(":java-spiffe-core"))) 35 | } 36 | -------------------------------------------------------------------------------- /java-spiffe-provider/src/main/java/io/spiffe/provider/AllowedIdSupplierSpiffeIdVerifier.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.provider; 2 | 3 | import io.spiffe.spiffeid.SpiffeId; 4 | import lombok.NonNull; 5 | 6 | import java.security.cert.X509Certificate; 7 | import java.util.Set; 8 | import java.util.function.Supplier; 9 | 10 | public class AllowedIdSupplierSpiffeIdVerifier implements SpiffeIdVerifier { 11 | 12 | private final Supplier> allowedSpiffeIdsSupplier; 13 | 14 | public AllowedIdSupplierSpiffeIdVerifier(@NonNull Supplier> allowedSpiffeIdsSupplier) { 15 | this.allowedSpiffeIdsSupplier = allowedSpiffeIdsSupplier; 16 | } 17 | 18 | @Override 19 | public void verify(SpiffeId spiffeId, X509Certificate[] verifiedChain) throws SpiffeVerificationException { 20 | Set allowedSpiffeIds = allowedSpiffeIdsSupplier.get(); 21 | if (!allowedSpiffeIds.contains(spiffeId)) { 22 | throw new SpiffeVerificationException(String.format("SPIFFE ID %s in X.509 certificate is not accepted", spiffeId)); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /java-spiffe-provider/src/main/java/io/spiffe/provider/EnvironmentUtils.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.provider; 2 | 3 | import lombok.val; 4 | import org.apache.commons.lang3.StringUtils; 5 | 6 | import java.security.Security; 7 | 8 | /** 9 | * Utility class to get value from the environment. 10 | */ 11 | final class EnvironmentUtils { 12 | 13 | private EnvironmentUtils() { 14 | } 15 | 16 | /** 17 | * Looks for a variable name defined in the environment. 18 | * It first looks in the {@link Security} environment, if it is not defined or is a blank value, 19 | * it looks in the {@link System} environment, if it is not defined or is a blank value, 20 | * returns the defaultValue. 21 | * @param variableName the key of the variable to look for in the environment 22 | * @param defaultValue the value that is returned if the variable has no value in the environment 23 | * @return a String with a value 24 | */ 25 | static String getProperty(final String variableName, final String defaultValue) { 26 | val value = getProperty(variableName); 27 | if (StringUtils.isNotBlank(value)) { 28 | return value; 29 | } 30 | 31 | return defaultValue; 32 | } 33 | 34 | /** 35 | * Looks for a variable name defined in the environment. 36 | * It first looks in the {@link Security} environment, if it is not defined or is a blank value, 37 | * it looks in the {@link System} environment. 38 | * @param variableName the key of the variable to look for in the environment 39 | * @return a String with a value 40 | */ 41 | static String getProperty(final String variableName) { 42 | String value; 43 | value = System.getProperty(variableName); 44 | if (StringUtils.isNotBlank(value)) { 45 | return value; 46 | } 47 | value = Security.getProperty(variableName); 48 | if (StringUtils.isNotBlank(value)) { 49 | return value; 50 | } 51 | return ""; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /java-spiffe-provider/src/main/java/io/spiffe/provider/SpiffeIdVerifier.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.provider; 2 | 3 | import io.spiffe.spiffeid.SpiffeId; 4 | 5 | import java.security.cert.X509Certificate; 6 | 7 | public interface SpiffeIdVerifier { 8 | /** 9 | * Verify that an X509-SVID is acceptable. This method receives the SPIFFE ID of the SVID and the certificate 10 | * chain. 11 | * 12 | * @param spiffeId the SPIFFE ID of the SVID 13 | * @param verifiedChain the certificate chain with the X509-SVID certificate back to an X.509 root for the trust domain. 14 | * @throws SpiffeVerificationException if there was an error verifying the SPIFFE ID or it wasn't considered valid. 15 | */ 16 | public void verify(SpiffeId spiffeId, X509Certificate[] verifiedChain) throws SpiffeVerificationException; 17 | } 18 | -------------------------------------------------------------------------------- /java-spiffe-provider/src/main/java/io/spiffe/provider/SpiffeKeyManagerFactory.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.provider; 2 | 3 | import io.spiffe.exception.SocketEndpointAddressException; 4 | import io.spiffe.exception.X509SourceException; 5 | import io.spiffe.provider.exception.SpiffeProviderException; 6 | import io.spiffe.svid.x509svid.X509SvidSource; 7 | import io.spiffe.workloadapi.DefaultX509Source; 8 | import io.spiffe.workloadapi.X509Source; 9 | import lombok.NonNull; 10 | import lombok.val; 11 | 12 | import javax.net.ssl.KeyManager; 13 | import javax.net.ssl.KeyManagerFactorySpi; 14 | import javax.net.ssl.ManagerFactoryParameters; 15 | import java.security.KeyStore; 16 | 17 | /** 18 | * Implementation of a {@link KeyManagerFactorySpi} to create a {@link KeyManager} that is backed by the Workload API. 19 | *

20 | * The Java Security API will call engineGetKeyManagers() to get an instance of a KeyManager. 21 | * This KeyManager instance is injected with an {@link DefaultX509Source} to obtain the latest X.509 SVIDs updates 22 | * from the Workload API. 23 | * 24 | * @see SpiffeSslContextFactory 25 | * @see X509SvidSource 26 | * @see X509SourceManager 27 | * @see SpiffeSslContextFactory 28 | */ 29 | public final class SpiffeKeyManagerFactory extends KeyManagerFactorySpi { 30 | 31 | /** 32 | * Default method for creating the KeyManager, uses an {@link DefaultX509Source} instance 33 | * that is handled by the Singleton {@link X509SourceManager} 34 | * 35 | * @throws SpiffeProviderException in case there is an error setting up the X.509 source 36 | */ 37 | @Override 38 | protected KeyManager[] engineGetKeyManagers() { 39 | val x509Source = getX509Source(); 40 | val spiffeKeyManager = new SpiffeKeyManager(x509Source); 41 | return new KeyManager[]{spiffeKeyManager}; 42 | } 43 | 44 | /** 45 | * Creates a new key manager and initializes it with the given X.509 SVID source. 46 | * 47 | * @param x509SvidSource an instance of a {@link X509SvidSource} 48 | * @return an array with an instance of a {@link KeyManager} 49 | */ 50 | public KeyManager[] engineGetKeyManagers(@NonNull final X509SvidSource x509SvidSource) { 51 | val spiffeKeyManager = new SpiffeKeyManager(x509SvidSource); 52 | return new KeyManager[]{spiffeKeyManager}; 53 | } 54 | 55 | @Override 56 | protected void engineInit(final KeyStore keyStore, final char[] chars) { 57 | //no implementation needed 58 | } 59 | 60 | @Override 61 | protected void engineInit(final ManagerFactoryParameters managerFactoryParameters) { 62 | //no implementation needed 63 | } 64 | 65 | private X509Source getX509Source() { 66 | try { 67 | return X509SourceManager.getX509Source(); 68 | } catch (X509SourceException e) { 69 | throw new SpiffeProviderException("The X.509 source could not be created", e); 70 | } catch (SocketEndpointAddressException e) { 71 | throw new SpiffeProviderException("The Workload API Socket endpoint address configured is not valid", e); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /java-spiffe-provider/src/main/java/io/spiffe/provider/SpiffeKeyStore.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.provider; 2 | 3 | import java.io.InputStream; 4 | import java.io.OutputStream; 5 | import java.security.Key; 6 | import java.security.KeyStoreSpi; 7 | import java.security.cert.Certificate; 8 | import java.time.Instant; 9 | import java.util.Collections; 10 | import java.util.Date; 11 | import java.util.Enumeration; 12 | import java.util.Objects; 13 | 14 | import static java.util.Collections.enumeration; 15 | import static io.spiffe.provider.SpiffeProviderConstants.DEFAULT_ALIAS; 16 | 17 | /** 18 | * This class is required by the Java Provider Architecture, but since this Provider 19 | * doesn't use certificates stored in a KeyStore, the only purpose of this class is 20 | * to return the ALIAS that is handled by this SPIFFE Provider implementation. 21 | */ 22 | public final class SpiffeKeyStore extends KeyStoreSpi { 23 | 24 | private static final int NUMBER_OF_ENTRIES = 1; 25 | 26 | @Override 27 | public Key engineGetKey(final String alias, final char[] password) { 28 | return null; 29 | } 30 | 31 | @Override 32 | public Certificate[] engineGetCertificateChain(final String alias) { 33 | return new Certificate[0]; 34 | } 35 | 36 | @Override 37 | public Certificate engineGetCertificate(final String alias) { 38 | return null; 39 | } 40 | 41 | @Override 42 | public Date engineGetCreationDate(final String alias) { 43 | return Date.from(Instant.now()); 44 | } 45 | 46 | @Override 47 | public void engineSetKeyEntry(final String alias, final Key key, final char[] password, final Certificate[] chain) { 48 | //no implementation needed 49 | } 50 | 51 | @Override 52 | public void engineSetKeyEntry(final String alias, final byte[] key, final Certificate[] chain) { 53 | //no implementation needed 54 | } 55 | 56 | @Override 57 | public void engineSetCertificateEntry(final String alias, final Certificate cert) { 58 | //no implementation needed 59 | } 60 | 61 | @Override 62 | public void engineDeleteEntry(final String alias) { 63 | //no implementation needed 64 | } 65 | 66 | @Override 67 | public Enumeration engineAliases() { 68 | return enumeration(Collections.singletonList(DEFAULT_ALIAS)); 69 | } 70 | 71 | @Override 72 | public boolean engineContainsAlias(final String alias) { 73 | return Objects.equals(alias, DEFAULT_ALIAS); 74 | } 75 | 76 | @Override 77 | public int engineSize() { 78 | return NUMBER_OF_ENTRIES; 79 | } 80 | 81 | @Override 82 | public boolean engineIsKeyEntry(final String alias) { 83 | return Objects.equals(alias, DEFAULT_ALIAS); 84 | } 85 | 86 | @Override 87 | public boolean engineIsCertificateEntry(final String alias) { 88 | return Objects.equals(alias, DEFAULT_ALIAS); 89 | } 90 | 91 | @Override 92 | public String engineGetCertificateAlias(final Certificate cert) { 93 | return DEFAULT_ALIAS; 94 | } 95 | 96 | @Override 97 | public void engineStore(final OutputStream stream, final char[] password) { 98 | //no implementation needed 99 | } 100 | 101 | @Override 102 | public void engineLoad(final InputStream stream, final char[] password) { 103 | //no implementation needed 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /java-spiffe-provider/src/main/java/io/spiffe/provider/SpiffeProvider.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.provider; 2 | 3 | import java.security.Provider; 4 | import java.security.Security; 5 | 6 | import static io.spiffe.provider.SpiffeProviderConstants.PROVIDER_NAME; 7 | 8 | /** 9 | * Represents a Security Provider for the Java Security API that supports SPIFFE X.509-SVIDs and Bundles 10 | * fetched from the Workload API. 11 | *

12 | * The {@link javax.net.ssl.KeyManager} and {@link javax.net.ssl.TrustManager} implementations in this Provider 13 | * handle the SPIFFE X.509-SVIDs and Bundles in memory fetching them from the Workload API and rotating them automatically. 14 | *

15 | * The {@link io.spiffe.provider.SpiffeKeyManager} provides the X.509-SVID (chain of certificates) to probe identity to another peer 16 | * in a TLS connection. 17 | *

18 | * The {@link io.spiffe.provider.SpiffeTrustManager} provides the X.509 Bundles to validate the peer's X.509 chain of certificates. 19 | * It also performs SPIFFE ID validation on the SVIDs presented by peers in a TLS connection. 20 | *

21 | * The way this Provider is plugged in into the Java Security API is by registering a {@link javax.net.ssl.KeyManagerFactory} 22 | * for creating an instance of a {@link javax.net.ssl.KeyManager}. It also registers a {@link javax.net.ssl.TrustManagerFactory} 23 | * for creating an instance of a {@link javax.net.ssl.TrustManager}. 24 | *

25 | * To use this Provider, it is needed to add the following lines to the java.security file: 26 | *

27 |  *      security.provider.n=io.spiffe.SpiffeProvider
28 |  *      ssl.KeyManagerFactory.algorithm=Spiffe
29 |  *      ssl.TrustManagerFactory.algorithm=Spiffe
30 |  * 
31 | *

32 | * Also, to configure the accepted SPIFFE IDs, add to the java.security the list of SPIFFE IDs 33 | * separated by the pipe character: 34 | *

35 |  *      ssl.spiffe.accept=spiffe://example.org/workload1 | spiffe://example.org/workload2,
36 |  * 
37 | * This property can also be defined as a System parameter passed through -Dssl.spiffe.accept: 38 | *
39 |  *      -Dssl.spiffe.accept=ssl.spiffe.accept=spiffe://example.org/workload1 | spiffe://example.org/workload2
40 |  * 
41 | *

42 | * To configure the `TrustManager` to accept any SPIFFE ID presented by a peer, 43 | * the property ssl.spiffe.acceptAll must be set with the value true: 44 | *

45 |  *     ssl.spiffe.acceptAll=true
46 |  * 
47 | */ 48 | public final class SpiffeProvider extends Provider { 49 | 50 | private static final String SPIFFE_KEY_MANAGER_FACTORY = 51 | String.format("KeyManagerFactory.%s", SpiffeProviderConstants.ALGORITHM); 52 | 53 | private static final String SPIFFE_TRUST_MANAGER_FACTORY = 54 | String.format("TrustManagerFactory.%s", SpiffeProviderConstants.ALGORITHM); 55 | 56 | private static final String SPIFFE_KEYSTORE = String.format("KeyStore.%s", SpiffeProviderConstants.ALGORITHM); 57 | 58 | /** 59 | * Constructor. 60 | *

61 | * Configure the Provider Name and register KeyManagerFactory, TrustManagerFactory and KeyStore 62 | */ 63 | public SpiffeProvider() { 64 | super(PROVIDER_NAME, 0.6, "SPIFFE based KeyStore and TrustStore"); 65 | super.put(SPIFFE_KEY_MANAGER_FACTORY, SpiffeKeyManagerFactory.class.getName()); 66 | super.put(SPIFFE_TRUST_MANAGER_FACTORY, SpiffeTrustManagerFactory.class.getName()); 67 | super.put(SPIFFE_KEYSTORE, SpiffeKeyStore.class.getName()); 68 | } 69 | 70 | 71 | /** 72 | * Installs this provider implementation. 73 | */ 74 | public static void install() { 75 | if (Security.getProvider(PROVIDER_NAME) == null) { 76 | Security.addProvider(new SpiffeProvider()); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /java-spiffe-provider/src/main/java/io/spiffe/provider/SpiffeProviderConstants.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.provider; 2 | 3 | /** 4 | * SPIFFE Provider constants. 5 | */ 6 | public final class SpiffeProviderConstants { 7 | 8 | /** 9 | * Name of the property to get the Set of accepted SPIFFE IDs. 10 | * This property is read in the java.security file or from a System property. 11 | */ 12 | public static final String SSL_SPIFFE_ACCEPT_PROPERTY = "ssl.spiffe.accept"; 13 | 14 | /** 15 | * Name of the property to be used as flag for accepting any SPIFFE IDs. 16 | * This property is read from the java.security file or from the System. 17 | */ 18 | public static final String SSL_SPIFFE_ACCEPT_ALL_PROPERTY = "ssl.spiffe.acceptAll"; 19 | 20 | /** 21 | * The name of this Provider implementation. 22 | */ 23 | public static final String PROVIDER_NAME = "Spiffe"; 24 | 25 | /** 26 | * The algorithm name for the KeyStore and TrustStore. 27 | */ 28 | public static final String ALGORITHM = "Spiffe"; 29 | 30 | /** 31 | * Alias used by the SpiffeKeyStore. 32 | * Note: KeyStore aliases are case-insensitive. 33 | */ 34 | public static final String DEFAULT_ALIAS = "spiffe"; 35 | 36 | private SpiffeProviderConstants() { 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /java-spiffe-provider/src/main/java/io/spiffe/provider/SpiffeVerificationException.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.provider; 2 | 3 | /** 4 | * This class indicates there was a problem verifying a peer's SPIFFE ID. The message should be used to indicate what 5 | * issue was encountered. 6 | */ 7 | public class SpiffeVerificationException extends Exception { 8 | public SpiffeVerificationException(String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /java-spiffe-provider/src/main/java/io/spiffe/provider/X509SourceManager.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.provider; 2 | 3 | import io.spiffe.exception.SocketEndpointAddressException; 4 | import io.spiffe.exception.X509SourceException; 5 | import io.spiffe.workloadapi.DefaultX509Source; 6 | import io.spiffe.workloadapi.X509Source; 7 | 8 | /** 9 | * Singleton that handles an instance of a {@link DefaultX509Source} that implements an {@link X509Source}. 10 | *

11 | * The default SPIFFE socket endpoint address is used to create an X.509 Source backed by the 12 | * Workload API. 13 | *

14 | * If the environment variable is not defined, it will throw an IllegalStateException. 15 | * If the X509Source cannot be initialized, it will throw a RuntimeException. 16 | *

17 | * This Singleton needed to be able to handle a single {@link DefaultX509Source} instance 18 | * to be used by the {@link SpiffeKeyManagerFactory} and {@link SpiffeTrustManagerFactory} to inject it 19 | * in the {@link SpiffeKeyManager} and {@link SpiffeTrustManager} instances. 20 | */ 21 | public final class X509SourceManager { 22 | 23 | private static X509Source x509Source; 24 | 25 | private X509SourceManager() { 26 | } 27 | 28 | /** 29 | * Returns the single instance handled by this singleton. If the instance has not been 30 | * created yet, it creates a new X509Source and initializes the singleton in a thread safe way. 31 | * 32 | * @return the single instance of {@link DefaultX509Source} 33 | * @throws X509SourceException if the X.509 source could not be initialized 34 | * @throws SocketEndpointAddressException is the socket endpoint address is not valid 35 | */ 36 | public static synchronized X509Source getX509Source() throws X509SourceException, SocketEndpointAddressException { 37 | if (x509Source == null) { 38 | x509Source = DefaultX509Source.newSource(); 39 | } 40 | return x509Source; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /java-spiffe-provider/src/main/java/io/spiffe/provider/exception/SpiffeProviderException.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.provider.exception; 2 | 3 | /** 4 | * Unchecked exception thrown when there is an error setting up the source of SVIDs and bundles. 5 | */ 6 | public class SpiffeProviderException extends RuntimeException { 7 | 8 | public SpiffeProviderException(final String message) { 9 | super(message); 10 | } 11 | 12 | public SpiffeProviderException(final String message, final Throwable cause) { 13 | super(message, cause); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /java-spiffe-provider/src/test/java/io/spiffe/provider/EnvironmentUtilsTest.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.provider; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.security.Security; 6 | 7 | import static org.junit.jupiter.api.Assertions.assertEquals; 8 | 9 | class EnvironmentUtilsTest { 10 | 11 | @Test 12 | void getPropertyFromSystem() { 13 | System.setProperty("testVariable", "example"); 14 | String value = EnvironmentUtils.getProperty("testVariable"); 15 | assertEquals("example", value); 16 | } 17 | 18 | @Test 19 | void getPropertyFromSecurity() { 20 | Security.setProperty("testVariable", "example"); 21 | String value = EnvironmentUtils.getProperty("testVariable"); 22 | assertEquals("example", value); 23 | } 24 | 25 | @Test 26 | void getSecurityPropertyWithDefaultValue() { 27 | Security.setProperty("testVariable", "example"); 28 | String value = EnvironmentUtils.getProperty("otherVariable", "default"); 29 | assertEquals("default", value); 30 | } 31 | 32 | @Test 33 | void getSystemPropertyWithDefaultValue() { 34 | System.setProperty("testVariable", "example"); 35 | String value = EnvironmentUtils.getProperty("testVariable", "default"); 36 | assertEquals("example", value); 37 | } 38 | 39 | @Test 40 | void getPropertyReturnBlankForNotFoundVariable() { 41 | String value = EnvironmentUtils.getProperty("unknown"); 42 | assertEquals("", value); 43 | } 44 | } -------------------------------------------------------------------------------- /java-spiffe-provider/src/test/java/io/spiffe/provider/SpiffeKeyManagerFactoryTest.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.provider; 2 | 3 | import io.spiffe.exception.X509SvidException; 4 | import io.spiffe.svid.x509svid.X509Svid; 5 | import io.spiffe.svid.x509svid.X509SvidSource; 6 | import io.spiffe.workloadapi.X509Source; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import javax.net.ssl.KeyManager; 10 | import java.lang.reflect.Field; 11 | import java.net.URISyntaxException; 12 | import java.nio.file.Path; 13 | import java.nio.file.Paths; 14 | import java.security.cert.X509Certificate; 15 | 16 | import static io.spiffe.provider.SpiffeProviderConstants.DEFAULT_ALIAS; 17 | import static io.spiffe.utils.TestUtils.toUri; 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | 20 | class SpiffeKeyManagerFactoryTest { 21 | 22 | @Test 23 | void engineGetKeyManagers_usingX509SourceManager() throws NoSuchFieldException, IllegalAccessException { 24 | // init singleton with an instance 25 | Field field = X509SourceManager.class.getDeclaredField("x509Source"); 26 | field.setAccessible(true); 27 | X509Source source = new X509SourceStub(); 28 | field.set(null, source); 29 | 30 | KeyManager[] keyManagers = new SpiffeKeyManagerFactory().engineGetKeyManagers(); 31 | SpiffeKeyManager keyManager = (SpiffeKeyManager) keyManagers[0]; 32 | 33 | X509Certificate[] chain = keyManager.getCertificateChain(DEFAULT_ALIAS); 34 | X509Certificate certificate = chain[0]; 35 | 36 | assertEquals(source.getX509Svid().getChain().get(0), certificate); 37 | } 38 | 39 | @Test 40 | void engineGetKeyManagers_passingAX509SvidSource() throws URISyntaxException, X509SvidException { 41 | Path cert = Paths.get(toUri("testdata/cert.pem")); 42 | Path key = Paths.get(toUri("testdata/key.pem")); 43 | X509Svid svid = X509Svid.load(cert, key); 44 | X509SvidSource x509SvidSource = () -> svid; 45 | 46 | KeyManager[] keyManagers = new SpiffeKeyManagerFactory().engineGetKeyManagers(x509SvidSource); 47 | SpiffeKeyManager keyManager = (SpiffeKeyManager) keyManagers[0]; 48 | 49 | assertEquals(svid.getChainArray()[0], keyManager.getCertificateChain(DEFAULT_ALIAS)[0]); 50 | } 51 | 52 | @Test 53 | void engineGetKeyManagers_nullParameter() { 54 | try { 55 | new SpiffeKeyManagerFactory().engineGetKeyManagers(null); 56 | } catch (NullPointerException e) { 57 | assertEquals("x509SvidSource is marked non-null but is null", e.getMessage()); 58 | } 59 | } 60 | 61 | @Test 62 | void engineInit() { 63 | new SpiffeKeyManagerFactory().engineInit(null); 64 | new SpiffeKeyManagerFactory().engineInit(null, null); 65 | 66 | } 67 | } -------------------------------------------------------------------------------- /java-spiffe-provider/src/test/java/io/spiffe/provider/SpiffeKeyStoreTest.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.provider; 2 | 3 | import org.junit.jupiter.api.BeforeAll; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.security.cert.Certificate; 7 | import java.util.Enumeration; 8 | 9 | import static io.spiffe.provider.SpiffeProviderConstants.DEFAULT_ALIAS; 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | import static org.junit.jupiter.api.Assertions.assertFalse; 12 | import static org.junit.jupiter.api.Assertions.assertNotNull; 13 | import static org.junit.jupiter.api.Assertions.assertNull; 14 | import static org.junit.jupiter.api.Assertions.assertTrue; 15 | 16 | class SpiffeKeyStoreTest { 17 | 18 | private static SpiffeKeyStore spiffeKeyStore; 19 | 20 | @BeforeAll 21 | static void setup() { 22 | spiffeKeyStore = new SpiffeKeyStore(); 23 | } 24 | @Test 25 | void engineGetKey() { 26 | assertNull(spiffeKeyStore.engineGetKey("alias", "pass".toCharArray())); 27 | } 28 | 29 | @Test 30 | void engineGetCertificateChain() { 31 | Certificate[] chain = spiffeKeyStore.engineGetCertificateChain("alias"); 32 | assertEquals(0, chain.length); 33 | } 34 | 35 | @Test 36 | void engineGetCertificate() { 37 | assertNull(spiffeKeyStore.engineGetCertificate("alias")); 38 | } 39 | 40 | @Test 41 | void engineGetCreationDate() { 42 | assertNotNull(spiffeKeyStore.engineGetCreationDate("alias")); 43 | } 44 | 45 | @Test 46 | void engineSetKeyEntry() { 47 | spiffeKeyStore.engineSetKeyEntry("alias", null, null); 48 | spiffeKeyStore.engineSetKeyEntry("alias", null, null, null); 49 | } 50 | 51 | @Test 52 | void testEngineSetKeyEntry() { 53 | spiffeKeyStore.engineSetKeyEntry("alias", null, null); 54 | spiffeKeyStore.engineSetKeyEntry("alias", null, null, null); 55 | } 56 | 57 | @Test 58 | void engineSetCertificateEntry() { 59 | spiffeKeyStore.engineSetCertificateEntry("alias", null); 60 | } 61 | 62 | @Test 63 | void engineDeleteEntry() { 64 | spiffeKeyStore.engineDeleteEntry("alias"); 65 | } 66 | 67 | @Test 68 | void engineAliases() { 69 | Enumeration enumeration = spiffeKeyStore.engineAliases(); 70 | assertEquals(DEFAULT_ALIAS, enumeration.nextElement()); 71 | } 72 | 73 | @Test 74 | void engineContainsAlias() { 75 | assertTrue(spiffeKeyStore.engineContainsAlias(DEFAULT_ALIAS)); 76 | } 77 | 78 | @Test 79 | void engineSize() { 80 | assertEquals(1, spiffeKeyStore.engineSize()); 81 | } 82 | 83 | @Test 84 | void engineIsKeyEntry() { 85 | assertTrue(spiffeKeyStore.engineIsKeyEntry(DEFAULT_ALIAS)); 86 | assertFalse(spiffeKeyStore.engineIsKeyEntry("alias")); 87 | } 88 | 89 | @Test 90 | void engineIsCertificateEntry() { 91 | assertTrue(spiffeKeyStore.engineIsCertificateEntry(DEFAULT_ALIAS)); 92 | assertFalse(spiffeKeyStore.engineIsCertificateEntry("alias")); 93 | } 94 | 95 | @Test 96 | void engineGetCertificateAlias() { 97 | assertEquals(DEFAULT_ALIAS, spiffeKeyStore.engineGetCertificateAlias(null)); 98 | } 99 | 100 | @Test 101 | void engineStore() { 102 | spiffeKeyStore.engineStore(null, null); 103 | } 104 | 105 | @Test 106 | void engineLoad() { 107 | spiffeKeyStore.engineLoad(null, null); 108 | } 109 | } -------------------------------------------------------------------------------- /java-spiffe-provider/src/test/java/io/spiffe/provider/SpiffeProviderTest.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.provider; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import javax.net.ssl.KeyManagerFactory; 6 | import javax.net.ssl.TrustManagerFactory; 7 | import java.security.NoSuchAlgorithmException; 8 | 9 | import static org.junit.jupiter.api.Assertions.assertNotNull; 10 | 11 | class SpiffeProviderTest { 12 | 13 | @Test 14 | void install() throws NoSuchAlgorithmException { 15 | SpiffeProvider.install(); 16 | KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(SpiffeProviderConstants.ALGORITHM); 17 | TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(SpiffeProviderConstants.ALGORITHM); 18 | assertNotNull(keyManagerFactory); 19 | assertNotNull(trustManagerFactory); 20 | 21 | // should do nothing 22 | SpiffeProvider.install(); 23 | } 24 | } -------------------------------------------------------------------------------- /java-spiffe-provider/src/test/java/io/spiffe/provider/X509SourceManagerTest.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.provider; 2 | 3 | import io.spiffe.utils.TestUtils; 4 | import io.spiffe.workloadapi.Address; 5 | import io.spiffe.workloadapi.X509Source; 6 | import org.junit.jupiter.api.Test; 7 | import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; 8 | 9 | import java.lang.reflect.Field; 10 | 11 | import static org.junit.jupiter.api.Assertions.assertEquals; 12 | 13 | class X509SourceManagerTest { 14 | 15 | @Test 16 | void getX509Source_returnTheX509SourceInstance() throws Exception { 17 | Field field = X509SourceManager.class.getDeclaredField("x509Source"); 18 | field.setAccessible(true); 19 | X509Source source = new X509SourceStub(); 20 | field.set(null, source); 21 | 22 | X509Source x509Source = X509SourceManager.getX509Source(); 23 | assertEquals(source, x509Source); 24 | } 25 | 26 | @Test 27 | void getX509Source_defaultAddressNotSet() throws Exception { 28 | new EnvironmentVariables(Address.SOCKET_ENV_VARIABLE, "").execute(() -> { 29 | try { 30 | X509SourceManager.getX509Source(); 31 | } catch (IllegalStateException e) { 32 | assertEquals("Endpoint Socket Address Environment Variable is not set: SPIFFE_ENDPOINT_SOCKET", e.getMessage()); 33 | } 34 | }); 35 | } 36 | } -------------------------------------------------------------------------------- /java-spiffe-provider/src/test/java/io/spiffe/provider/X509SourceStub.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.provider; 2 | 3 | import io.spiffe.bundle.x509bundle.X509Bundle; 4 | import io.spiffe.exception.BundleNotFoundException; 5 | import io.spiffe.exception.X509BundleException; 6 | import io.spiffe.exception.X509SvidException; 7 | import io.spiffe.spiffeid.TrustDomain; 8 | import io.spiffe.svid.x509svid.X509Svid; 9 | import io.spiffe.workloadapi.X509Source; 10 | import lombok.NonNull; 11 | 12 | import java.net.URISyntaxException; 13 | import java.nio.file.Path; 14 | import java.nio.file.Paths; 15 | 16 | import static io.spiffe.utils.TestUtils.toUri; 17 | 18 | public class X509SourceStub implements X509Source { 19 | 20 | private final X509Svid svid; 21 | private final X509Bundle bundle; 22 | 23 | public X509SourceStub() { 24 | try { 25 | Path cert = Paths.get(toUri("testdata/cert.pem")); 26 | Path key = Paths.get(toUri("testdata/key.pem")); 27 | svid = X509Svid.load(cert, key); 28 | bundle = X509Bundle.load( 29 | TrustDomain.parse("spiffe://example.org"), 30 | Paths.get(toUri("testdata/bundle.pem"))); 31 | } catch (X509SvidException | URISyntaxException | X509BundleException e) { 32 | throw new RuntimeException(e); 33 | } 34 | } 35 | 36 | @Override 37 | public X509Bundle getBundleForTrustDomain(@NonNull TrustDomain trustDomain) throws BundleNotFoundException { 38 | if (TrustDomain.parse("example.org").equals(trustDomain)) { 39 | return bundle; 40 | } 41 | throw new BundleNotFoundException("trustDomain not found"); 42 | } 43 | 44 | @Override 45 | public X509Svid getX509Svid() { 46 | return svid; 47 | } 48 | 49 | @Override 50 | public void close() { 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /java-spiffe-provider/src/test/java/io/spiffe/provider/examples/mtls/HttpsServer.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.provider.examples.mtls; 2 | 3 | import io.spiffe.exception.SocketEndpointAddressException; 4 | import io.spiffe.exception.X509SourceException; 5 | import io.spiffe.provider.SpiffeKeyManager; 6 | import io.spiffe.provider.SpiffeSslContextFactory; 7 | import io.spiffe.provider.SpiffeSslContextFactory.SslContextOptions; 8 | import io.spiffe.provider.SpiffeTrustManager; 9 | import io.spiffe.provider.X509SourceManager; 10 | import io.spiffe.provider.exception.SpiffeProviderException; 11 | import io.spiffe.workloadapi.X509Source; 12 | import lombok.val; 13 | 14 | import javax.net.ssl.SSLContext; 15 | import javax.net.ssl.SSLServerSocket; 16 | import javax.net.ssl.SSLServerSocketFactory; 17 | import javax.net.ssl.SSLSocket; 18 | import java.io.IOException; 19 | import java.security.KeyManagementException; 20 | import java.security.NoSuchAlgorithmException; 21 | 22 | /** 23 | * Example of a simple HTTPS Server backed by the Workload API to get the X.509 certificates 24 | * and trusted bundles. 25 | *

26 | * The purpose of this class is to show the use of the {@link SpiffeSslContextFactory} to create 27 | * a {@link SSLContext} that uses X.509-SVID provided by a Workload API. The SSLContext uses the 28 | * {@link SpiffeKeyManager} and {@link SpiffeTrustManager} for 29 | * providing certificates and doing chain and SPIFFE ID validation. 30 | * To run this example, Spire should be running, SPIFFE_ENDPOINT_SOCKET env variable should be 31 | * defined, and a property ssl.spiffe.accept should be defined in the java.security having a 32 | * spiffe id from a client workload. 33 | */ 34 | public class HttpsServer { 35 | 36 | int port; 37 | 38 | public static void main(String[] args) { 39 | HttpsServer httpsServer = new HttpsServer(4000); 40 | try { 41 | httpsServer.run(); 42 | } catch (IOException | KeyManagementException | NoSuchAlgorithmException e) { 43 | throw new RuntimeException("Error starting HttpsServer", e); 44 | } 45 | } 46 | 47 | HttpsServer(int port ) { 48 | this.port = port; 49 | } 50 | 51 | void run() throws IOException, KeyManagementException, NoSuchAlgorithmException { 52 | X509Source x509Source; 53 | try { 54 | x509Source = X509SourceManager.getX509Source(); 55 | } catch (SocketEndpointAddressException | X509SourceException e) { 56 | throw new SpiffeProviderException("Error at getting the X509Source instance", e); 57 | } 58 | 59 | val sslContextOptions = SslContextOptions 60 | .builder() 61 | .x509Source(x509Source) 62 | .build(); 63 | SSLContext sslContext = SpiffeSslContextFactory.getSslContext(sslContextOptions); 64 | 65 | SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory(); 66 | 67 | try (SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(port)) { 68 | // Server will validate Client chain and SPIFFE ID 69 | sslServerSocket.setNeedClientAuth(true); 70 | 71 | SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); 72 | new WorkloadThread(sslSocket, x509Source).start(); 73 | } 74 | } 75 | } 76 | 77 | 78 | -------------------------------------------------------------------------------- /java-spiffe-provider/src/test/java/io/spiffe/provider/examples/mtls/WorkloadThread.java: -------------------------------------------------------------------------------- 1 | package io.spiffe.provider.examples.mtls; 2 | 3 | import io.spiffe.internal.CertificateUtils; 4 | import io.spiffe.spiffeid.SpiffeId; 5 | import io.spiffe.workloadapi.X509Source; 6 | import lombok.extern.java.Log; 7 | 8 | import javax.net.ssl.SSLSession; 9 | import javax.net.ssl.SSLSocket; 10 | import java.io.BufferedReader; 11 | import java.io.InputStream; 12 | import java.io.InputStreamReader; 13 | import java.io.OutputStream; 14 | import java.io.OutputStreamWriter; 15 | import java.io.PrintWriter; 16 | import java.security.cert.X509Certificate; 17 | import java.util.logging.Level; 18 | 19 | @Log 20 | class WorkloadThread extends Thread { 21 | 22 | private final X509Source x509Source; 23 | private SSLSocket sslSocket; 24 | 25 | WorkloadThread(SSLSocket sslSocket, X509Source x509Source) { 26 | this.sslSocket = sslSocket; 27 | this.x509Source = x509Source; 28 | } 29 | 30 | 31 | @Override 32 | public void run() { 33 | try { 34 | sslSocket.startHandshake(); 35 | SSLSession sslSession = sslSocket.getSession(); 36 | 37 | log.info("SSLSession :\n"); 38 | log.info("\tProtocol : \n" + sslSession.getProtocol()); 39 | log.info("\tCipher suite \n: " + sslSession.getCipherSuite()); 40 | 41 | // Start handling application content 42 | InputStream inputStream = sslSocket.getInputStream(); 43 | OutputStream outputStream = sslSocket.getOutputStream(); 44 | 45 | BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); 46 | PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(outputStream)); 47 | 48 | SpiffeId peerSpiffeId = CertificateUtils 49 | .getSpiffeId((X509Certificate) sslSession.getPeerCertificates()[0]); 50 | 51 | SpiffeId mySpiffeId = CertificateUtils 52 | .getSpiffeId((X509Certificate) sslSession.getLocalCertificates()[0]); 53 | 54 | // Send message to peer 55 | printWriter.printf("Hello %s, I'm %s", peerSpiffeId, mySpiffeId); 56 | printWriter.println(); 57 | printWriter.flush(); 58 | 59 | // Read message from peer 60 | String line; 61 | while ((line = bufferedReader.readLine()) != null) { 62 | log.info("Message received: " + line); 63 | break; 64 | } 65 | 66 | x509Source.close(); 67 | sslSocket.close(); 68 | } catch (Exception e) { 69 | log.log(Level.SEVERE, e.getMessage()); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /java-spiffe-provider/src/test/resources/testdata/bundle.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBzDCCAVGgAwIBAgIBADAKBggqhkjOPQQDAzAeMQswCQYDVQQGEwJVUzEPMA0G 3 | A1UEChMGU1BJRkZFMB4XDTIwMDMxNjE4MDQ1N1oXDTIwMDMyMzE4MDUwN1owHjEL 4 | MAkGA1UEBhMCVVMxDzANBgNVBAoTBlNQSUZGRTB2MBAGByqGSM49AgEGBSuBBAAi 5 | A2IABOStlk5qbbCuuRqKitTqTzQf/UnlljO2bHAekgz+0na4oJhcsOYhRLbKQVcr 6 | U5jf7BpcT7nYHyLMv79Cy+Xa1snzMCGhx1obgt8gAXb4b4GwyAiNzaq7ytX4SAZB 7 | CUXp36NjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O 8 | BBYEFCMzt4WGhen9N2N+MwzUkwEtG6YLMB8GA1UdEQQYMBaGFHNwaWZmZTovL2V4 9 | YW1wbGUub3JnMAoGCCqGSM49BAMDA2kAMGYCMQDJSIl+8jQek7tHRSfU2vYLQ6sy 10 | 2t4NJcc5zeSjNxFIajUogl7L8T1QNzrKpXm/ZbQCMQDU54VnA1Awq0Qq2pf4zQ1C 11 | KOpj4PguMpUmulRVrzD2KNQz+FUKsHgL/mIZSfN5g1M= 12 | -----END CERTIFICATE----- 13 | -------------------------------------------------------------------------------- /java-spiffe-provider/src/test/resources/testdata/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICADCCAYagAwIBAgIQJqqLnXOR3tjUV43PpGW4YDAKBggqhkjOPQQDAzAeMQsw 3 | CQYDVQQGEwJVUzEPMA0GA1UEChMGU1BJRkZFMB4XDTIwMDMxODE5MDc1MloXDTIw 4 | MDMyMzE4MDUwN1owHTELMAkGA1UEBhMCVVMxDjAMBgNVBAoTBVNQSVJFMFkwEwYH 5 | KoZIzj0CAQYIKoZIzj0DAQcDQgAE4/NA2umeK9j1U5+egPqfzomjnpnLz68jNvUN 6 | tdA0Lg6E7/nsmvqoNbVVbaD84Jplfg6/6HWSnoO7K7A+oZ1g+qOBpjCBozAOBgNV 7 | HQ8BAf8EBAMCA6gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1Ud 8 | EwEB/wQCMAAwHQYDVR0OBBYEFPLf59AkFCva6ehKx8L4i+pjCU1CMB8GA1UdIwQY 9 | MBaAFCMzt4WGhen9N2N+MwzUkwEtG6YLMCQGA1UdEQQdMBuGGXNwaWZmZTovL2V4 10 | YW1wbGUub3JnL3Rlc3QwCgYIKoZIzj0EAwMDaAAwZQIwRzrN6Rh8X28UYJEuql/1 11 | GZEeto7zzj0UtjZFwQy2ODl48nFFRGKUnq8mc4cIMI/kAjEAlixJBUHqb4ty8Ff+ 12 | d0XHzA5duE1hzFxd2feRppjqiOHKJu7Rh2dzZd3rZhMZWrrd 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /java-spiffe-provider/src/test/resources/testdata/cert2.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIB/TCCAYSgAwIBAgIQRWYURsMOUzpIOD9/uyhK/zAKBggqhkjOPQQDAzAeMQsw 3 | CQYDVQQGEwJVUzEPMA0GA1UEChMGU1BJRkZFMB4XDTIwMDMxOTE5MzAyM1oXDTIw 4 | MDMxOTIwMzAzM1owHTELMAkGA1UEBhMCVVMxDjAMBgNVBAoTBVNQSVJFMFkwEwYH 5 | KoZIzj0CAQYIKoZIzj0DAQcDQgAEdJg/X2P9fl6miUX/WtVj/Aeen0urPUI5rwI4 6 | 0q21AugzjbXbk+XzXj7VfUNyqHsfWx4VZo9sTg3zYgnTTdQhvKOBpDCBoTAOBgNV 7 | HQ8BAf8EBAMCA6gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1Ud 8 | EwEB/wQCMAAwHQYDVR0OBBYEFO/QWXfD2Rkf2SttF7fdaYfY8a/8MB8GA1UdIwQY 9 | MBaAFNEafrZejn4vo7x0pFOzvJJcvmuQMCIGA1UdEQQbMBmGF3NwaWZmZTovL290 10 | aGVyLm9yZy90ZXN0MAoGCCqGSM49BAMDA2cAMGQCMALzVBadygedI0YSE1VFDEtW 11 | D+QVECNd4ax+3nKswJk3bLj+9hBswLajuKHVULOgLgIwW85beZr9jklhz6MvdbM5 12 | TeE8X+rXG9fdELMClZI/k2+IwtFua4la7ikb+xh3ZUnA 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /java-spiffe-provider/src/test/resources/testdata/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgGSpi81I7JqXLgi9O 3 | CvwUSIdt7Ep/Ki7iupcHTYAziSWhRANCAATj80Da6Z4r2PVTn56A+p/OiaOemcvP 4 | ryM29Q210DQuDoTv+eya+qg1tVVtoPzgmmV+Dr/odZKeg7srsD6hnWD6 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /java-spiffe-provider/src/test/resources/testdata/key2.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgUZSGAqMXSIyjJe4c 3 | 43nEUp/eAnzG5RprAnDZIi2vb2yhRANCAAR0mD9fY/1+XqaJRf9a1WP8B56fS6s9 4 | QjmvAjjSrbUC6DONtduT5fNePtV9Q3Koex9bHhVmj2xODfNiCdNN1CG8 5 | -----END PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /java-spiffe-provider/src/test/resources/testdata/spiffeIds.txt: -------------------------------------------------------------------------------- 1 | spiffe://example.org/workload-server 2 | spiffe://example.org/workload-server2 3 | spiffe://example2.org/workload-server 4 | -------------------------------------------------------------------------------- /lombok.config: -------------------------------------------------------------------------------- 1 | lombok.addLombokGeneratedAnnotation = true 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'java-spiffe' 2 | include 'java-spiffe-core' 3 | include 'java-spiffe-provider' 4 | include 'java-spiffe-helper' 5 | include 'java-spiffe-core:grpc-netty-linux' 6 | include 'java-spiffe-core:grpc-netty-macos' 7 | include 'java-spiffe-core:grpc-netty-macos-aarch64' --------------------------------------------------------------------------------