Showing preview only (1,301K chars total). Download the full file or copy to clipboard to get everything.
Repository: shred/acme4j
Branch: master
Commit: 0f3b7de23b3c
Files: 327
Total size: 1.2 MB
Directory structure:
gitextract_nzsxpl3o/
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── .gitlab-ci.yml
├── CONTRIBUTING.md
├── LICENSE-APL.txt
├── README.md
├── acme4j-client/
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ ├── .gitignore
│ │ │ ├── module-info.java
│ │ │ └── org/
│ │ │ └── shredzone/
│ │ │ └── acme4j/
│ │ │ ├── Account.java
│ │ │ ├── AccountBuilder.java
│ │ │ ├── AcmeJsonResource.java
│ │ │ ├── AcmeResource.java
│ │ │ ├── Authorization.java
│ │ │ ├── Certificate.java
│ │ │ ├── Identifier.java
│ │ │ ├── Login.java
│ │ │ ├── Metadata.java
│ │ │ ├── Order.java
│ │ │ ├── OrderBuilder.java
│ │ │ ├── PollableResource.java
│ │ │ ├── Problem.java
│ │ │ ├── RenewalInfo.java
│ │ │ ├── RevocationReason.java
│ │ │ ├── Session.java
│ │ │ ├── Status.java
│ │ │ ├── challenge/
│ │ │ │ ├── Challenge.java
│ │ │ │ ├── Dns01Challenge.java
│ │ │ │ ├── DnsAccount01Challenge.java
│ │ │ │ ├── DnsPersist01Challenge.java
│ │ │ │ ├── Http01Challenge.java
│ │ │ │ ├── TlsAlpn01Challenge.java
│ │ │ │ ├── TokenChallenge.java
│ │ │ │ └── package-info.java
│ │ │ ├── connector/
│ │ │ │ ├── Connection.java
│ │ │ │ ├── DefaultConnection.java
│ │ │ │ ├── HttpConnector.java
│ │ │ │ ├── NetworkSettings.java
│ │ │ │ ├── NonceHolder.java
│ │ │ │ ├── RequestSigner.java
│ │ │ │ ├── Resource.java
│ │ │ │ ├── ResourceIterator.java
│ │ │ │ ├── TrimmingInputStream.java
│ │ │ │ └── package-info.java
│ │ │ ├── exception/
│ │ │ │ ├── AcmeException.java
│ │ │ │ ├── AcmeLazyLoadingException.java
│ │ │ │ ├── AcmeNetworkException.java
│ │ │ │ ├── AcmeNotSupportedException.java
│ │ │ │ ├── AcmeProtocolException.java
│ │ │ │ ├── AcmeRateLimitedException.java
│ │ │ │ ├── AcmeServerException.java
│ │ │ │ ├── AcmeUnauthorizedException.java
│ │ │ │ ├── AcmeUserActionRequiredException.java
│ │ │ │ └── package-info.java
│ │ │ ├── package-info.java
│ │ │ ├── provider/
│ │ │ │ ├── AbstractAcmeProvider.java
│ │ │ │ ├── AcmeProvider.java
│ │ │ │ ├── ChallengeProvider.java
│ │ │ │ ├── ChallengeType.java
│ │ │ │ ├── GenericAcmeProvider.java
│ │ │ │ ├── actalis/
│ │ │ │ │ ├── ActalisAcmeProvider.java
│ │ │ │ │ └── package-info.java
│ │ │ │ ├── google/
│ │ │ │ │ ├── GoogleAcmeProvider.java
│ │ │ │ │ └── package-info.java
│ │ │ │ ├── letsencrypt/
│ │ │ │ │ ├── LetsEncryptAcmeProvider.java
│ │ │ │ │ └── package-info.java
│ │ │ │ ├── package-info.java
│ │ │ │ ├── pebble/
│ │ │ │ │ ├── PebbleAcmeProvider.java
│ │ │ │ │ └── package-info.java
│ │ │ │ ├── sslcom/
│ │ │ │ │ ├── SslComAcmeProvider.java
│ │ │ │ │ └── package-info.java
│ │ │ │ └── zerossl/
│ │ │ │ ├── ZeroSSLAcmeProvider.java
│ │ │ │ └── package-info.java
│ │ │ ├── toolbox/
│ │ │ │ ├── AcmeUtils.java
│ │ │ │ ├── JSON.java
│ │ │ │ ├── JSONBuilder.java
│ │ │ │ ├── JoseUtils.java
│ │ │ │ └── package-info.java
│ │ │ └── util/
│ │ │ ├── CSRBuilder.java
│ │ │ ├── CertificateUtils.java
│ │ │ ├── KeyPairUtils.java
│ │ │ └── package-info.java
│ │ ├── resources/
│ │ │ ├── .gitignore
│ │ │ ├── META-INF/
│ │ │ │ └── services/
│ │ │ │ └── org.shredzone.acme4j.provider.AcmeProvider
│ │ │ └── org/
│ │ │ └── shredzone/
│ │ │ └── acme4j/
│ │ │ └── provider/
│ │ │ └── pebble/
│ │ │ └── pebble.minica.pem
│ │ └── resources-filtered/
│ │ └── org/
│ │ └── shredzone/
│ │ └── acme4j/
│ │ └── version.properties
│ └── test/
│ ├── java/
│ │ ├── .gitignore
│ │ └── org/
│ │ └── shredzone/
│ │ └── acme4j/
│ │ ├── AccountBuilderTest.java
│ │ ├── AccountTest.java
│ │ ├── AcmeJsonResourceTest.java
│ │ ├── AcmeResourceTest.java
│ │ ├── AuthorizationTest.java
│ │ ├── CertificateTest.java
│ │ ├── IdentifierTest.java
│ │ ├── LoginTest.java
│ │ ├── OrderBuilderTest.java
│ │ ├── OrderTest.java
│ │ ├── ProblemTest.java
│ │ ├── RenewalInfoTest.java
│ │ ├── SessionTest.java
│ │ ├── StatusTest.java
│ │ ├── challenge/
│ │ │ ├── ChallengeTest.java
│ │ │ ├── Dns01ChallengeTest.java
│ │ │ ├── DnsAccount01ChallengeTest.java
│ │ │ ├── DnsPersist01ChallengeTest.java
│ │ │ ├── Http01ChallengeTest.java
│ │ │ ├── TlsAlpn01ChallengeTest.java
│ │ │ └── TokenChallengeTest.java
│ │ ├── connector/
│ │ │ ├── DefaultConnectionTest.java
│ │ │ ├── DummyConnection.java
│ │ │ ├── HttpConnectorTest.java
│ │ │ ├── NetworkSettingsTest.java
│ │ │ ├── ResourceIteratorTest.java
│ │ │ ├── ResourceTest.java
│ │ │ ├── SessionProviderTest.java
│ │ │ └── TrimmingInputStreamTest.java
│ │ ├── exception/
│ │ │ ├── AcmeExceptionTest.java
│ │ │ ├── AcmeLazyLoadingExceptionTest.java
│ │ │ ├── AcmeNetworkExceptionTest.java
│ │ │ ├── AcmeNotSupportedExceptionTest.java
│ │ │ ├── AcmeProtocolExceptionTest.java
│ │ │ ├── AcmeRateLimitedExceptionTest.java
│ │ │ └── AcmeUserActionRequiredExceptionTest.java
│ │ ├── provider/
│ │ │ ├── AbstractAcmeProviderTest.java
│ │ │ ├── GenericAcmeProviderTest.java
│ │ │ ├── TestableConnectionProvider.java
│ │ │ ├── actalis/
│ │ │ │ └── ActalisAcmeProviderTest.java
│ │ │ ├── google/
│ │ │ │ └── GoogleAcmeProviderTest.java
│ │ │ ├── letsencrypt/
│ │ │ │ └── LetsEncryptAcmeProviderTest.java
│ │ │ ├── pebble/
│ │ │ │ └── PebbleAcmeProviderTest.java
│ │ │ ├── sslcom/
│ │ │ │ └── SslComAcmeProviderTest.java
│ │ │ └── zerossl/
│ │ │ └── ZeroSSLAcmeProviderTest.java
│ │ ├── toolbox/
│ │ │ ├── AcmeUtilsTest.java
│ │ │ ├── JSONBuilderTest.java
│ │ │ ├── JSONTest.java
│ │ │ ├── JoseUtilsTest.java
│ │ │ └── TestUtils.java
│ │ └── util/
│ │ ├── CSRBuilderTest.java
│ │ ├── CertificateUtilsTest.java
│ │ └── KeyPairUtilsTest.java
│ └── resources/
│ ├── .gitignore
│ ├── META-INF/
│ │ └── services/
│ │ └── org.shredzone.acme4j.provider.AcmeProvider
│ ├── ari-example-cert.pem
│ ├── cert.pem
│ ├── certid-cert.pem
│ ├── csr.der
│ ├── domain-private.key
│ ├── domain-public.key
│ ├── json/
│ │ ├── authorizationChallenges.json
│ │ ├── canceledOrderResponse.json
│ │ ├── datatypes.json
│ │ ├── deactivateAccountResponse.json
│ │ ├── directory.json
│ │ ├── directoryNoMeta.json
│ │ ├── dns01Challenge.json
│ │ ├── dnsAccount01Challenge.json
│ │ ├── dnsPersist01Challenge.json
│ │ ├── finalizeAutoRenewResponse.json
│ │ ├── finalizeRequest.json
│ │ ├── finalizeResponse.json
│ │ ├── genericChallenge.json
│ │ ├── httpChallenge.json
│ │ ├── httpNoTokenChallenge.json
│ │ ├── modifyAccount.json
│ │ ├── modifyAccountResponse.json
│ │ ├── newAccount.json
│ │ ├── newAccountOnlyExisting.json
│ │ ├── newAccountResponse.json
│ │ ├── newAuthorizationRequest.json
│ │ ├── newAuthorizationRequestSub.json
│ │ ├── newAuthorizationResponse.json
│ │ ├── newAuthorizationResponseSub.json
│ │ ├── problem.json
│ │ ├── renewalInfo.json
│ │ ├── replacedCertificateRequest.json
│ │ ├── requestAutoRenewOrderRequest.json
│ │ ├── requestAutoRenewOrderResponse.json
│ │ ├── requestCertificateRequest.json
│ │ ├── requestCertificateRequestWithDate.json
│ │ ├── requestOrderRequest.json
│ │ ├── requestOrderRequestSub.json
│ │ ├── requestOrderResponse.json
│ │ ├── requestOrderResponseSub.json
│ │ ├── requestProfileOrderRequest.json
│ │ ├── requestProfileOrderResponse.json
│ │ ├── requestReplacesRequest.json
│ │ ├── requestReplacesResponse.json
│ │ ├── revokeCertificateRequest.json
│ │ ├── revokeCertificateWithReasonRequest.json
│ │ ├── tlsAlpnChallenge.json
│ │ ├── triggerHttpChallenge.json
│ │ ├── triggerHttpChallengeRequest.json
│ │ ├── triggerHttpChallengeResponse.json
│ │ ├── updateAccount.json
│ │ ├── updateAccountResponse.json
│ │ ├── updateAuthorizationResponse.json
│ │ ├── updateAuthorizationWildcardResponse.json
│ │ ├── updateAutoRenewOrderResponse.json
│ │ ├── updateHttpChallengeResponse.json
│ │ └── updateOrderResponse.json
│ ├── private.key
│ ├── public.key
│ └── simplelogger.properties
├── acme4j-example/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ ├── .gitignore
│ │ │ ├── module-info.java
│ │ │ └── org/
│ │ │ └── shredzone/
│ │ │ └── acme4j/
│ │ │ └── example/
│ │ │ └── ClientTest.java
│ │ └── resources/
│ │ ├── .gitignore
│ │ └── simplelogger.properties
│ └── test/
│ ├── java/
│ │ └── .gitignore
│ └── resources/
│ └── .gitignore
├── acme4j-it/
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── docker/
│ │ │ ├── challtestsrv.dockerfile
│ │ │ └── challtestsrv.sh
│ │ ├── java/
│ │ │ ├── module-info.java
│ │ │ └── org/
│ │ │ └── shredzone/
│ │ │ └── acme4j/
│ │ │ └── it/
│ │ │ ├── BammBammClient.java
│ │ │ └── package-info.java
│ │ └── resources/
│ │ └── .gitignore
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── shredzone/
│ │ └── acme4j/
│ │ └── it/
│ │ ├── ProviderIT.java
│ │ ├── SoftFail.java
│ │ ├── SoftFailExtension.java
│ │ ├── boulder/
│ │ │ └── OrderHttpIT.java
│ │ └── pebble/
│ │ ├── AccountIT.java
│ │ ├── OrderIT.java
│ │ ├── OrderWildcardIT.java
│ │ ├── PebbleITBase.java
│ │ └── SessionIT.java
│ └── resources/
│ └── simplelogger.properties
├── acme4j-smime/
│ ├── .gitattributes
│ ├── pom.xml
│ ├── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── module-info.java
│ │ │ │ └── org/
│ │ │ │ └── shredzone/
│ │ │ │ └── acme4j/
│ │ │ │ └── smime/
│ │ │ │ ├── EmailIdentifier.java
│ │ │ │ ├── challenge/
│ │ │ │ │ ├── EmailReply00Challenge.java
│ │ │ │ │ ├── EmailReply00ChallengeProvider.java
│ │ │ │ │ └── package-info.java
│ │ │ │ ├── csr/
│ │ │ │ │ ├── KeyUsageType.java
│ │ │ │ │ ├── SMIMECSRBuilder.java
│ │ │ │ │ └── package-info.java
│ │ │ │ ├── email/
│ │ │ │ │ ├── EmailProcessor.java
│ │ │ │ │ ├── ResponseBodyGenerator.java
│ │ │ │ │ ├── ResponseGenerator.java
│ │ │ │ │ └── package-info.java
│ │ │ │ ├── exception/
│ │ │ │ │ ├── AcmeInvalidMessageException.java
│ │ │ │ │ └── package-info.java
│ │ │ │ ├── package-info.java
│ │ │ │ └── wrapper/
│ │ │ │ ├── Mail.java
│ │ │ │ ├── SignedMail.java
│ │ │ │ ├── SignedMailBuilder.java
│ │ │ │ ├── SimpleMail.java
│ │ │ │ └── package-info.java
│ │ │ └── resources/
│ │ │ ├── .gitignore
│ │ │ └── META-INF/
│ │ │ └── services/
│ │ │ └── org.shredzone.acme4j.provider.ChallengeProvider
│ │ └── test/
│ │ ├── java/
│ │ │ ├── .gitignore
│ │ │ └── org/
│ │ │ └── shredzone/
│ │ │ └── acme4j/
│ │ │ └── smime/
│ │ │ ├── EmailIdentifierTest.java
│ │ │ ├── SMIMETests.java
│ │ │ ├── challenge/
│ │ │ │ └── EmailReply00ChallengeTest.java
│ │ │ ├── csr/
│ │ │ │ └── SMIMECSRBuilderTest.java
│ │ │ ├── email/
│ │ │ │ └── EmailProcessorTest.java
│ │ │ └── wrapper/
│ │ │ ├── SignedMailBuilderTest.java
│ │ │ └── SignedMailTest.java
│ │ └── resources/
│ │ ├── .gitignore
│ │ ├── 7508-fake-ca.jks
│ │ ├── 7508-valid-ca.jks
│ │ ├── email/
│ │ │ ├── challenge.eml
│ │ │ ├── invalid-cert-mismatch.eml
│ │ │ ├── invalid-nosan.eml
│ │ │ ├── invalid-protected-mail-from.eml
│ │ │ ├── invalid-protected-mail-subject.eml
│ │ │ ├── invalid-protected-mail-to.eml
│ │ │ ├── invalid-signed-mail.eml
│ │ │ ├── valid-mail-7508.eml
│ │ │ └── valid-mail.eml
│ │ ├── invalid-signer-privkey.pem
│ │ ├── invalid-signer.pem
│ │ ├── json/
│ │ │ ├── emailReplyChallenge.json
│ │ │ └── emailReplyChallengeMismatch.json
│ │ ├── key.pem
│ │ ├── valid-signer-nosan-privkey.pem
│ │ ├── valid-signer-nosan.pem
│ │ ├── valid-signer-privkey.pem
│ │ └── valid-signer.pem
│ └── tool/
│ ├── smime-generator.py
│ └── test-key-generator.sh
├── pom.xml
└── src/
└── doc/
├── docs/
│ ├── ca/
│ │ ├── actalis.md
│ │ ├── google.md
│ │ ├── index.md
│ │ ├── letsencrypt.md
│ │ ├── pebble.md
│ │ ├── sslcom.md
│ │ └── zerossl.md
│ ├── challenge/
│ │ ├── dns-01.md
│ │ ├── dns-account-01.md
│ │ ├── dns-persist-01.md
│ │ ├── email-reply-00.md
│ │ ├── http-01.md
│ │ ├── index.md
│ │ └── tls-alpn-01.md
│ ├── development/
│ │ ├── challenge.md
│ │ ├── index.md
│ │ ├── provider.md
│ │ └── testing.md
│ ├── example.md
│ ├── faq.md
│ ├── index.md
│ ├── migration.md
│ └── usage/
│ ├── account.md
│ ├── advanced.md
│ ├── connecting.md
│ ├── debugging.md
│ ├── errors.md
│ ├── exceptions.md
│ ├── index.md
│ ├── order.md
│ ├── persistence.md
│ ├── renewal.md
│ └── revocation.md
├── mkdocs.yml
└── theme/
├── breadcrumbs.html
├── css/
│ ├── font.css
│ ├── theme_custom.css
│ └── theme_pygments.css
├── footer.html
└── main.html
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
github: shred
ko_fi: shredzone
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
pull_request:
branches:
- master
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-22.04
strategy:
matrix:
java: [ '17', '21' ]
steps:
- uses: actions/checkout@v2
- name: Set up JDK
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: ${{ matrix.java }}
- name: Build
run: mvn --no-transfer-progress -B -P ci verify
================================================
FILE: .gitignore
================================================
target
================================================
FILE: .gitlab-ci.yml
================================================
stages:
- build
- test
variables:
DOCKER_TLS_CERTDIR: "/certs"
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_VERIFY: "1"
DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client"
image: maven:3-eclipse-temurin-17
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- /root/.m2/repository/
- acme4j-client/target/
- acme4j-example/target/
- acme4j-it/target/
- acme4j-smime/target/
- target/
build:
stage: build
script:
- apt-get update -y && apt-get install -y mkdocs
- mvn -B clean install mkdocs:build
test:
stage: test
services:
- name: docker:dind
alias: pebble, bammbamm
script:
- (cd acme4j-it; mvn -B docker:remove)
- mvn -B -P ci verify -DpebbleHost=pebble -DbammbammUrl=http://bammbamm:8055 -Ddocker.showLogs=true
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to _acme4j_
Thank you for taking your time to contribute!
## Acceptance Criteria
These criteria must be met for a successful pull request:
* Follow the [Style Guide](#style-guide).
* If you add code, remember to add [unit tests](#unit-tests) that test your code.
* All unit tests must run successfully.
* Integration tests should run successfully, unless there is a good reason (e.g. waiting for a pending change in Pebble).
* Your commits follow the [git commit](#git-commits) guide.
* You accept that your code is distributed under the terms of [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0).
* You confirm that you did not use AI based code generators like GitHub Copilot for your contribution.
## Style Guide
Our style guide bases on [Oracle's Code conventions for the Java Programming Language](http://www.oracle.com/technetwork/java/codeconventions-150003.pdf). These additional rules apply:
* Indentation is 4 spaces. Do not use tabs!
* Remove trailing spaces.
* Line length is 90 characters. You may exceed this length by a few characters if it is easier to read than a wrapped line.
* `if`, `for` and `while` statements always use blocks, even for a single statement.
* All types and methods must have a descriptive JavaDoc, except of `@Override` annotated methods. For plain getter and setter methods, `@param` and `@return` can be omitted.
## Unit Tests
More than 80% of the code is covered by unit tests, and we would like to keep it that way.
* Main functionalities must be covered by unit tests.
* Corner cases should be covered by unit tests.
* Common exception handling does not need to be tested.
* No tests are required for code that is not expected to be executed (e.g. `UnsupportedEncodingException` when handling utf-8, or the empty private default constructor of a utility class).
* Unit tests should not depend on external resources, as they might be temporarily unavailable at runtime.
There are no unit tests required for the `acme4j-example` and `acme4j-it` modules.
## git Commits
Good programming does not end with a clean source code, but should have pretty commits as well.
* Always put separate concerns into separate commits.
* If you have interim commits in your history, squash them with an interactive rebase before sending the pull request.
* Use present tense and imperative mood in commit messages ("fix bug #1234", not "fixed bug #1234").
* Always give meaningful commit messages (not just "bug fix").
* The commit message must be concise and should not exceed 50 characters. Further explanations may follow in subsequent lines, with an empty line as separator.
* Commits must compile and must not break unit tests.
* Do not commit IDE generated files and directories (like `.project` or `.idea`).
================================================
FILE: LICENSE-APL.txt
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# ACME Java Client  
This is a Java client for the _Automatic Certificate Management Environment_ (ACME) protocol as specified in [RFC 8555](https://tools.ietf.org/html/rfc8555).
ACME is a protocol that a certificate authority (CA) and an applicant can use to automate the process of verification and certificate issuance.
This Java client helps to connect to an ACME server, and performing all necessary steps to manage certificates.
## Features
* Mature and stable code base. First release was in December 2015!
* Fully [RFC 8555](https://tools.ietf.org/html/rfc8555) compliant
* Supports the `http-01`, `dns-01`, and `tls-alpn-01` ([RFC 8737](https://tools.ietf.org/html/rfc8737)) challenges
* Supports [RFC 8738](https://tools.ietf.org/html/rfc8738) IP identifier validation
* Supports [RFC 8739](https://tools.ietf.org/html/rfc8739) short-term automatic certificate renewal (experimental)
* Supports [RFC 8823](https://tools.ietf.org/html/rfc8823) for S/MIME certificates (experimental)
* Supports [RFC 9444](https://tools.ietf.org/html/rfc9444) for subdomain validation
* Supports [RFC 9773](https://tools.ietf.org/html/rfc9773) for renewal information
* Supports [draft-ietf-acme-profiles-01](https://datatracker.ietf.org/doc/draft-ietf-acme-profiles/) for certificate profiles (experimental)
* Supports [draft-ietf-acme-dns-account-label-02](https://datatracker.ietf.org/doc/draft-ietf-acme-dns-account-label/) for DNS labeled with ACME account ID challenges (experimental)
* Supports [draft-ietf-acme-dns-persist-01](https://datatracker.ietf.org/doc/draft-ietf-acme-dns-persist/) for dns-persist-01 challenges (experimental)
* Easy to use Java API
* Requires JRE 17 or higher
* Supports [Actalis](https://www.actalis.com/), [Google Trust Services](https://pki.goog/), [Let's Encrypt](https://letsencrypt.org/), [SSL.com](https://www.ssl.com/), [ZeroSSL](https://zerossl.com/), and **all other CAs that comply with the ACME protocol (RFC 8555)**. Note that _acme4j_ is an independent project that is not supported or endorsed by any of the CAs.
* Built with maven, packages available at [Maven Central](http://search.maven.org/#search|ga|1|g%3A%22org.shredzone.acme4j%22)
* Extensive unit and integration tests
* Adheres to [Semantic Versioning](https://semver.org/)
## Dependencies
* [Bouncy Castle](https://www.bouncycastle.org/)
* [jose4j](https://bitbucket.org/b_c/jose4j/wiki/Home)
* [slf4j](http://www.slf4j.org/)
* For `acme4j-smime`: [Jakarta Mail](https://eclipse-ee4j.github.io/mail/), [Bouncy Castle](https://www.bouncycastle.org/)
## Usage
* See the [online documentation](https://shredzone.org/maven/acme4j/) about how to use _acme4j_.
* For a quick start, take a look at [the source code of an example](https://shredzone.org/maven/acme4j/example.html).
## Announcements
Follow our Mastodon feed for release notes and other acme4j related news.
* Mastodon: `@acme4j@foojay.social`
* RSS: https://foojay.social/@acme4j.rss
## Contribute
* Fork the [Source code at Codeberg](https://codeberg.org/shred/acme4j) or [GitHub](https://github.com/shred/acme4j). Feel free to send pull requests (see [Contributing](CONTRIBUTING.md) for the rules).
* Found a bug? [File a bug report!](https://codeberg.org/shred/acme4j/issues) ([GitHub](https://github.com/shred/acme4j/issues))
## License
_acme4j_ is open source software. The source code is distributed under the terms of [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0).
## Acknowledgements
* I would like to thank Brian Campbell and all the other [jose4j](https://bitbucket.org/b_c/jose4j/wiki/Home) developers. _acme4j_ would not exist without your excellent work.
* Thanks to [Daniel McCarney](https://github.com/cpu) for his help with the ACME protocol, Pebble, and Boulder.
* [Ulrich Krause](https://github.com/eknori) for his help to make _acme4j_ run on IBM Java VMs.
* I also like to thank [everyone who contributed to _acme4j_](https://codeberg.org/shred/acme4j/activity/contributors).
* The Mastodon account is hosted by [foojay.io](https://foojay.io).
================================================
FILE: acme4j-client/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!--
*
* acme4j - ACME Java client
*
* Copyright (C) 2015 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.shredzone.acme4j</groupId>
<artifactId>acme4j</artifactId>
<version>5.1.1-SNAPSHOT</version>
</parent>
<artifactId>acme4j-client</artifactId>
<name>acme4j Client</name>
<description>ACME client for Java</description>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/resources-filtered</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<!-- Required because unit tests provides an own AcmeProvider
which cannot be added to the module-info definition. -->
<useModulePath>false</useModulePath>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.bitbucket.b_c</groupId>
<artifactId>jose4j</artifactId>
<version>${jose4j.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
================================================
FILE: acme4j-client/src/main/java/.gitignore
================================================
================================================
FILE: acme4j-client/src/main/java/module-info.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2020 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
/**
* This is the main module of the acme4j client.
*/
module org.shredzone.acme4j {
requires static com.github.spotbugs.annotations;
requires java.net.http;
requires org.bouncycastle.pkix;
requires org.bouncycastle.provider;
requires org.jose4j;
requires org.slf4j;
exports org.shredzone.acme4j;
exports org.shredzone.acme4j.challenge;
exports org.shredzone.acme4j.connector;
exports org.shredzone.acme4j.exception;
exports org.shredzone.acme4j.provider;
exports org.shredzone.acme4j.toolbox;
exports org.shredzone.acme4j.util;
uses org.shredzone.acme4j.provider.AcmeProvider;
uses org.shredzone.acme4j.provider.ChallengeProvider;
provides org.shredzone.acme4j.provider.AcmeProvider
with org.shredzone.acme4j.provider.GenericAcmeProvider,
org.shredzone.acme4j.provider.actalis.ActalisAcmeProvider,
org.shredzone.acme4j.provider.google.GoogleAcmeProvider,
org.shredzone.acme4j.provider.letsencrypt.LetsEncryptAcmeProvider,
org.shredzone.acme4j.provider.pebble.PebbleAcmeProvider,
org.shredzone.acme4j.provider.sslcom.SslComAcmeProvider,
org.shredzone.acme4j.provider.zerossl.ZeroSSLAcmeProvider;
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/Account.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2015 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j;
import java.io.Serial;
import java.net.URI;
import java.net.URL;
import java.security.KeyPair;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.shredzone.acme4j.connector.Resource;
import org.shredzone.acme4j.connector.ResourceIterator;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeNotSupportedException;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.exception.AcmeServerException;
import org.shredzone.acme4j.toolbox.AcmeUtils;
import org.shredzone.acme4j.toolbox.JSON.Value;
import org.shredzone.acme4j.toolbox.JSONBuilder;
import org.shredzone.acme4j.toolbox.JoseUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A representation of an account at the ACME server.
*/
public class Account extends AcmeJsonResource {
@Serial
private static final long serialVersionUID = 7042863483428051319L;
private static final Logger LOG = LoggerFactory.getLogger(Account.class);
private static final String KEY_TOS_AGREED = "termsOfServiceAgreed";
private static final String KEY_ORDERS = "orders";
private static final String KEY_CONTACT = "contact";
private static final String KEY_STATUS = "status";
private static final String KEY_EXTERNAL_ACCOUNT_BINDING = "externalAccountBinding";
protected Account(Login login, URL location) {
super(login, location);
}
/**
* Returns if the user agreed to the terms of service.
*
* @return {@code true} if the user agreed to the terms of service. May be
* empty if the server did not provide such an information.
*/
public Optional<Boolean> getTermsOfServiceAgreed() {
return getJSON().get(KEY_TOS_AGREED).map(Value::asBoolean);
}
/**
* List of registered contact addresses (emails, phone numbers etc).
* <p>
* This list is unmodifiable. Use {@link #modify()} to change the contacts. May be
* empty, but is never {@code null}.
*/
public List<URI> getContacts() {
return getJSON().get(KEY_CONTACT)
.asArray()
.stream()
.map(Value::asURI)
.toList();
}
/**
* Returns the current status of the account.
* <p>
* Possible values are: {@link Status#VALID}, {@link Status#DEACTIVATED},
* {@link Status#REVOKED}.
*/
public Status getStatus() {
return getJSON().get(KEY_STATUS).asStatus();
}
/**
* Returns {@code true} if the account is bound to an external non-ACME account.
*
* @since 2.8
*/
public boolean hasExternalAccountBinding() {
return getJSON().contains(KEY_EXTERNAL_ACCOUNT_BINDING);
}
/**
* Returns the key identifier of the external non-ACME account. If this account is
* not bound to an external account, the result is empty.
*
* @since 2.8
*/
public Optional<String> getKeyIdentifier() {
return getJSON().get(KEY_EXTERNAL_ACCOUNT_BINDING)
.optional().map(Value::asObject)
.map(j -> j.get("protected")).map(Value::asEncodedObject)
.map(j -> j.get("kid")).map(Value::asString);
}
/**
* Returns an {@link Iterator} of all {@link Order} belonging to this
* {@link Account}.
* <p>
* Using the iterator will initiate one or more requests to the ACME server.
*
* @return {@link Iterator} instance that returns {@link Order} objects in no specific
* sorting order. {@link Iterator#hasNext()} and {@link Iterator#next()} may throw
* {@link AcmeProtocolException} if a batch of authorization URIs could not be fetched
* from the server.
*/
public Iterator<Order> getOrders() {
var ordersUrl = getJSON().get(KEY_ORDERS).optional().map(Value::asURL);
if (ordersUrl.isEmpty()) {
// Let's Encrypt does not provide this field at the moment, although it's required.
// See https://github.com/letsencrypt/boulder/issues/3335
throw new AcmeNotSupportedException("getOrders()");
}
return new ResourceIterator<>(getLogin(), KEY_ORDERS, ordersUrl.get(), Login::bindOrder);
}
/**
* Creates a builder for a new {@link Order}.
*
* @return {@link OrderBuilder} object
*/
public OrderBuilder newOrder() {
return getLogin().newOrder();
}
/**
* Pre-authorizes a domain. The CA will check if it accepts the domain for
* certification, and returns the necessary challenges.
* <p>
* Some servers may not allow pre-authorization.
* <p>
* It is not possible to pre-authorize wildcard domains.
*
* @param domain
* Domain name to be pre-authorized. IDN names are accepted and will be ACE
* encoded automatically.
* @return {@link Authorization} object for this domain
* @throws AcmeException
* if the server does not allow pre-authorization
* @throws AcmeServerException
* if the server allows pre-authorization, but will refuse to issue a
* certificate for this domain
*/
public Authorization preAuthorizeDomain(String domain) throws AcmeException {
Objects.requireNonNull(domain, "domain");
if (domain.isEmpty()) {
throw new IllegalArgumentException("domain must not be empty");
}
return preAuthorize(Identifier.dns(domain));
}
/**
* Pre-authorizes an {@link Identifier}. The CA will check if it accepts the
* identifier for certification, and returns the necessary challenges.
* <p>
* Some servers may not allow pre-authorization.
* <p>
* It is not possible to pre-authorize wildcard domains.
*
* @param identifier
* {@link Identifier} to be pre-authorized.
* @return {@link Authorization} object for this identifier
* @throws AcmeException
* if the server does not allow pre-authorization
* @throws AcmeServerException
* if the server allows pre-authorization, but will refuse to issue a
* certificate for this identifier
* @since 2.3
*/
public Authorization preAuthorize(Identifier identifier) throws AcmeException {
Objects.requireNonNull(identifier, "identifier");
var newAuthzUrl = getSession().resourceUrl(Resource.NEW_AUTHZ);
if (identifier.toMap().containsKey(Identifier.KEY_SUBDOMAIN_AUTH_ALLOWED)
&& !getSession().getMetadata().isSubdomainAuthAllowed()) {
throw new AcmeNotSupportedException("subdomain-auth");
}
LOG.debug("preAuthorize {}", identifier);
try (var conn = getSession().connect()) {
var claims = new JSONBuilder();
claims.put("identifier", identifier.toMap());
conn.sendSignedRequest(newAuthzUrl, claims, getLogin());
var auth = getLogin().bindAuthorization(conn.getLocation());
auth.setJSON(conn.readJsonResponse());
return auth;
}
}
/**
* Changes the {@link KeyPair} associated with the account.
* <p>
* After a successful call, the new key pair is already set in the associated
* {@link Login}. The old key pair can be discarded.
*
* @param newKeyPair
* new {@link KeyPair} to be used for identifying this account
*/
public void changeKey(KeyPair newKeyPair) throws AcmeException {
Objects.requireNonNull(newKeyPair, "newKeyPair");
if (Arrays.equals(getLogin().getPublicKey().getEncoded(),
newKeyPair.getPublic().getEncoded())) {
throw new IllegalArgumentException("newKeyPair must actually be a new key pair");
}
LOG.debug("key-change");
try (var conn = getSession().connect()) {
var keyChangeUrl = getSession().resourceUrl(Resource.KEY_CHANGE);
var payloadClaim = new JSONBuilder();
payloadClaim.put("account", getLocation());
payloadClaim.putKey("oldKey", getLogin().getPublicKey());
var jose = JoseUtils.createJoseRequest(keyChangeUrl, newKeyPair,
payloadClaim, null, null);
conn.sendSignedRequest(keyChangeUrl, jose, getLogin());
getLogin().setKeyPair(newKeyPair);
}
}
/**
* Permanently deactivates an account. Related certificates may still be valid after
* account deactivation, and need to be revoked separately if neccessary.
* <p>
* A deactivated account cannot be reactivated!
*/
public void deactivate() throws AcmeException {
LOG.debug("deactivate");
try (var conn = getSession().connect()) {
var claims = new JSONBuilder();
claims.put(KEY_STATUS, "deactivated");
conn.sendSignedRequest(getLocation(), claims, getLogin());
setJSON(conn.readJsonResponse());
}
}
/**
* Modifies the account data of the account.
*
* @return {@link EditableAccount} where the account can be modified
*/
public EditableAccount modify() {
return new EditableAccount();
}
/**
* Provides editable properties of an {@link Account}.
*/
public class EditableAccount {
private final List<URI> editContacts = new ArrayList<>();
private EditableAccount() {
editContacts.addAll(Account.this.getContacts());
}
/**
* Returns the list of all contact URIs for modification. Use the {@link List}
* methods to modify the contact list.
* <p>
* The modified list is not validated. If you change entries, you have to make
* sure that they are valid according to the RFC. It is recommended to use
* the {@code addContact()} methods below to add new contacts to the list.
*/
@SuppressFBWarnings("EI_EXPOSE_REP") // behavior is intended
public List<URI> getContacts() {
return editContacts;
}
/**
* Adds a new Contact to the account.
*
* @param contact
* Contact URI
* @return itself
*/
public EditableAccount addContact(URI contact) {
AcmeUtils.validateContact(contact);
editContacts.add(contact);
return this;
}
/**
* Adds a new Contact to the account.
* <p>
* This is a convenience call for {@link #addContact(URI)}.
*
* @param contact
* Contact URI as string
* @return itself
*/
public EditableAccount addContact(String contact) {
addContact(URI.create(contact));
return this;
}
/**
* Adds a new Contact email to the account.
* <p>
* This is a convenience call for {@link #addContact(String)} that doesn't
* require to prepend the email address with the "mailto" scheme.
*
* @param email
* Contact email without "mailto" scheme (e.g. test@gmail.com)
* @return itself
*/
public EditableAccount addEmail(String email) {
addContact("mailto:" + email);
return this;
}
/**
* Commits the changes and updates the account.
*/
public void commit() throws AcmeException {
LOG.debug("modify/commit");
try (var conn = getSession().connect()) {
var claims = new JSONBuilder();
if (!editContacts.isEmpty()) {
claims.put(KEY_CONTACT, editContacts);
}
conn.sendSignedRequest(getLocation(), claims, getLogin());
setJSON(conn.readJsonResponse());
}
}
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/AccountBuilder.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2016 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j;
import static java.util.Objects.requireNonNull;
import static org.jose4j.jws.AlgorithmIdentifiers.*;
import static org.shredzone.acme4j.toolbox.JoseUtils.macKeyAlgorithm;
import java.net.URI;
import java.security.KeyPair;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import edu.umd.cs.findbugs.annotations.Nullable;
import org.shredzone.acme4j.connector.Resource;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.toolbox.AcmeUtils;
import org.shredzone.acme4j.toolbox.JSONBuilder;
import org.shredzone.acme4j.toolbox.JoseUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A builder for registering a new account with the CA.
* <p>
* You need to create a new key pair and set it via {@link #useKeyPair(KeyPair)}. Your
* account will be identified by the public part of that key pair, so make sure to store
* it safely! There is no automatic way to regain access to your account if the key pair
* is lost.
* <p>
* Depending on the CA you register with, you might need to give additional information.
* <ul>
* <li>You might need to agree to the terms of service via
* {@link #agreeToTermsOfService()}.</li>
* <li>You might need to give at least one contact URI.</li>
* <li>You might need to provide a key identifier (e.g. your customer number) and
* a shared secret via {@link #withKeyIdentifier(String, SecretKey)}.</li>
* </ul>
* <p>
* It is not possible to modify an existing account with the {@link AccountBuilder}. To
* modify an existing account, use {@link Account#modify()} and
* {@link Account#changeKey(KeyPair)}.
*/
public class AccountBuilder {
private static final Logger LOG = LoggerFactory.getLogger(AccountBuilder.class);
private static final Set<String> VALID_ALGORITHMS = Set.of(HMAC_SHA256, HMAC_SHA384, HMAC_SHA512);
private final List<URI> contacts = new ArrayList<>();
private @Nullable Boolean termsOfServiceAgreed;
private @Nullable Boolean onlyExisting;
private @Nullable String keyIdentifier;
private @Nullable KeyPair keyPair;
private @Nullable SecretKey macKey;
private @Nullable String macAlgorithm;
/**
* Add a contact URI to the list of contacts.
* <p>
* A contact URI may be e.g. an email address or a phone number. It depends on the CA
* what kind of contact URIs are accepted, and how many must be provided as minimum.
*
* @param contact
* Contact URI
* @return itself
*/
public AccountBuilder addContact(URI contact) {
AcmeUtils.validateContact(contact);
contacts.add(contact);
return this;
}
/**
* Add a contact address to the list of contacts.
* <p>
* This is a convenience call for {@link #addContact(URI)}.
*
* @param contact
* Contact URI as string
* @return itself
* @throws IllegalArgumentException
* if there is a syntax error in the URI string
*/
public AccountBuilder addContact(String contact) {
addContact(URI.create(contact));
return this;
}
/**
* Add an email address to the list of contacts.
* <p>
* This is a convenience call for {@link #addContact(String)} that doesn't require
* to prepend the "mailto" scheme to an email address.
*
* @param email
* Contact email without "mailto" scheme (e.g. test@gmail.com)
* @return itself
* @throws IllegalArgumentException
* if there is a syntax error in the URI string
*/
public AccountBuilder addEmail(String email) {
if (email.startsWith("mailto:")) {
addContact(email);
} else {
addContact("mailto:" + email);
}
return this;
}
/**
* Documents that the user has agreed to the terms of service.
* <p>
* If the CA requires the user to agree to the terms of service, it is your
* responsibility to present them to the user, and actively ask for their agreement. A
* link to the terms of service is provided via
* {@code session.getMetadata().getTermsOfService()}.
*
* @return itself
*/
public AccountBuilder agreeToTermsOfService() {
this.termsOfServiceAgreed = true;
return this;
}
/**
* Signals that only an existing account should be returned. The server will not
* create a new account if the key is not known.
* <p>
* If you have lost your account's location URL, but still have your account's key
* pair, you can register your account again with the same key, and use
* {@link #onlyExisting()} to make sure that your existing account is returned. If
* your key is unknown to the server, an error is thrown once the account is to be
* created.
*
* @return itself
*/
public AccountBuilder onlyExisting() {
this.onlyExisting = true;
return this;
}
/**
* Sets the {@link KeyPair} to be used for this account.
* <p>
* Only the public key of the pair is sent to the server for registration. acme4j will
* never send the private key part.
* <p>
* Make sure to store your key pair safely after registration! There is no automatic
* way to regain access to your account if the key pair is lost.
*
* @param keyPair
* Account's {@link KeyPair}
* @return itself
*/
public AccountBuilder useKeyPair(KeyPair keyPair) {
this.keyPair = requireNonNull(keyPair, "keyPair");
return this;
}
/**
* Sets a Key Identifier and MAC key provided by the CA. Use this if your CA requires
* an individual account identification (e.g. your customer number) and a shared
* secret for registration. See the documentation of your CA about how to retrieve the
* key identifier and MAC key.
*
* @param kid
* Key Identifier
* @param macKey
* MAC key
* @return itself
* @see #withKeyIdentifier(String, String)
*/
public AccountBuilder withKeyIdentifier(String kid, SecretKey macKey) {
if (kid != null && kid.isEmpty()) {
throw new IllegalArgumentException("kid must not be empty");
}
this.macKey = requireNonNull(macKey, "macKey");
this.keyIdentifier = kid;
return this;
}
/**
* Sets a Key Identifier and MAC key provided by the CA. Use this if your CA requires
* an individual account identification (e.g. your customer number) and a shared
* secret for registration. See the documentation of your CA about how to retrieve the
* key identifier and MAC key.
* <p>
* This is a convenience call of {@link #withKeyIdentifier(String, SecretKey)} that
* accepts a base64url encoded MAC key, so both parameters can be passed in as
* strings.
*
* @param kid
* Key Identifier
* @param encodedMacKey
* Base64url encoded MAC key.
* @return itself
* @see #withKeyIdentifier(String, SecretKey)
*/
public AccountBuilder withKeyIdentifier(String kid, String encodedMacKey) {
var encodedKey = AcmeUtils.base64UrlDecode(requireNonNull(encodedMacKey, "encodedMacKey"));
return withKeyIdentifier(kid, new SecretKeySpec(encodedKey, "HMAC"));
}
/**
* Sets the MAC key algorithm that is provided by the CA. To be used in combination
* with key identifier. By default, the algorithm is deduced from the size of the
* MAC key. If a different size is needed, it can be set using this method.
*
* @param macAlgorithm
* the algorithm to be set in the {@code alg} field, e.g. {@code "HS512"}.
* @return itself
* @since 3.1.0
*/
public AccountBuilder withMacAlgorithm(String macAlgorithm) {
var algorithm = requireNonNull(macAlgorithm, "macAlgorithm");
if (!VALID_ALGORITHMS.contains(algorithm)) {
throw new IllegalArgumentException("Invalid MAC algorithm: " + macAlgorithm);
}
this.macAlgorithm = algorithm;
return this;
}
/**
* Creates a new account.
* <p>
* Use this method to finally create your account with the given parameters. Do not
* use the {@link AccountBuilder} after invoking this method.
*
* @param session
* {@link Session} to be used for registration
* @return {@link Account} referring to the new account
* @see #createLogin(Session)
*/
public Account create(Session session) throws AcmeException {
return createLogin(session).getAccount();
}
/**
* Creates a new account.
* <p>
* This method is identical to {@link #create(Session)}, but returns a {@link Login}
* that is ready to be used.
*
* @param session
* {@link Session} to be used for registration
* @return {@link Login} referring to the new account
*/
public Login createLogin(Session session) throws AcmeException {
requireNonNull(session, "session");
if (keyPair == null) {
throw new IllegalStateException("Use AccountBuilder.useKeyPair() to set the account's key pair.");
}
LOG.debug("create");
try (var conn = session.connect()) {
var resourceUrl = session.resourceUrl(Resource.NEW_ACCOUNT);
var claims = new JSONBuilder();
if (!contacts.isEmpty()) {
claims.put("contact", contacts);
}
if (termsOfServiceAgreed != null) {
claims.put("termsOfServiceAgreed", termsOfServiceAgreed);
}
if (keyIdentifier != null && macKey != null) {
var algorithm = Optional.ofNullable(macAlgorithm)
.or(session.provider()::getProposedEabMacAlgorithm)
.orElseGet(() -> macKeyAlgorithm(macKey));
claims.put("externalAccountBinding", JoseUtils.createExternalAccountBinding(
keyIdentifier, keyPair.getPublic(), macKey, algorithm, resourceUrl));
}
if (onlyExisting != null) {
claims.put("onlyReturnExisting", onlyExisting);
}
conn.sendSignedRequest(resourceUrl, claims, session, (url, payload, nonce) ->
JoseUtils.createJoseRequest(url, keyPair, payload, nonce, null));
var login = new Login(conn.getLocation(), keyPair, session);
login.getAccount().setJSON(conn.readJsonResponse());
return login;
}
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/AcmeJsonResource.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2018 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j;
import java.io.Serial;
import java.net.URL;
import java.time.Instant;
import java.util.Objects;
import java.util.Optional;
import edu.umd.cs.findbugs.annotations.Nullable;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeLazyLoadingException;
import org.shredzone.acme4j.toolbox.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An extension of {@link AcmeResource} that also contains the current state of a resource
* as JSON document. If the current state is not present, this class takes care of
* fetching it from the server if necessary.
*/
public abstract class AcmeJsonResource extends AcmeResource {
@Serial
private static final long serialVersionUID = -5060364275766082345L;
private static final Logger LOG = LoggerFactory.getLogger(AcmeJsonResource.class);
private @Nullable JSON data = null;
private @Nullable Instant retryAfter = null;
/**
* Create a new {@link AcmeJsonResource}.
*
* @param login
* {@link Login} the resource is bound with
* @param location
* Location {@link URL} of this resource
*/
protected AcmeJsonResource(Login login, URL location) {
super(login, location);
}
/**
* Returns the JSON representation of the resource data.
* <p>
* If there is no data, {@link #fetch()} is invoked to fetch it from the server.
* <p>
* This method can be used to read proprietary data from the resources.
*
* @return Resource data, as {@link JSON}.
* @throws AcmeLazyLoadingException
* if an {@link AcmeException} occured while fetching the current state from
* the server.
*/
public JSON getJSON() {
if (data == null) {
try {
fetch();
} catch (AcmeException ex) {
throw new AcmeLazyLoadingException(this, ex);
}
}
return Objects.requireNonNull(data);
}
/**
* Sets the JSON representation of the resource data.
*
* @param data
* New {@link JSON} data, must not be {@code null}.
*/
protected void setJSON(JSON data) {
invalidate();
this.data = Objects.requireNonNull(data, "data");
}
/**
* Checks if this resource is valid.
*
* @return {@code true} if the resource state has been loaded from the server. If
* {@code false}, {@link #getJSON()} would implicitly call {@link #fetch()}
* to fetch the current state from the server.
*/
protected boolean isValid() {
return data != null;
}
/**
* Invalidates the state of this resource. Enforces a {@link #fetch()} when
* {@link #getJSON()} is invoked.
* <p>
* Subclasses can override this method to purge internal caches that are based on the
* JSON structure. Remember to invoke {@code super.invalidate()}!
*/
protected void invalidate() {
data = null;
retryAfter = null;
}
/**
* Updates this resource, by fetching the current resource data from the server.
*
* @return An {@link Optional} estimation when the resource status will change. If you
* are polling for the resource to complete, you should wait for the given instant
* before trying again. Empty if the server did not return a "Retry-After" header.
* @throws AcmeException
* if the resource could not be fetched.
* @since 3.2.0
*/
public Optional<Instant> fetch() throws AcmeException {
var resourceType = getClass().getSimpleName();
LOG.debug("update {}", resourceType);
try (var conn = getSession().connect()) {
conn.sendSignedPostAsGetRequest(getLocation(), getLogin());
setJSON(conn.readJsonResponse());
var retryAfterOpt = conn.getRetryAfter();
retryAfterOpt.ifPresent(instant -> LOG.debug("Retry-After: {}", instant));
setRetryAfter(retryAfterOpt.orElse(null));
return retryAfterOpt;
}
}
/**
* Sets a Retry-After instant.
*
* @since 3.2.0
*/
protected void setRetryAfter(@Nullable Instant retryAfter) {
this.retryAfter = retryAfter;
}
/**
* Gets an estimation when the resource status will change. If you are polling for
* the resource to complete, you should wait for the given instant before trying
* a status refresh.
* <p>
* This instant was sent with the Retry-After header at the last update.
*
* @return Retry-after {@link Instant}, or empty if there was no such header.
* @since 3.2.0
*/
public Optional<Instant> getRetryAfter() {
return Optional.ofNullable(retryAfter);
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/AcmeResource.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2016 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j;
import java.io.Serial;
import java.io.Serializable;
import java.net.URL;
import java.util.Objects;
import edu.umd.cs.findbugs.annotations.Nullable;
/**
* This is the root class of all ACME resources (like accounts, orders, certificates).
* Every resource is identified by its location URL.
* <p>
* This class also takes care for proper serialization and de-serialization of the
* resource. After de-serialization, the resource must be bound to a {@link Login} again,
* using {@link #rebind(Login)}.
*/
public abstract class AcmeResource implements Serializable {
@Serial
private static final long serialVersionUID = -7930580802257379731L;
private transient @Nullable Login login;
private final URL location;
/**
* Create a new {@link AcmeResource}.
*
* @param login
* {@link Login} the resource is bound with
* @param location
* Location {@link URL} of this resource
*/
protected AcmeResource(Login login, URL location) {
this.location = Objects.requireNonNull(location, "location");
rebind(login);
}
/**
* Gets the {@link Login} this resource is bound with.
*/
protected Login getLogin() {
if (login == null) {
throw new IllegalStateException("Use rebind() for binding this object to a login.");
}
return login;
}
/**
* Gets the {@link Session} this resource is bound with.
*/
protected Session getSession() {
return getLogin().getSession();
}
/**
* Rebinds this resource to a {@link Login}.
* <p>
* Logins are not serialized, because they contain volatile session data and also a
* private key. After de-serialization of an {@link AcmeResource}, use this method to
* rebind it to a {@link Login}.
*
* @param login
* {@link Login} to bind this resource to
*/
public void rebind(Login login) {
if (this.login != null) {
throw new IllegalStateException("Resource is already bound to a login");
}
this.login = Objects.requireNonNull(login, "login");
}
/**
* Gets the resource's location.
*/
public URL getLocation() {
return location;
}
@Override
protected final void finalize() {
// CT_CONSTRUCTOR_THROW: Prevents finalizer attack
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/Authorization.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2015 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j;
import static java.util.stream.Collectors.toUnmodifiableList;
import java.io.Serial;
import java.net.URL;
import java.time.Duration;
import java.time.Instant;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.AcmeUtils;
import org.shredzone.acme4j.toolbox.JSON.Value;
import org.shredzone.acme4j.toolbox.JSONBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Represents an authorization request at the ACME server.
*/
public class Authorization extends AcmeJsonResource implements PollableResource {
@Serial
private static final long serialVersionUID = -3116928998379417741L;
private static final Logger LOG = LoggerFactory.getLogger(Authorization.class);
protected Authorization(Login login, URL location) {
super(login, location);
}
/**
* Gets the {@link Identifier} to be authorized.
* <p>
* For wildcard domain orders, the domain itself (without wildcard prefix) is returned
* here. To find out if this {@link Authorization} is related to a wildcard domain
* order, check the {@link #isWildcard()} method.
*
* @since 2.3
*/
public Identifier getIdentifier() {
return getJSON().get("identifier").asIdentifier();
}
/**
* Gets the authorization status.
* <p>
* Possible values are: {@link Status#PENDING}, {@link Status#VALID},
* {@link Status#INVALID}, {@link Status#DEACTIVATED}, {@link Status#EXPIRED},
* {@link Status#REVOKED}.
*/
@Override
public Status getStatus() {
return getJSON().get("status").asStatus();
}
/**
* Gets the expiry date of the authorization, if set by the server.
*/
public Optional<Instant> getExpires() {
return getJSON().get("expires")
.map(Value::asString)
.map(AcmeUtils::parseTimestamp);
}
/**
* Returns {@code true} if this {@link Authorization} is related to a wildcard domain,
* {@code false} otherwise.
*/
public boolean isWildcard() {
return getJSON().get("wildcard")
.map(Value::asBoolean)
.orElse(false);
}
/**
* Returns {@code true} if certificates for subdomains can be issued according to
* RFC9444.
*
* @since 3.3.0
*/
public boolean isSubdomainAuthAllowed() {
return getJSON().get("subdomainAuthAllowed")
.map(Value::asBoolean)
.orElse(false);
}
/**
* Gets a list of all challenges offered by the server, in no specific order.
*/
public List<Challenge> getChallenges() {
var login = getLogin();
return getJSON().get("challenges")
.asArray()
.stream()
.map(Value::asObject)
.map(login::createChallenge)
.collect(toUnmodifiableList());
}
/**
* Finds a {@link Challenge} of the given type. Responding to this {@link Challenge}
* is sufficient for authorization.
* <p>
* {@link Authorization#findChallenge(Class)} should be preferred, as this variant
* is not type safe.
*
* @param type
* Challenge name (e.g. "http-01")
* @return {@link Challenge} matching that name, or empty if there is no such
* challenge, or if the challenge alone is not sufficient for authorization.
* @throws ClassCastException
* if the type does not match the expected Challenge class type
*/
@SuppressWarnings("unchecked")
public <T extends Challenge> Optional<T> findChallenge(final String type) {
return (Optional<T>) getChallenges().stream()
.filter(ch -> type.equals(ch.getType()))
.reduce((a, b) -> {
throw new AcmeProtocolException("Found more than one challenge of type " + type);
});
}
/**
* Finds a {@link Challenge} of the given class type. Responding to this {@link
* Challenge} is sufficient for authorization.
*
* @param type
* Challenge type (e.g. "Http01Challenge.class")
* @return {@link Challenge} of that type, or empty if there is no such
* challenge, or if the challenge alone is not sufficient for authorization.
* @since 2.8
*/
public <T extends Challenge> Optional<T> findChallenge(Class<T> type) {
return getChallenges().stream()
.filter(type::isInstance)
.map(type::cast)
.reduce((a, b) -> {
throw new AcmeProtocolException("Found more than one challenge of type " + type.getName());
});
}
/**
* Waits until the authorization is completed.
* <p>
* It is completed if it reaches either {@link Status#VALID} or
* {@link Status#INVALID}.
* <p>
* This method is synchronous and blocks the current thread.
*
* @param timeout
* Timeout until a terminal status must have been reached
* @return Status that was reached
* @since 3.4.0
*/
public Status waitForCompletion(Duration timeout)
throws AcmeException, InterruptedException {
return waitForStatus(EnumSet.of(Status.VALID, Status.INVALID), timeout);
}
/**
* Permanently deactivates the {@link Authorization}.
*/
public void deactivate() throws AcmeException {
LOG.debug("deactivate");
try (var conn = getSession().connect()) {
var claims = new JSONBuilder();
claims.put("status", "deactivated");
conn.sendSignedRequest(getLocation(), claims, getLogin());
setJSON(conn.readJsonResponse());
}
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/Certificate.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2016 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j;
import static java.util.Collections.unmodifiableList;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toUnmodifiableList;
import static org.shredzone.acme4j.toolbox.AcmeUtils.getRenewalUniqueIdentifier;
import java.io.IOException;
import java.io.Serial;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.security.KeyPair;
import java.security.Principal;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import edu.umd.cs.findbugs.annotations.Nullable;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.shredzone.acme4j.connector.Resource;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeLazyLoadingException;
import org.shredzone.acme4j.exception.AcmeNotSupportedException;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.AcmeUtils;
import org.shredzone.acme4j.toolbox.JSONBuilder;
import org.shredzone.acme4j.toolbox.JoseUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Represents an issued certificate and its certificate chain.
* <p>
* A certificate is immutable once it is issued. For renewal, a new certificate must be
* ordered.
*/
public class Certificate extends AcmeResource {
@Serial
private static final long serialVersionUID = 7381527770159084201L;
private static final Logger LOG = LoggerFactory.getLogger(Certificate.class);
private @Nullable List<X509Certificate> certChain;
private @Nullable Collection<URL> alternates;
private transient @Nullable RenewalInfo renewalInfo = null;
private transient @Nullable List<Certificate> alternateCerts = null;
protected Certificate(Login login, URL certUrl) {
super(login, certUrl);
}
/**
* Downloads the certificate chain.
* <p>
* The certificate is downloaded lazily by the other methods. Usually there is no need
* to invoke this method, unless the download is to be enforced. If the certificate
* has been downloaded already, nothing will happen.
*
* @throws AcmeException
* if the certificate could not be downloaded
*/
public void download() throws AcmeException {
if (certChain == null) {
LOG.debug("download");
try (var conn = getSession().connect()) {
conn.sendCertificateRequest(getLocation(), getLogin());
alternates = conn.getLinks("alternate");
certChain = conn.readCertificates();
}
}
}
/**
* Returns the created certificate.
*
* @return The created end-entity {@link X509Certificate} without issuer chain.
*/
public X509Certificate getCertificate() {
lazyDownload();
return requireNonNull(certChain).get(0);
}
/**
* Returns the created certificate and issuer chain.
*
* @return The created end-entity {@link X509Certificate} and issuer chain. The first
* certificate is always the end-entity certificate, followed by the
* intermediate certificates required to build a path to a trusted root.
*/
public List<X509Certificate> getCertificateChain() {
lazyDownload();
return unmodifiableList(requireNonNull(certChain));
}
/**
* Returns URLs to alternate certificate chains.
*
* @return Alternate certificate chains, or empty if there are none.
*/
public List<URL> getAlternates() {
lazyDownload();
return requireNonNull(alternates).stream().collect(toUnmodifiableList());
}
/**
* Returns alternate certificate chains, if available.
*
* @return Alternate certificate chains, or empty if there are none.
* @since 2.11
*/
public List<Certificate> getAlternateCertificates() {
if (alternateCerts == null) {
var login = getLogin();
alternateCerts = getAlternates().stream()
.map(login::bindCertificate)
.collect(toList());
}
return unmodifiableList(alternateCerts);
}
/**
* Checks if this certificate was issued by the given issuer name.
*
* @param issuer
* Issuer name to check against, case-sensitive
* @return {@code true} if this issuer name was found in the certificate chain as
* issuer, {@code false} otherwise.
* @since 3.0.0
*/
public boolean isIssuedBy(String issuer) {
var issuerCn = "CN=" + issuer;
return getCertificateChain().stream()
.map(X509Certificate::getIssuerX500Principal)
.map(Principal::getName)
.anyMatch(issuerCn::equals);
}
/**
* Finds a {@link Certificate} that was issued by the given issuer name.
*
* @param issuer
* Issuer name to check against, case-sensitive
* @return Certificate that was issued by that issuer, or {@code empty} if there was
* none. The returned {@link Certificate} may be this instance, or one of the
* {@link #getAlternateCertificates()} instances. If multiple certificates are issued
* by that issuer, the first one that was found is returned.
* @since 3.0.0
*/
public Optional<Certificate> findCertificate(String issuer) {
if (isIssuedBy(issuer)) {
return Optional.of(this);
}
return getAlternateCertificates().stream()
.filter(c -> c.isIssuedBy(issuer))
.findFirst();
}
/**
* Writes the certificate to the given writer. It is written in PEM format, with the
* end-entity cert coming first, followed by the intermediate certificates.
*
* @param out
* {@link Writer} to write to. The writer is not closed after use.
*/
public void writeCertificate(Writer out) throws IOException {
try {
for (var cert : getCertificateChain()) {
AcmeUtils.writeToPem(cert.getEncoded(), AcmeUtils.PemLabel.CERTIFICATE, out);
}
} catch (CertificateEncodingException ex) {
throw new IOException("Encoding error", ex);
}
}
/**
* Returns the location of the certificate's RenewalInfo. Empty if the CA does not
* provide this information.
*
* @since 3.0.0
*/
public Optional<URL> getRenewalInfoLocation() {
try {
return getSession().resourceUrlOptional(Resource.RENEWAL_INFO)
.map(baseUrl -> {
try {
var url = baseUrl.toExternalForm();
if (!url.endsWith("/")) {
url += '/';
}
url += getRenewalUniqueIdentifier(getCertificate());
return URI.create(url).toURL();
} catch (MalformedURLException ex) {
throw new AcmeProtocolException("Invalid RenewalInfo URL", ex);
}
});
} catch (AcmeException ex) {
throw new AcmeLazyLoadingException(this, ex);
}
}
/**
* Returns {@code true} if the CA provides renewal information.
*
* @since 3.0.0
*/
public boolean hasRenewalInfo() {
return getRenewalInfoLocation().isPresent();
}
/**
* Reads the RenewalInfo for this certificate.
*
* @return The {@link RenewalInfo} of this certificate.
* @throws AcmeNotSupportedException if the CA does not support renewal information.
* @since 3.0.0
*/
@SuppressFBWarnings("EI_EXPOSE_REP") // behavior is intended
public RenewalInfo getRenewalInfo() {
if (renewalInfo == null) {
renewalInfo = getRenewalInfoLocation()
.map(getLogin()::bindRenewalInfo)
.orElseThrow(() -> new AcmeNotSupportedException("renewal-info"));
}
return renewalInfo;
}
/**
* Revokes this certificate.
*/
public void revoke() throws AcmeException {
revoke(null);
}
/**
* Revokes this certificate.
*
* @param reason
* {@link RevocationReason} stating the reason of the revocation that is
* used when generating OCSP responses and CRLs. {@code null} to give no
* reason.
* @see #revoke(Login, X509Certificate, RevocationReason)
* @see #revoke(Session, KeyPair, X509Certificate, RevocationReason)
*/
public void revoke(@Nullable RevocationReason reason) throws AcmeException {
revoke(getLogin(), getCertificate(), reason);
}
/**
* Revoke a certificate.
* <p>
* Use this method if the certificate's location is unknown, so you cannot regenerate
* a {@link Certificate} instance. This method requires a {@link Login} to your
* account and the issued certificate.
*
* @param login
* {@link Login} to the account
* @param cert
* The {@link X509Certificate} to be revoked
* @param reason
* {@link RevocationReason} stating the reason of the revocation that is used
* when generating OCSP responses and CRLs. {@code null} to give no reason.
* @see #revoke(Session, KeyPair, X509Certificate, RevocationReason)
* @since 2.6
*/
public static void revoke(Login login, X509Certificate cert, @Nullable RevocationReason reason)
throws AcmeException {
LOG.debug("revoke");
var session = login.getSession();
var resUrl = session.resourceUrl(Resource.REVOKE_CERT);
try (var conn = session.connect()) {
var claims = new JSONBuilder();
claims.putBase64("certificate", cert.getEncoded());
if (reason != null) {
claims.put("reason", reason.getReasonCode());
}
conn.sendSignedRequest(resUrl, claims, login);
} catch (CertificateEncodingException ex) {
throw new AcmeProtocolException("Invalid certificate", ex);
}
}
/**
* Revoke a certificate.
* <p>
* Use this method if the key pair of your account was lost (so you are unable to
* login into your account), but you still have the key pair of the affected domain
* and the issued certificate.
*
* @param session
* {@link Session} connected to the ACME server
* @param domainKeyPair
* Key pair the CSR was signed with
* @param cert
* The {@link X509Certificate} to be revoked
* @param reason
* {@link RevocationReason} stating the reason of the revocation that is used
* when generating OCSP responses and CRLs. {@code null} to give no reason.
* @see #revoke(Login, X509Certificate, RevocationReason)
*/
public static void revoke(Session session, KeyPair domainKeyPair, X509Certificate cert,
@Nullable RevocationReason reason) throws AcmeException {
LOG.debug("revoke using the domain key pair");
var resUrl = session.resourceUrl(Resource.REVOKE_CERT);
try (var conn = session.connect()) {
var claims = new JSONBuilder();
claims.putBase64("certificate", cert.getEncoded());
if (reason != null) {
claims.put("reason", reason.getReasonCode());
}
conn.sendSignedRequest(resUrl, claims, session, (url, payload, nonce) ->
JoseUtils.createJoseRequest(url, domainKeyPair, payload, nonce, null));
} catch (CertificateEncodingException ex) {
throw new AcmeProtocolException("Invalid certificate", ex);
}
}
/**
* Lazily downloads the certificate. Throws a runtime {@link AcmeLazyLoadingException}
* if the download failed.
*/
private void lazyDownload() {
try {
download();
} catch (AcmeException ex) {
throw new AcmeLazyLoadingException(this, ex);
}
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/Identifier.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2018 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j;
import static java.util.Collections.unmodifiableMap;
import static java.util.Objects.requireNonNull;
import static org.shredzone.acme4j.toolbox.AcmeUtils.toAce;
import java.io.Serial;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.TreeMap;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.JSON;
/**
* Represents an identifier.
* <p>
* The ACME protocol only defines the DNS identifier, which identifies a domain name.
* acme4j also supports IP identifiers.
* <p>
* CAs, and other acme4j modules, may define further, proprietary identifier types.
*
* @since 2.3
*/
public class Identifier implements Serializable {
@Serial
private static final long serialVersionUID = -7777851842076362412L;
/**
* Type constant for DNS identifiers.
*/
public static final String TYPE_DNS = "dns";
/**
* Type constant for IP identifiers.
*
* @see <a href="https://tools.ietf.org/html/rfc8738">RFC 8738</a>
*/
public static final String TYPE_IP = "ip";
static final String KEY_TYPE = "type";
static final String KEY_VALUE = "value";
static final String KEY_ANCESTOR_DOMAIN = "ancestorDomain";
static final String KEY_SUBDOMAIN_AUTH_ALLOWED = "subdomainAuthAllowed";
private final Map<String, Object> content = new TreeMap<>();
/**
* Creates a new {@link Identifier}.
* <p>
* This is a generic constructor for identifiers. Refer to the documentation of your
* CA to find out about the accepted identifier types and values.
* <p>
* Note that for DNS identifiers, no ASCII encoding of unicode domain takes place
* here. Use {@link #dns(String)} instead.
*
* @param type
* Identifier type
* @param value
* Identifier value
*/
public Identifier(String type, String value) {
content.put(KEY_TYPE, requireNonNull(type, KEY_TYPE));
content.put(KEY_VALUE, requireNonNull(value, KEY_VALUE));
}
/**
* Creates a new {@link Identifier} from the given {@link JSON} structure.
*
* @param json
* {@link JSON} containing the identifier data
*/
public Identifier(JSON json) {
if (!json.contains(KEY_TYPE)) {
throw new AcmeProtocolException("Required key " + KEY_TYPE + " is missing");
}
if (!json.contains(KEY_VALUE)) {
throw new AcmeProtocolException("Required key " + KEY_VALUE + " is missing");
}
content.putAll(json.toMap());
}
/**
* Makes a copy of the given Identifier.
*/
private Identifier(Identifier identifier) {
content.putAll(identifier.content);
}
/**
* Creates a new DNS identifier for the given domain name.
*
* @param domain
* Domain name. Unicode domains are automatically ASCII encoded.
* @return New {@link Identifier}
*/
public static Identifier dns(String domain) {
return new Identifier(TYPE_DNS, toAce(domain));
}
/**
* Creates a new IP identifier for the given {@link InetAddress}.
*
* @param ip
* {@link InetAddress}
* @return New {@link Identifier}
*/
public static Identifier ip(InetAddress ip) {
return new Identifier(TYPE_IP, ip.getHostAddress());
}
/**
* Creates a new IP identifier for the given {@link InetAddress}.
*
* @param ip
* IP address as {@link String}
* @return New {@link Identifier}
* @since 2.7
*/
public static Identifier ip(String ip) {
try {
return ip(InetAddress.getByName(ip));
} catch (UnknownHostException ex) {
throw new IllegalArgumentException("Bad IP: " + ip, ex);
}
}
/**
* Sets an ancestor domain, as required in RFC-9444.
*
* @param domain
* The ancestor domain to be set. Unicode domains are automatically ASCII
* encoded.
* @return An {@link Identifier} that contains the ancestor domain.
* @since 3.3.0
*/
public Identifier withAncestorDomain(String domain) {
expectType(TYPE_DNS);
var result = new Identifier(this);
result.content.put(KEY_ANCESTOR_DOMAIN, toAce(domain));
return result;
}
/**
* Gives the permission to authorize subdomains of this domain, as required in
* RFC-9444.
*
* @return An {@link Identifier} that allows subdomain auths.
* @since 3.3.0
*/
public Identifier allowSubdomainAuth() {
expectType(TYPE_DNS);
var result = new Identifier(this);
result.content.put(KEY_SUBDOMAIN_AUTH_ALLOWED, true);
return result;
}
/**
* Returns the identifier type.
*/
public String getType() {
return content.get(KEY_TYPE).toString();
}
/**
* Returns the identifier value.
*/
public String getValue() {
return content.get(KEY_VALUE).toString();
}
/**
* Returns the domain name if this is a DNS identifier.
*
* @return Domain name. Unicode domains are ASCII encoded.
* @throws AcmeProtocolException
* if this is not a DNS identifier.
*/
public String getDomain() {
expectType(TYPE_DNS);
return getValue();
}
/**
* Returns the IP address if this is an IP identifier.
*
* @return {@link InetAddress}
* @throws AcmeProtocolException
* if this is not a DNS identifier.
*/
public InetAddress getIP() {
expectType(TYPE_IP);
try {
return InetAddress.getByName(getValue());
} catch (UnknownHostException ex) {
throw new AcmeProtocolException("bad ip identifier value", ex);
}
}
/**
* Returns the identifier as JSON map.
*/
public Map<String, Object> toMap() {
return unmodifiableMap(content);
}
/**
* Makes sure this identifier is of the given type.
*
* @param type
* Expected type
* @throws AcmeProtocolException
* if this identifier is of a different type
*/
private void expectType(String type) {
if (!type.equals(getType())) {
throw new AcmeProtocolException("expected '" + type + "' identifier, but found '" + getType() + "'");
}
}
@Override
public String toString() {
if (content.size() == 2) {
return getType() + '=' + getValue();
}
return content.toString();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Identifier i)) {
return false;
}
return content.equals(i.content);
}
@Override
public int hashCode() {
return content.hashCode();
}
@Override
protected final void finalize() {
// CT_CONSTRUCTOR_THROW: Prevents finalizer attack
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/Login.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2018 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j;
import static java.util.Objects.requireNonNull;
import static org.shredzone.acme4j.toolbox.AcmeUtils.getRenewalUniqueIdentifier;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.security.KeyPair;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import edu.umd.cs.findbugs.annotations.Nullable;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.connector.Resource;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeLazyLoadingException;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.toolbox.JSONBuilder;
import org.shredzone.acme4j.toolbox.JoseUtils;
/**
* A {@link Login} into an account.
* <p>
* A login is bound to a {@link Session}. However, a {@link Session} can handle multiple
* logins in parallel.
* <p>
* To create a login, you need to specify the location URI of the {@link Account}, and
* need to provide the {@link KeyPair} the account was created with. If the account's
* location URL is unknown, the account can be re-registered with the
* {@link AccountBuilder}, using {@link AccountBuilder#onlyExisting()} to make sure that
* no new account will be created. If the key pair was lost though, there is no automatic
* way to regain access to your account, and you have to contact your CA's support hotline
* for assistance.
* <p>
* Note that {@link Login} objects are intentionally not serializable, as they contain a
* keypair and volatile data. On distributed systems, you can create a {@link Login} to
* the same account for every service instance.
*/
public class Login {
private final Session session;
private final Account account;
private KeyPair keyPair;
/**
* Creates a new {@link Login}.
*
* @param accountLocation
* Account location {@link URL}
* @param keyPair
* {@link KeyPair} of the account
* @param session
* {@link Session} to be used
*/
public Login(URL accountLocation, KeyPair keyPair, Session session) {
this.keyPair = requireNonNull(keyPair, "keyPair");
this.session = requireNonNull(session, "session");
this.account = new Account(this, requireNonNull(accountLocation, "accountLocation"));
}
/**
* Gets the {@link Session} that is used.
*/
@SuppressFBWarnings("EI_EXPOSE_REP") // behavior is intended
public Session getSession() {
return session;
}
/**
* Gets the {@link PublicKey} of the ACME account.
*
* @since 5.0.0
*/
public PublicKey getPublicKey() {
return keyPair.getPublic();
}
/**
* Gets the {@link Account} that is bound to this login.
*
* @return {@link Account} bound to the login
*/
@SuppressFBWarnings("EI_EXPOSE_REP") // behavior is intended
public Account getAccount() {
return account;
}
/**
* Creates a new instance of an existing {@link Authorization} and binds it to this
* login.
*
* @param location
* Location of the Authorization
* @return {@link Authorization} bound to the login
*/
public Authorization bindAuthorization(URL location) {
return new Authorization(this, requireNonNull(location, "location"));
}
/**
* Creates a new instance of an existing {@link Certificate} and binds it to this
* login.
*
* @param location
* Location of the Certificate
* @return {@link Certificate} bound to the login
*/
public Certificate bindCertificate(URL location) {
return new Certificate(this, requireNonNull(location, "location"));
}
/**
* Creates a new instance of an existing {@link Order} and binds it to this login.
*
* @param location
* Location URL of the order
* @return {@link Order} bound to the login
*/
public Order bindOrder(URL location) {
return new Order(this, requireNonNull(location, "location"));
}
/**
* Creates a new instance of an existing {@link RenewalInfo} and binds it to this
* login.
*
* @param location
* Location URL of the renewal info
* @return {@link RenewalInfo} bound to the login
* @since 3.0.0
*/
public RenewalInfo bindRenewalInfo(URL location) {
return new RenewalInfo(this, requireNonNull(location, "location"));
}
/**
* Creates a new instance of an existing {@link RenewalInfo} and binds it to this
* login.
*
* @param certificate
* {@link X509Certificate} to get the {@link RenewalInfo} for
* @return {@link RenewalInfo} bound to the login
* @since 3.2.0
*/
public RenewalInfo bindRenewalInfo(X509Certificate certificate) throws AcmeException {
try {
var url = getSession().resourceUrl(Resource.RENEWAL_INFO).toExternalForm();
if (!url.endsWith("/")) {
url += '/';
}
url += getRenewalUniqueIdentifier(certificate);
return bindRenewalInfo(URI.create(url).toURL());
} catch (MalformedURLException ex) {
throw new AcmeProtocolException("Invalid RenewalInfo URL", ex);
}
}
/**
* Creates a new instance of an existing {@link Challenge} and binds it to this
* login. Use this method only if the resulting challenge type is unknown.
*
* @param location
* Location URL of the challenge
* @return {@link Challenge} bound to the login
* @since 2.8
* @see #bindChallenge(URL, Class)
*/
public Challenge bindChallenge(URL location) {
try (var connect = session.connect()) {
connect.sendSignedPostAsGetRequest(location, this);
return createChallenge(connect.readJsonResponse());
} catch (AcmeException ex) {
throw new AcmeLazyLoadingException(Challenge.class, location, ex);
}
}
/**
* Creates a new instance of an existing {@link Challenge} and binds it to this
* login. Use this method if the resulting challenge type is known.
*
* @param location
* Location URL of the challenge
* @param type
* Expected challenge type
* @return Challenge bound to the login
* @throws AcmeProtocolException
* if the challenge found at the location does not match the expected
* challenge type.
* @since 2.12
*/
public <C extends Challenge> C bindChallenge(URL location, Class<C> type) {
var challenge = bindChallenge(location);
if (!type.isInstance(challenge)) {
throw new AcmeProtocolException("Challenge type " + challenge.getType()
+ " does not match requested class " + type);
}
return type.cast(challenge);
}
/**
* Creates a {@link Challenge} instance for the given challenge data.
*
* @param data
* Challenge JSON data
* @return {@link Challenge} instance
*/
public Challenge createChallenge(JSON data) {
var challenge = session.provider().createChallenge(this, data);
if (challenge == null) {
throw new AcmeProtocolException("Could not create challenge for: " + data);
}
return challenge;
}
/**
* Creates a builder for a new {@link Order}.
*
* @return {@link OrderBuilder} object
* @since 3.0.0
*/
public OrderBuilder newOrder() {
return new OrderBuilder(this);
}
/**
* Sets a different {@link KeyPair}. The new key pair is only used locally in this
* instance, but is not set on server side!
*/
protected void setKeyPair(KeyPair keyPair) {
this.keyPair = requireNonNull(keyPair, "keyPair");
}
/**
* Creates an ACME JOSE request. This method is meant for internal purposes only.
*
* @param url
* {@link URL} of the ACME call
* @param payload
* ACME JSON payload. If {@code null}, a POST-as-GET request is generated
* instead.
* @param nonce
* Nonce to be used. {@code null} if no nonce is to be used in the JOSE
* header.
* @return JSON structure of the JOSE request, ready to be sent.
* @since 5.0.0
*/
public JSONBuilder createJoseRequest(URL url, @Nullable JSONBuilder payload, @Nullable String nonce) {
return JoseUtils.createJoseRequest(url, keyPair, payload, nonce, getAccount().getLocation().toString());
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/Metadata.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2016 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j;
import static java.util.stream.Collectors.toList;
import java.net.URI;
import java.net.URL;
import java.time.Duration;
import java.util.Collection;
import java.util.Optional;
import java.util.Set;
import org.shredzone.acme4j.exception.AcmeNotSupportedException;
import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.toolbox.JSON.Value;
/**
* A collection of metadata related to the CA provider.
*/
public class Metadata {
private final JSON meta;
/**
* Creates a new {@link Metadata} instance.
*
* @param meta
* JSON map of metadata
*/
public Metadata(JSON meta) {
this.meta = meta;
}
/**
* Returns an {@link URI} of the current terms of service, or empty if not available.
*/
public Optional<URI> getTermsOfService() {
return meta.get("termsOfService").map(Value::asURI);
}
/**
* Returns an {@link URL} of a website providing more information about the ACME
* server. Empty if not available.
*/
public Optional<URL> getWebsite() {
return meta.get("website").map(Value::asURL);
}
/**
* Returns a collection of hostnames, which the ACME server recognises as referring to
* itself for the purposes of CAA record validation. Empty if not available.
*/
public Collection<String> getCaaIdentities() {
return meta.get("caaIdentities")
.asArray()
.stream()
.map(Value::asString)
.collect(toList());
}
/**
* Returns whether an external account is required by this CA.
*/
public boolean isExternalAccountRequired() {
return meta.get("externalAccountRequired").map(Value::asBoolean).orElse(false);
}
/**
* Returns whether the CA supports short-term auto-renewal of certificates.
*
* @since 2.3
*/
public boolean isAutoRenewalEnabled() {
return meta.get("auto-renewal").isPresent();
}
/**
* Returns the minimum acceptable value for the maximum validity of a certificate
* before auto-renewal.
*
* @since 2.3
* @throws AcmeNotSupportedException if the server does not support auto-renewal.
*/
public Duration getAutoRenewalMinLifetime() {
return meta.getFeature("auto-renewal")
.map(Value::asObject)
.orElseGet(JSON::empty)
.get("min-lifetime")
.asDuration();
}
/**
* Returns the maximum delta between auto-renewal end date and auto-renewal start
* date.
*
* @since 2.3
* @throws AcmeNotSupportedException if the server does not support auto-renewal.
*/
public Duration getAutoRenewalMaxDuration() {
return meta.getFeature("auto-renewal")
.map(Value::asObject)
.orElseGet(JSON::empty)
.get("max-duration")
.asDuration();
}
/**
* Returns whether the CA also allows to fetch STAR certificates via GET request.
*
* @since 2.6
* @throws AcmeNotSupportedException if the server does not support auto-renewal.
*/
public boolean isAutoRenewalGetAllowed() {
return meta.getFeature("auto-renewal").optional()
.map(Value::asObject)
.orElseGet(JSON::empty)
.get("allow-certificate-get")
.optional()
.map(Value::asBoolean)
.orElse(false);
}
/**
* Returns whether the CA supports the profile feature.
*
* @since 3.5.0
* @draft This method is currently based on RFC draft draft-ietf-acme-profiles. It
* may be changed or removed without notice to reflect future changes to the draft.
* SemVer rules do not apply here.
*/
public boolean isProfileAllowed() {
return meta.get("profiles").isPresent();
}
/**
* Returns whether the CA supports the requested profile.
* <p>
* Also returns {@code false} if profiles are not allowed in general.
*
* @since 3.5.0
* @draft This method is currently based on RFC draft draft-ietf-acme-profiles. It
* may be changed or removed without notice to reflect future changes to the draft.
* SemVer rules do not apply here.
*/
public boolean isProfileAllowed(String profile) {
return getProfiles().contains(profile);
}
/**
* Returns all profiles supported by the CA. May be empty if the CA does not support
* profiles.
*
* @since 3.5.0
* @draft This method is currently based on RFC draft draft-ietf-acme-profiles. It
* may be changed or removed without notice to reflect future changes to the draft.
* SemVer rules do not apply here.
*/
public Set<String> getProfiles() {
return meta.get("profiles")
.optional()
.map(Value::asObject)
.orElseGet(JSON::empty)
.keySet();
}
/**
* Returns a description of the requested profile. This can be a human-readable string
* or a URL linking to a documentation.
* <p>
* Empty if the profile is not allowed.
*
* @since 3.5.0
* @draft This method is currently based on RFC draft draft-ietf-acme-profiles. It
* may be changed or removed without notice to reflect future changes to the draft.
* SemVer rules do not apply here.
*/
public Optional<String> getProfileDescription(String profile) {
return meta.get("profiles").optional()
.map(Value::asObject)
.orElseGet(JSON::empty)
.get(profile)
.optional()
.map(Value::asString);
}
/**
* Returns whether the CA supports subdomain auth according to RFC9444.
*
* @since 3.3.0
*/
public boolean isSubdomainAuthAllowed() {
return meta.get("subdomainAuthAllowed").map(Value::asBoolean).orElse(false);
}
/**
* Returns the JSON representation of the metadata. This is useful for reading
* proprietary metadata properties.
*/
public JSON getJSON() {
return meta;
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/Order.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2017 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j;
import static java.util.Collections.unmodifiableList;
import static java.util.stream.Collectors.toList;
import java.io.IOException;
import java.io.Serial;
import java.net.URL;
import java.security.KeyPair;
import java.time.Duration;
import java.time.Instant;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import edu.umd.cs.findbugs.annotations.Nullable;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeNotSupportedException;
import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.toolbox.JSON.Value;
import org.shredzone.acme4j.toolbox.JSONBuilder;
import org.shredzone.acme4j.util.CSRBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A representation of a certificate order at the CA.
*/
public class Order extends AcmeJsonResource implements PollableResource {
@Serial
private static final long serialVersionUID = 5435808648658292177L;
private static final Logger LOG = LoggerFactory.getLogger(Order.class);
private transient @Nullable Certificate certificate = null;
private transient @Nullable List<Authorization> authorizations = null;
protected Order(Login login, URL location) {
super(login, location);
}
/**
* Returns the current status of the order.
* <p>
* Possible values are: {@link Status#PENDING}, {@link Status#READY},
* {@link Status#PROCESSING}, {@link Status#VALID}, {@link Status#INVALID}.
* If the server supports STAR, another possible value is {@link Status#CANCELED}.
*/
@Override
public Status getStatus() {
return getJSON().get("status").asStatus();
}
/**
* Returns a {@link Problem} document with the reason if the order has failed.
*/
public Optional<Problem> getError() {
return getJSON().get("error").map(v -> v.asProblem(getLocation()));
}
/**
* Gets the expiry date of the authorization, if set by the server.
*/
public Optional<Instant> getExpires() {
return getJSON().get("expires").map(Value::asInstant);
}
/**
* Gets a list of {@link Identifier} that are connected to this order.
*
* @since 2.3
*/
public List<Identifier> getIdentifiers() {
return getJSON().get("identifiers")
.asArray()
.stream()
.map(Value::asIdentifier)
.toList();
}
/**
* Gets the "not before" date that was used for the order.
*/
public Optional<Instant> getNotBefore() {
return getJSON().get("notBefore").map(Value::asInstant);
}
/**
* Gets the "not after" date that was used for the order.
*/
public Optional<Instant> getNotAfter() {
return getJSON().get("notAfter").map(Value::asInstant);
}
/**
* Gets the {@link Authorization} that are required to fulfil this order, in no
* specific order.
*/
public List<Authorization> getAuthorizations() {
if (authorizations == null) {
var login = getLogin();
authorizations = getJSON().get("authorizations")
.asArray()
.stream()
.map(Value::asURL)
.map(login::bindAuthorization)
.collect(toList());
}
return unmodifiableList(authorizations);
}
/**
* Gets the location {@link URL} of where to send the finalization call to.
* <p>
* For internal purposes. Use {@link #execute(byte[])} to finalize an order.
*/
public URL getFinalizeLocation() {
return getJSON().get("finalize").asURL();
}
/**
* Gets the {@link Certificate}.
*
* @throws IllegalStateException
* if the order is not ready yet. You must finalize the order first, and wait
* for the status to become {@link Status#VALID}.
*/
@SuppressFBWarnings("EI_EXPOSE_REP") // behavior is intended
public Certificate getCertificate() {
if (certificate == null) {
certificate = getJSON().get("star-certificate")
.optional()
.or(() -> getJSON().get("certificate").optional())
.map(Value::asURL)
.map(getLogin()::bindCertificate)
.orElseThrow(() -> new IllegalStateException("Order is not completed"));
}
return certificate;
}
/**
* Returns whether this is a STAR certificate ({@code true}) or a standard certificate
* ({@code false}).
*
* @since 3.5.0
*/
public boolean isAutoRenewalCertificate() {
return getJSON().contains("star-certificate");
}
/**
* Finalizes the order.
* <p>
* If the finalization was successful, the certificate is provided via
* {@link #getCertificate()}.
* <p>
* Even though the ACME protocol uses the term "finalize an order", this method is
* called {@link #execute(KeyPair)} to avoid confusion with the problematic
* {@link Object#finalize()} method.
*
* @param domainKeyPair
* The {@link KeyPair} that is going to be certified. This is <em>not</em>
* your account's keypair!
* @see #execute(KeyPair, Consumer)
* @see #execute(PKCS10CertificationRequest)
* @see #execute(byte[])
* @see #waitUntilReady(Duration)
* @see #waitForCompletion(Duration)
* @since 3.0.0
*/
public void execute(KeyPair domainKeyPair) throws AcmeException {
execute(domainKeyPair, csrBuilder -> {});
}
/**
* Finalizes the order (see {@link #execute(KeyPair)}).
* <p>
* This method also accepts a builderConsumer that can be used to add further details
* to the CSR (e.g. your organization). The identifiers (IPs, domain names, etc.) are
* automatically added to the CSR.
*
* @param domainKeyPair
* The {@link KeyPair} that is going to be used together with the certificate.
* This is not your account's keypair!
* @param builderConsumer
* {@link Consumer} that adds further details to the provided
* {@link CSRBuilder}.
* @see #execute(KeyPair)
* @see #execute(PKCS10CertificationRequest)
* @see #execute(byte[])
* @see #waitUntilReady(Duration)
* @see #waitForCompletion(Duration)
* @since 3.0.0
*/
public void execute(KeyPair domainKeyPair, Consumer<CSRBuilder> builderConsumer) throws AcmeException {
try {
var csrBuilder = new CSRBuilder();
csrBuilder.addIdentifiers(getIdentifiers());
builderConsumer.accept(csrBuilder);
csrBuilder.sign(domainKeyPair);
execute(csrBuilder.getCSR());
} catch (IOException ex) {
throw new AcmeException("Failed to create CSR", ex);
}
}
/**
* Finalizes the order (see {@link #execute(KeyPair)}).
* <p>
* This method receives a {@link PKCS10CertificationRequest} instance of a CSR that
* was generated externally. Use this method to gain full control over the content of
* the CSR. The CSR is not checked by acme4j, but just transported to the CA. It is
* your responsibility that it matches to the order.
*
* @param csr
* {@link PKCS10CertificationRequest} to be used for this order.
* @see #execute(KeyPair)
* @see #execute(KeyPair, Consumer)
* @see #execute(byte[])
* @see #waitUntilReady(Duration)
* @see #waitForCompletion(Duration)
* @since 3.0.0
*/
public void execute(PKCS10CertificationRequest csr) throws AcmeException {
try {
execute(csr.getEncoded());
} catch (IOException ex) {
throw new AcmeException("Invalid CSR", ex);
}
}
/**
* Finalizes the order (see {@link #execute(KeyPair)}).
* <p>
* This method receives a byte array containing an encoded CSR that was generated
* externally. Use this method to gain full control over the content of the CSR. The
* CSR is not checked by acme4j, but just transported to the CA. It is your
* responsibility that it matches to the order.
*
* @param csr
* Binary representation of a CSR containing the parameters for the
* certificate being requested, in DER format
* @see #waitUntilReady(Duration)
* @see #waitForCompletion(Duration)
*/
public void execute(byte[] csr) throws AcmeException {
LOG.debug("finalize");
try (var conn = getSession().connect()) {
var claims = new JSONBuilder();
claims.putBase64("csr", csr);
conn.sendSignedRequest(getFinalizeLocation(), claims, getLogin());
}
invalidate();
}
/**
* Waits until the order is ready for finalization.
* <p>
* Is is ready if it reaches {@link Status#READY}. The method will also return if the
* order already has another terminal state, which is either {@link Status#VALID} or
* {@link Status#INVALID}.
* <p>
* This method is synchronous and blocks the current thread.
*
* @param timeout
* Timeout until a terminal status must have been reached
* @return Status that was reached
* @since 3.4.0
*/
public Status waitUntilReady(Duration timeout)
throws AcmeException, InterruptedException {
return waitForStatus(EnumSet.of(Status.READY, Status.VALID, Status.INVALID), timeout);
}
/**
* Waits until the order finalization is completed.
* <p>
* Is is completed if it reaches either {@link Status#VALID} or
* {@link Status#INVALID}.
* <p>
* This method is synchronous and blocks the current thread.
*
* @param timeout
* Timeout until a terminal status must have been reached
* @return Status that was reached
* @since 3.4.0
*/
public Status waitForCompletion(Duration timeout)
throws AcmeException, InterruptedException {
return waitForStatus(EnumSet.of(Status.VALID, Status.INVALID), timeout);
}
/**
* Checks if this order is auto-renewing, according to the ACME STAR specifications.
*
* @since 2.3
*/
public boolean isAutoRenewing() {
return getJSON().get("auto-renewal")
.optional()
.isPresent();
}
/**
* Returns the earliest date of validity of the first certificate issued.
*
* @since 2.3
* @throws AcmeNotSupportedException if auto-renewal is not supported
*/
public Optional<Instant> getAutoRenewalStartDate() {
return getJSON().getFeature("auto-renewal")
.map(Value::asObject)
.orElseGet(JSON::empty)
.get("start-date")
.optional()
.map(Value::asInstant);
}
/**
* Returns the latest date of validity of the last certificate issued.
*
* @since 2.3
* @throws AcmeNotSupportedException if auto-renewal is not supported
*/
public Instant getAutoRenewalEndDate() {
return getJSON().getFeature("auto-renewal")
.map(Value::asObject)
.orElseGet(JSON::empty)
.get("end-date")
.asInstant();
}
/**
* Returns the maximum lifetime of each certificate.
*
* @since 2.3
* @throws AcmeNotSupportedException if auto-renewal is not supported
*/
public Duration getAutoRenewalLifetime() {
return getJSON().getFeature("auto-renewal")
.optional()
.map(Value::asObject)
.orElseGet(JSON::empty)
.get("lifetime")
.asDuration();
}
/**
* Returns the pre-date period of each certificate.
*
* @since 2.7
* @throws AcmeNotSupportedException if auto-renewal is not supported
*/
public Optional<Duration> getAutoRenewalLifetimeAdjust() {
return getJSON().getFeature("auto-renewal")
.optional()
.map(Value::asObject)
.orElseGet(JSON::empty)
.get("lifetime-adjust")
.optional()
.map(Value::asDuration);
}
/**
* Returns {@code true} if STAR certificates from this order can also be fetched via
* GET requests.
*
* @since 2.6
*/
public boolean isAutoRenewalGetEnabled() {
return getJSON().getFeature("auto-renewal")
.optional()
.map(Value::asObject)
.orElseGet(JSON::empty)
.get("allow-certificate-get")
.optional()
.map(Value::asBoolean)
.orElse(false);
}
/**
* Cancels an auto-renewing order.
*
* @since 2.3
*/
public void cancelAutoRenewal() throws AcmeException {
if (!getSession().getMetadata().isAutoRenewalEnabled()) {
throw new AcmeNotSupportedException("auto-renewal");
}
LOG.debug("cancel");
try (var conn = getSession().connect()) {
var claims = new JSONBuilder();
claims.put("status", "canceled");
conn.sendSignedRequest(getLocation(), claims, getLogin());
setJSON(conn.readJsonResponse());
}
}
/**
* Returns the selected profile.
*
* @since 3.5.0
* @throws AcmeNotSupportedException if profile is not supported
*/
public String getProfile() {
return getJSON().getFeature("profile").asString();
}
@Override
protected void invalidate() {
super.invalidate();
certificate = null;
authorizations = null;
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/OrderBuilder.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2017 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
import static org.shredzone.acme4j.toolbox.AcmeUtils.getRenewalUniqueIdentifier;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.Instant;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
import edu.umd.cs.findbugs.annotations.Nullable;
import org.shredzone.acme4j.connector.Resource;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeNotSupportedException;
import org.shredzone.acme4j.toolbox.JSONBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Start a new certificate {@link Order}.
* <p>
* Use {@link Login#newOrder()} or {@link Account#newOrder()} to create a new
* {@link OrderBuilder} instance. Both methods are identical.
*/
public class OrderBuilder {
private static final Logger LOG = LoggerFactory.getLogger(OrderBuilder.class);
private final Login login;
private final Set<Identifier> identifierSet = new LinkedHashSet<>();
private @Nullable Instant notBefore;
private @Nullable Instant notAfter;
private @Nullable String replaces;
private boolean autoRenewal;
private @Nullable Instant autoRenewalStart;
private @Nullable Instant autoRenewalEnd;
private @Nullable Duration autoRenewalLifetime;
private @Nullable Duration autoRenewalLifetimeAdjust;
private boolean autoRenewalGet;
private @Nullable String profile;
/**
* Create a new {@link OrderBuilder}.
*
* @param login
* {@link Login} to bind with
*/
protected OrderBuilder(Login login) {
this.login = login;
}
/**
* Adds a domain name to the order.
*
* @param domain
* Name of a domain to be ordered. May be a wildcard domain if supported by
* the CA. IDN names are accepted and will be ACE encoded automatically.
* @return itself
*/
public OrderBuilder domain(String domain) {
return identifier(Identifier.dns(domain));
}
/**
* Adds domain names to the order.
*
* @param domains
* Collection of domain names to be ordered. May be wildcard domains if
* supported by the CA. IDN names are accepted and will be ACE encoded
* automatically.
* @return itself
*/
public OrderBuilder domains(String... domains) {
for (var domain : requireNonNull(domains, "domains")) {
domain(domain);
}
return this;
}
/**
* Adds a collection of domain names to the order.
*
* @param domains
* Collection of domain names to be ordered. May be wildcard domains if
* supported by the CA. IDN names are accepted and will be ACE encoded
* automatically.
* @return itself
*/
public OrderBuilder domains(Collection<String> domains) {
requireNonNull(domains, "domains").forEach(this::domain);
return this;
}
/**
* Adds an {@link Identifier} to the order.
*
* @param identifier
* {@link Identifier} to be added to the order.
* @return itself
* @since 2.3
*/
public OrderBuilder identifier(Identifier identifier) {
identifierSet.add(requireNonNull(identifier, "identifier"));
return this;
}
/**
* Adds a collection of {@link Identifier} to the order.
*
* @param identifiers
* Collection of {@link Identifier} to be added to the order.
* @return itself
* @since 2.3
*/
public OrderBuilder identifiers(Collection<Identifier> identifiers) {
requireNonNull(identifiers, "identifiers").forEach(this::identifier);
return this;
}
/**
* Sets a "not before" date in the certificate. May be ignored by the CA.
*
* @param notBefore "not before" date
* @return itself
*/
public OrderBuilder notBefore(Instant notBefore) {
if (autoRenewal) {
throw new IllegalArgumentException("cannot combine notBefore with autoRenew");
}
this.notBefore = requireNonNull(notBefore, "notBefore");
return this;
}
/**
* Sets a "not after" date in the certificate. May be ignored by the CA.
*
* @param notAfter "not after" date
* @return itself
*/
public OrderBuilder notAfter(Instant notAfter) {
if (autoRenewal) {
throw new IllegalArgumentException("cannot combine notAfter with autoRenew");
}
this.notAfter = requireNonNull(notAfter, "notAfter");
return this;
}
/**
* Enables short-term automatic renewal of the certificate, if supported by the CA.
* <p>
* Automatic renewals cannot be combined with {@link #notBefore(Instant)} or
* {@link #notAfter(Instant)}.
*
* @return itself
* @since 2.3
*/
public OrderBuilder autoRenewal() {
if (notBefore != null || notAfter != null) {
throw new IllegalArgumentException("cannot combine notBefore/notAfter with autoRenewal");
}
this.autoRenewal = true;
return this;
}
/**
* Sets the earliest date of validity of the first issued certificate. If not set,
* the start date is the earliest possible date.
* <p>
* Implies {@link #autoRenewal()}.
*
* @param start
* Start date of validity
* @return itself
* @since 2.3
*/
public OrderBuilder autoRenewalStart(Instant start) {
autoRenewal();
this.autoRenewalStart = requireNonNull(start, "start");
return this;
}
/**
* Sets the latest date of validity of the last issued certificate. If not set, the
* CA's default is used.
* <p>
* Implies {@link #autoRenewal()}.
*
* @param end
* End date of validity
* @return itself
* @see Metadata#getAutoRenewalMaxDuration()
* @since 2.3
*/
public OrderBuilder autoRenewalEnd(Instant end) {
autoRenewal();
this.autoRenewalEnd = requireNonNull(end, "end");
return this;
}
/**
* Sets the maximum validity period of each certificate. If not set, the CA's
* default is used.
* <p>
* Implies {@link #autoRenewal()}.
*
* @param duration
* Duration of validity of each certificate
* @return itself
* @see Metadata#getAutoRenewalMinLifetime()
* @since 2.3
*/
public OrderBuilder autoRenewalLifetime(Duration duration) {
autoRenewal();
this.autoRenewalLifetime = requireNonNull(duration, "duration");
return this;
}
/**
* Sets the amount of pre-dating each certificate. If not set, the CA's
* default (0) is used.
* <p>
* Implies {@link #autoRenewal()}.
*
* @param duration
* Duration of certificate pre-dating
* @return itself
* @since 2.7
*/
public OrderBuilder autoRenewalLifetimeAdjust(Duration duration) {
autoRenewal();
this.autoRenewalLifetimeAdjust = requireNonNull(duration, "duration");
return this;
}
/**
* Announces that the client wishes to fetch the auto-renewed certificate via GET
* request. If not used, the STAR certificate can only be fetched via POST-as-GET
* request. {@link Metadata#isAutoRenewalGetAllowed()} must return {@code true} in
* order for this option to work.
* <p>
* This option is only needed if you plan to fetch the STAR certificate via other
* means than by using acme4j. acme4j is fetching certificates via POST-as-GET
* request.
* <p>
* Implies {@link #autoRenewal()}.
*
* @return itself
* @since 2.6
*/
public OrderBuilder autoRenewalEnableGet() {
autoRenewal();
this.autoRenewalGet = true;
return this;
}
/**
* Notifies the CA of the desired profile of the ordered certificate.
* <p>
* Optional, only supported if the CA supports profiles. However, in this case the
* client <em>may</em> include this field.
*
* @param profile
* Identifier of the desired profile
* @return itself
* @draft This method is currently based on RFC draft draft-ietf-acme-profiles. It
* may be changed or removed without notice to reflect future changes to the draft.
* SemVer rules do not apply here.
* @since 3.5.0
*/
public OrderBuilder profile(String profile) {
this.profile = Objects.requireNonNull(profile);
return this;
}
/**
* Notifies the CA that the ordered certificate will replace a previously issued
* certificate. The certificate is identified by its ARI unique identifier.
* <p>
* Optional, only supported if the CA provides renewal information. However, in this
* case the client <em>should</em> include this field.
*
* @param uniqueId
* Certificate's renewal unique identifier.
* @return itself
* @since 3.2.0
*/
public OrderBuilder replaces(String uniqueId) {
this.replaces = Objects.requireNonNull(uniqueId);
return this;
}
/**
* Notifies the CA that the ordered certificate will replace a previously issued
* certificate.
* <p>
* Optional, only supported if the CA provides renewal information. However, in this
* case the client <em>should</em> include this field.
*
* @param certificate
* Certificate to be replaced
* @return itself
* @since 3.2.0
*/
public OrderBuilder replaces(X509Certificate certificate) {
return replaces(getRenewalUniqueIdentifier(certificate));
}
/**
* Notifies the CA that the ordered certificate will replace a previously issued
* certificate.
* <p>
* Optional, only supported if the CA provides renewal information. However, in this
* case the client <em>should</em> include this field.
*
* @param certificate
* Certificate to be replaced
* @return itself
* @since 3.2.0
*/
public OrderBuilder replaces(Certificate certificate) {
return replaces(certificate.getCertificate());
}
/**
* Sends a new order to the server, and returns an {@link Order} object.
*
* @return {@link Order} that was created
*/
public Order create() throws AcmeException {
if (identifierSet.isEmpty()) {
throw new IllegalArgumentException("At least one identifer is required");
}
var session = login.getSession();
if (autoRenewal && !session.getMetadata().isAutoRenewalEnabled()) {
throw new AcmeNotSupportedException("auto-renewal");
}
if (autoRenewalGet && !session.getMetadata().isAutoRenewalGetAllowed()) {
throw new AcmeNotSupportedException("auto-renewal-get");
}
if (replaces != null && session.resourceUrlOptional(Resource.RENEWAL_INFO).isEmpty()) {
throw new AcmeNotSupportedException("renewal-information");
}
if (profile != null && !session.getMetadata().isProfileAllowed()) {
throw new AcmeNotSupportedException("profile");
}
if (profile != null && !session.getMetadata().isProfileAllowed(profile)) {
throw new AcmeNotSupportedException("profile: " + profile);
}
var hasAncestorDomain = identifierSet.stream()
.filter(id -> Identifier.TYPE_DNS.equals(id.getType()))
.anyMatch(id -> id.toMap().containsKey(Identifier.KEY_ANCESTOR_DOMAIN));
if (hasAncestorDomain && !login.getSession().getMetadata().isSubdomainAuthAllowed()) {
throw new AcmeNotSupportedException("ancestor-domain");
}
LOG.debug("create");
try (var conn = session.connect()) {
var claims = new JSONBuilder();
claims.array("identifiers", identifierSet.stream().map(Identifier::toMap).collect(toList()));
if (notBefore != null) {
claims.put("notBefore", notBefore);
}
if (notAfter != null) {
claims.put("notAfter", notAfter);
}
if (autoRenewal) {
var arClaims = claims.object("auto-renewal");
if (autoRenewalStart != null) {
arClaims.put("start-date", autoRenewalStart);
}
if (autoRenewalStart != null) {
arClaims.put("end-date", autoRenewalEnd);
}
if (autoRenewalLifetime != null) {
arClaims.put("lifetime", autoRenewalLifetime);
}
if (autoRenewalLifetimeAdjust != null) {
arClaims.put("lifetime-adjust", autoRenewalLifetimeAdjust);
}
if (autoRenewalGet) {
arClaims.put("allow-certificate-get", autoRenewalGet);
}
}
if (replaces != null) {
claims.put("replaces", replaces);
}
if (profile != null) {
claims.put("profile", profile);
}
conn.sendSignedRequest(session.resourceUrl(Resource.NEW_ORDER), claims, login);
var order = new Order(login, conn.getLocation());
order.setJSON(conn.readJsonResponse());
return order;
}
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/PollableResource.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2024 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j;
import static java.time.Instant.now;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import org.shredzone.acme4j.exception.AcmeException;
/**
* Marks an ACME Resource with a pollable status.
* <p>
* The resource provides a status, and a method for updating the internal cache to read
* the current status from the server.
*
* @since 3.4.0
*/
public interface PollableResource {
/**
* Default delay between status polls if there is no Retry-After header.
*/
Duration DEFAULT_RETRY_AFTER = Duration.ofSeconds(3L);
/**
* Returns the current status of the resource.
*/
Status getStatus();
/**
* Fetches the current status from the server.
*
* @return Retry-After time, if given by the CA, otherwise empty.
*/
Optional<Instant> fetch() throws AcmeException;
/**
* Waits until a terminal status has been reached, by polling until one of the given
* status or the given timeout has been reached. This call honors the Retry-After
* header if set by the CA.
* <p>
* This method is synchronous and blocks the current thread.
* <p>
* If the resource is already in a terminal status, the method returns immediately.
*
* @param statusSet
* Set of {@link Status} that are accepted as terminal
* @param timeout
* Timeout until a terminal status must have been reached
* @return Status that was reached
*/
default Status waitForStatus(Set<Status> statusSet, Duration timeout)
throws AcmeException, InterruptedException {
Objects.requireNonNull(timeout, "timeout");
Objects.requireNonNull(statusSet, "statusSet");
if (statusSet.isEmpty()) {
throw new IllegalArgumentException("At least one Status is required");
}
var currentStatus = getStatus();
if (statusSet.contains(currentStatus)) {
return currentStatus;
}
var timebox = now().plus(timeout);
Instant now;
while ((now = now()).isBefore(timebox)) {
// Poll status and get the time of the next poll
var retryAfter = fetch()
.orElse(now.plus(DEFAULT_RETRY_AFTER));
currentStatus = getStatus();
if (statusSet.contains(currentStatus)) {
return currentStatus;
}
// Preemptively end the loop if the next iteration would be after timebox
if (retryAfter.isAfter(timebox)) {
break;
}
// Wait until retryAfter is reached
Thread.sleep(now.until(retryAfter, ChronoUnit.MILLIS));
}
throw new AcmeException("Timeout has been reached");
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2017 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j;
import java.io.Serial;
import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import java.util.Optional;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.toolbox.JSON.Value;
/**
* A JSON problem. It contains further, machine- and human-readable details about the
* reason of an error or failure.
*
* @see <a href="https://tools.ietf.org/html/rfc7807">RFC 7807</a>
*/
public class Problem implements Serializable {
@Serial
private static final long serialVersionUID = -8418248862966754214L;
private final URL baseUrl;
private final JSON problemJson;
/**
* Creates a new {@link Problem} object.
*
* @param problem
* Problem as JSON structure
* @param baseUrl
* Document's base {@link URL} to resolve relative URIs against
*/
public Problem(JSON problem, URL baseUrl) {
this.problemJson = problem;
this.baseUrl = baseUrl;
}
/**
* Returns the problem type. It is always an absolute URI.
*/
public URI getType() {
return problemJson.get("type")
.map(Value::asString)
.map(it -> {
try {
return baseUrl.toURI().resolve(it);
} catch (URISyntaxException ex) {
throw new IllegalArgumentException("Bad base URL", ex);
}
})
.orElseThrow(() -> new AcmeProtocolException("Problem without type"));
}
/**
* Returns a short, human-readable summary of the problem. The text may be localized
* if supported by the server. Empty if the server did not provide a title.
*
* @see #toString()
*/
public Optional<String> getTitle() {
return problemJson.get("title").map(Value::asString);
}
/**
* Returns a detailed and specific human-readable explanation of the problem. The
* text may be localized if supported by the server.
*
* @see #toString()
*/
public Optional<String> getDetail() {
return problemJson.get("detail").map(Value::asString);
}
/**
* Returns a URI that identifies the specific occurence of the problem. It is always
* an absolute URI.
*/
public Optional<URI> getInstance() {
return problemJson.get("instance")
.map(Value::asString)
.map(it -> {
try {
return baseUrl.toURI().resolve(it);
} catch (URISyntaxException ex) {
throw new IllegalArgumentException("Bad base URL", ex);
}
});
}
/**
* Returns the {@link Identifier} this problem relates to.
*
* @since 2.3
*/
public Optional<Identifier> getIdentifier() {
return problemJson.get("identifier")
.optional()
.map(Value::asIdentifier);
}
/**
* Returns a list of sub-problems.
*/
public List<Problem> getSubProblems() {
return problemJson.get("subproblems")
.asArray()
.stream()
.map(o -> o.asProblem(baseUrl))
.toList();
}
/**
* Returns the problem as {@link JSON} object, to access other, non-standard fields.
*
* @return Problem as {@link JSON} object
*/
public JSON asJSON() {
return problemJson;
}
/**
* Returns a human-readable description of the problem, that is as specific as
* possible. The description may be localized if supported by the server.
* <p>
* If {@link #getSubProblems()} exist, they will be appended.
* <p>
* Technically, it returns {@link #getDetail()}. If not set, {@link #getTitle()} is
* returned instead. As a last resort, {@link #getType()} is returned.
*/
@Override
public String toString() {
var sb = new StringBuilder();
if (getDetail().isPresent()) {
sb.append(getDetail().get());
} else if (getTitle().isPresent()) {
sb.append(getTitle().get());
} else {
sb.append(getType());
}
var subproblems = getSubProblems();
if (!subproblems.isEmpty()) {
sb.append(" (");
var first = true;
for (var sub : subproblems) {
if (!first) {
sb.append(" ‒ ");
}
sb.append(sub.toString());
first = false;
}
sb.append(')');
}
return sb.toString();
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/RenewalInfo.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2023 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j;
import java.net.URL;
import java.time.Instant;
import java.time.temporal.TemporalAmount;
import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
import edu.umd.cs.findbugs.annotations.Nullable;
import org.shredzone.acme4j.connector.Connection;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.JSON.Value;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Renewal Information of a certificate.
*
* @since 3.0.0
*/
public class RenewalInfo extends AcmeJsonResource {
private static final Logger LOG = LoggerFactory.getLogger(RenewalInfo.class);
protected RenewalInfo(Login login, URL location) {
super(login, location);
}
/**
* Returns the starting {@link Instant} of the time window the CA recommends for
* certificate renewal.
*/
public Instant getSuggestedWindowStart() {
return getJSON().get("suggestedWindow").asObject().get("start").asInstant();
}
/**
* Returns the ending {@link Instant} of the time window the CA recommends for
* certificate renewal.
*/
public Instant getSuggestedWindowEnd() {
return getJSON().get("suggestedWindow").asObject().get("end").asInstant();
}
/**
* An optional {@link URL} pointing to a page which may explain why the suggested
* renewal window is what it is.
*/
public Optional<URL> getExplanation() {
return getJSON().get("explanationURL").optional().map(Value::asURL);
}
/**
* Checks if the given {@link Instant} is before the suggested time window, so a
* certificate renewal is not required yet.
*
* @param instant
* {@link Instant} to check
* @return {@code true} if the {@link Instant} is before the time window, {@code
* false} otherwise.
*/
public boolean renewalIsNotRequired(Instant instant) {
assertValidTimeWindow();
return instant.isBefore(getSuggestedWindowStart());
}
/**
* Checks if the given {@link Instant} is within the suggested time window, and a
* certificate renewal is recommended.
* <p>
* An {@link Instant} is deemed to be within the time window if it is equal to, or
* after {@link #getSuggestedWindowStart()}, and before {@link
* #getSuggestedWindowEnd()}.
*
* @param instant
* {@link Instant} to check
* @return {@code true} if the {@link Instant} is within the time window, {@code
* false} otherwise.
*/
public boolean renewalIsRecommended(Instant instant) {
assertValidTimeWindow();
return !instant.isBefore(getSuggestedWindowStart())
&& instant.isBefore(getSuggestedWindowEnd());
}
/**
* Checks if the given {@link Instant} is past the time window, and a certificate
* renewal is overdue.
* <p>
* An {@link Instant} is deemed to be past the time window if it is equal to, or after
* {@link #getSuggestedWindowEnd()}.
*
* @param instant
* {@link Instant} to check
* @return {@code true} if the {@link Instant} is past the time window, {@code false}
* otherwise.
*/
public boolean renewalIsOverdue(Instant instant) {
assertValidTimeWindow();
return !instant.isBefore(getSuggestedWindowEnd());
}
/**
* Returns a proposed {@link Instant} when the certificate related to this
* {@link RenewalInfo} should be renewed.
* <p>
* This method is useful for setting alarms for renewal cron jobs. As a parameter, the
* frequency of the cron job is set. The resulting {@link Instant} is guaranteed to be
* executed in time, considering the cron job intervals.
* <p>
* This method uses {@link ThreadLocalRandom} for random numbers. It is sufficient for
* most cases, as only an "earliest" {@link Instant} is returned, but the actual
* renewal process also depends on cron job execution times and other factors like
* system load.
* <p>
* The result is empty if it is impossible to renew the certificate in time, under the
* given circumstances. This is either because the time window already ended in the
* past, or because the cron job would not be executed before the ending of the time
* window. In this case, it is recommended to renew the certificate immediately.
*
* @param frequency
* Frequency of the cron job executing the certificate renewals. May be
* {@code null} if there is no cron job, and the renewal is going to be
* executed exactly at the given {@link Instant}.
* @return Random {@link Instant} when the certificate should be renewed. This instant
* might be slightly in the past. In this case, start the renewal process at the next
* possible regular moment.
*/
public Optional<Instant> getRandomProposal(@Nullable TemporalAmount frequency) {
assertValidTimeWindow();
Instant start = Instant.now();
Instant suggestedStart = getSuggestedWindowStart();
if (start.isBefore(suggestedStart)) {
start = suggestedStart;
}
Instant end = getSuggestedWindowEnd();
if (frequency != null) {
end = end.minus(frequency);
}
if (!end.isAfter(start)) {
return Optional.empty();
}
return Optional.of(Instant.ofEpochMilli(ThreadLocalRandom.current().nextLong(
start.toEpochMilli(),
end.toEpochMilli())));
}
/**
* Asserts that the end of the suggested time window is after the start.
*/
private void assertValidTimeWindow() {
if (getSuggestedWindowStart().isAfter(getSuggestedWindowEnd())) {
throw new AcmeProtocolException("Received an invalid suggested window");
}
}
@Override
public Optional<Instant> fetch() throws AcmeException {
LOG.debug("update RenewalInfo");
try (Connection conn = getSession().connect()) {
conn.sendRequest(getLocation(), getSession(), null);
setJSON(conn.readJsonResponse());
var retryAfterOpt = conn.getRetryAfter();
retryAfterOpt.ifPresent(instant -> LOG.debug("Retry-After: {}", instant));
setRetryAfter(retryAfterOpt.orElse(null));
return retryAfterOpt;
}
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/RevocationReason.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2016 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j;
import java.util.Arrays;
/**
* An enumeration of revocation reasons.
*
* @see <a href="https://tools.ietf.org/html/rfc5280#section-5.3.1">RFC 5280 Section
* 5.3.1</a>
*/
public enum RevocationReason {
UNSPECIFIED(0),
KEY_COMPROMISE(1),
CA_COMPROMISE(2),
AFFILIATION_CHANGED(3),
SUPERSEDED(4),
CESSATION_OF_OPERATION(5),
CERTIFICATE_HOLD(6),
REMOVE_FROM_CRL(8),
PRIVILEGE_WITHDRAWN(9),
AA_COMPROMISE(10);
private final int reasonCode;
RevocationReason(int reasonCode) {
this.reasonCode = reasonCode;
}
/**
* Returns the reason code as defined in RFC 5280.
*/
public int getReasonCode() {
return reasonCode;
}
/**
* Returns the {@link RevocationReason} that matches the reason code.
*
* @param reasonCode
* Reason code as defined in RFC 5280
* @return Matching {@link RevocationReason}
* @throws IllegalArgumentException if the reason code is unknown or invalid
*/
public static RevocationReason code(int reasonCode) {
return Arrays.stream(values())
.filter(rr -> rr.reasonCode == reasonCode)
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Unknown revocation reason code: " + reasonCode));
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/Session.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2015 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j;
import static java.util.Objects.requireNonNull;
import java.net.URI;
import java.net.URL;
import java.net.http.HttpClient;
import java.security.KeyPair;
import java.time.ZonedDateTime;
import java.util.EnumMap;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.StreamSupport;
import edu.umd.cs.findbugs.annotations.Nullable;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.shredzone.acme4j.connector.Connection;
import org.shredzone.acme4j.connector.NetworkSettings;
import org.shredzone.acme4j.connector.NonceHolder;
import org.shredzone.acme4j.connector.Resource;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeNotSupportedException;
import org.shredzone.acme4j.provider.AcmeProvider;
import org.shredzone.acme4j.provider.GenericAcmeProvider;
import org.shredzone.acme4j.toolbox.AcmeUtils;
import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.toolbox.JSON.Value;
/**
* A {@link Session} tracks the entire communication with a CA.
* <p>
* To create a session instance, use its constructor. It requires the URI of the ACME
* server to connect to. This can be the location of the CA's directory (via {@code http}
* or {@code https} protocol), or a special URI (via {@code acme} protocol). See the
* documentation about valid URIs.
* <p>
* Starting with version 4.0.0, a session instance can be shared between multiple threads.
* A session won't perform parallel HTTP connections. For high-load scenarios, it is
* recommended to use multiple sessions.
*/
public class Session {
private static final GenericAcmeProvider GENERIC_PROVIDER = new GenericAcmeProvider();
private final AtomicReference<Map<Resource, URL>> resourceMap = new AtomicReference<>();
private final AtomicReference<Metadata> metadata = new AtomicReference<>();
private final AtomicReference<HttpClient> httpClient = new AtomicReference<>();
private final ReentrantLock nonceLock = new ReentrantLock();
private final NetworkSettings networkSettings = new NetworkSettings();
private final URI serverUri;
private final AcmeProvider provider;
private @Nullable String nonce;
private @Nullable Locale locale = Locale.getDefault();
private String languageHeader = AcmeUtils.localeToLanguageHeader(Locale.getDefault());
protected @Nullable ZonedDateTime directoryLastModified;
protected @Nullable ZonedDateTime directoryExpires;
/**
* Creates a new {@link Session}.
*
* @param serverUri
* URI string of the ACME server to connect to. This is either the location of
* the CA's ACME directory (using {@code http} or {@code https} protocol), or
* a special URI (using the {@code acme} protocol).
* @throws IllegalArgumentException
* if no ACME provider was found for the server URI.
*/
public Session(String serverUri) {
this(URI.create(serverUri));
}
/**
* Creates a new {@link Session}.
*
* @param serverUri
* {@link URI} of the ACME server to connect to. This is either the location
* of the CA's ACME directory (using {@code http} or {@code https} protocol),
* or a special URI (using the {@code acme} protocol).
* @throws IllegalArgumentException
* if no ACME provider was found for the server URI.
*/
public Session(URI serverUri) {
this.serverUri = requireNonNull(serverUri, "serverUri");
if (GENERIC_PROVIDER.accepts(serverUri)) {
provider = GENERIC_PROVIDER;
return;
}
var providers = ServiceLoader.load(AcmeProvider.class);
provider = StreamSupport.stream(providers.spliterator(), false)
.filter(p -> p.accepts(serverUri))
.reduce((a, b) -> {
throw new IllegalArgumentException("Both ACME providers "
+ a.getClass().getSimpleName() + " and "
+ b.getClass().getSimpleName() + " accept "
+ serverUri + ". Please check your classpath.");
})
.orElseThrow(() -> new IllegalArgumentException("No ACME provider found for " + serverUri));
}
/**
* Creates a new {@link Session} using the given {@link AcmeProvider}.
* <p>
* This constructor is only to be used for testing purposes.
*
* @param serverUri
* {@link URI} of the ACME server
* @param provider
* {@link AcmeProvider} to be used
* @since 2.8
*/
public Session(URI serverUri, AcmeProvider provider) {
this.serverUri = requireNonNull(serverUri, "serverUri");
this.provider = requireNonNull(provider, "provider");
if (!provider.accepts(serverUri)) {
throw new IllegalArgumentException("Provider does not accept " + serverUri);
}
}
/**
* Logs into an existing account.
*
* @param accountLocation
* Location {@link URL} of the account
* @param accountKeyPair
* Account {@link KeyPair}
* @return {@link Login} to this account
*/
public Login login(URL accountLocation, KeyPair accountKeyPair) {
return new Login(accountLocation, accountKeyPair, this);
}
/**
* Gets the ACME server {@link URI} of this session.
*/
public URI getServerUri() {
return serverUri;
}
/**
* Locks the Session for the current thread, and returns a {@link NonceHolder}.
* <p>
* The current thread can lock the nonce multiple times. Other threads have to wait
* until the current thread unlocks the nonce.
*
* @since 4.0.0
*/
public NonceHolder lockNonce() {
nonceLock.lock();
return new NonceHolder() {
@Override
public String getNonce() {
return Session.this.nonce;
}
@Override
public void setNonce(@Nullable String nonce) {
Session.this.nonce = nonce;
}
@Override
public void close() {
nonceLock.unlock();
}
};
}
/**
* Gets the current locale of this session, or {@code null} if no special language is
* selected.
*/
@Nullable
public Locale getLocale() {
return locale;
}
/**
* Sets the locale used in this session. The locale is passed to the server as
* Accept-Language header. The server <em>may</em> respond with localized messages.
* The default is the system's language. If set to {@code null}, any language will be
* accepted.
*/
public void setLocale(@Nullable Locale locale) {
this.locale = locale;
this.languageHeader = AcmeUtils.localeToLanguageHeader(locale);
}
/**
* Gets an Accept-Language header value that matches the current locale. This method
* is mainly for internal use.
*
* @since 3.0.0
*/
public String getLanguageHeader() {
return languageHeader;
}
/**
* Returns the current {@link NetworkSettings}.
*
* @return {@link NetworkSettings}
* @since 2.8
*/
@SuppressFBWarnings("EI_EXPOSE_REP") // behavior is intended
public NetworkSettings networkSettings() {
return networkSettings;
}
/**
* Returns the {@link AcmeProvider} that is used for this session.
*
* @return {@link AcmeProvider}
*/
public AcmeProvider provider() {
return provider;
}
/**
* Returns a new {@link Connection} to the ACME server.
*
* @return {@link Connection}
*/
public Connection connect() {
return provider.connect(getServerUri(), networkSettings, getHttpClient());
}
/**
* Returns the shared {@link HttpClient} instance for this session. The instance is
* created lazily on first access and then cached for reuse. This allows multiple
* connections to share the same HTTP client, improving resource utilization and
* connection pooling.
*
* @return Shared {@link HttpClient} instance
* @since 4.0.0
*/
public HttpClient getHttpClient() {
var result = httpClient.get();
if (result == null) {
result = httpClient.updateAndGet(
client -> client != null
? client
: provider.createHttpClient(networkSettings)
);
}
return result;
}
/**
* Gets the {@link URL} of the given {@link Resource}. This may involve connecting to
* the server and fetching the directory. The result is cached.
*
* @param resource
* {@link Resource} to get the {@link URL} of
* @return {@link URL} of the resource
* @throws AcmeException
* if the server does not offer the {@link Resource}
*/
public URL resourceUrl(Resource resource) throws AcmeException {
return resourceUrlOptional(resource)
.orElseThrow(() -> new AcmeNotSupportedException(resource.path()));
}
/**
* Gets the {@link URL} of the given {@link Resource}. This may involve connecting to
* the server and fetching the directory. The result is cached.
*
* @param resource
* {@link Resource} to get the {@link URL} of
* @return {@link URL} of the resource, or empty if the resource is not available.
* @since 3.0.0
*/
public Optional<URL> resourceUrlOptional(Resource resource) throws AcmeException {
readDirectory();
return Optional.ofNullable(resourceMap.get()
.get(requireNonNull(resource, "resource")));
}
/**
* Gets the metadata of the provider's directory. This may involve connecting to the
* server and fetching the directory. The result is cached.
*
* @return {@link Metadata}. May contain no data, but is never {@code null}.
*/
public Metadata getMetadata() throws AcmeException {
readDirectory();
return metadata.get();
}
/**
* Returns the date when the directory has been modified the last time.
*
* @return The last modification date of the directory, or {@code null} if unknown
* (directory has not been read yet or did not provide this information).
* @since 2.10
*/
@Nullable
public ZonedDateTime getDirectoryLastModified() {
return directoryLastModified;
}
/**
* Sets the date when the directory has been modified the last time. Should only be
* invoked by {@link AcmeProvider} implementations.
*
* @param directoryLastModified
* The last modification date of the directory, or {@code null} if unknown
* (directory has not been read yet or did not provide this information).
* @since 2.10
*/
public void setDirectoryLastModified(@Nullable ZonedDateTime directoryLastModified) {
this.directoryLastModified = directoryLastModified;
}
/**
* Returns the date when the current directory records will expire. A fresh copy of
* the directory will be fetched automatically after that instant.
*
* @return The expiration date, or {@code null} if the server did not provide this
* information.
* @since 2.10
*/
@Nullable
public ZonedDateTime getDirectoryExpires() {
return directoryExpires;
}
/**
* Sets the date when the current directory will expire. Should only be invoked by
* {@link AcmeProvider} implementations.
*
* @param directoryExpires
* Expiration date, or {@code null} if the server did not provide this
* information.
* @since 2.10
*/
public void setDirectoryExpires(@Nullable ZonedDateTime directoryExpires) {
this.directoryExpires = directoryExpires;
}
/**
* Returns {@code true} if a copy of the directory is present in a local cache. It is
* not evaluated if the cached copy has expired though.
*
* @return {@code true} if a directory is available.
* @since 2.10
*/
public boolean hasDirectory() {
return resourceMap.get() != null;
}
/**
* Purges the directory cache. Makes sure that a fresh copy of the directory will be
* read from the CA on the next time the directory is accessed.
*
* @since 3.0.0
*/
public void purgeDirectoryCache() {
setDirectoryLastModified(null);
setDirectoryExpires(null);
resourceMap.set(null);
}
/**
* Reads the provider's directory, then rebuild the resource map. The resource map
* is unchanged if the {@link AcmeProvider} returns that the directory has not been
* changed on the remote side.
*/
private void readDirectory() throws AcmeException {
var directoryJson = provider().directory(this, getServerUri());
if (directoryJson == null) {
if (!hasDirectory()) {
throw new AcmeException("AcmeProvider did not provide a directory");
}
return;
}
var meta = directoryJson.get("meta");
if (meta.isPresent()) {
metadata.set(new Metadata(meta.asObject()));
} else {
metadata.set(new Metadata(JSON.empty()));
}
var map = new EnumMap<Resource, URL>(Resource.class);
for (var res : Resource.values()) {
directoryJson.get(res.path())
.map(Value::asURL)
.ifPresent(url -> map.put(res, url));
}
resourceMap.set(map);
}
@Override
protected final void finalize() {
// CT_CONSTRUCTOR_THROW: Prevents finalizer attack
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/Status.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2015 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j;
import java.util.Arrays;
import java.util.Locale;
/**
* An enumeration of status codes of challenges and authorizations.
*/
public enum Status {
/**
* The server has created the resource, and is waiting for the client to process it.
*/
PENDING,
/**
* The {@link Order} is ready to be finalized. Invoke {@link Order#execute(byte[])}.
*/
READY,
/**
* The server is processing the resource. The client should invoke
* {@link AcmeJsonResource#fetch()} and re-check the status.
*/
PROCESSING,
/**
* The resource is valid and can be used as intended.
*/
VALID,
/**
* An error or authorization/validation failure has occured. The client should check
* for error messages.
*/
INVALID,
/**
* The {@link Authorization} has been revoked by the server.
*/
REVOKED,
/**
* The {@link Account} or {@link Authorization} has been deactivated by the client.
*/
DEACTIVATED,
/**
* The {@link Authorization} is expired.
*/
EXPIRED,
/**
* An auto-renewing {@link Order} is canceled.
*
* @since 2.3
*/
CANCELED,
/**
* The server did not provide a status, or the provided status is not a specified ACME
* status.
*/
UNKNOWN;
/**
* Parses the string and returns a corresponding Status object.
*
* @param str
* String to parse
* @return {@link Status} matching the string, or {@link Status#UNKNOWN} if there was
* no match
*/
public static Status parse(String str) {
var check = str.toUpperCase(Locale.ENGLISH);
return Arrays.stream(values())
.filter(s -> s.name().equals(check))
.findFirst()
.orElse(Status.UNKNOWN);
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2015 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j.challenge;
import java.io.Serial;
import java.time.Duration;
import java.time.Instant;
import java.util.EnumSet;
import java.util.Optional;
import org.shredzone.acme4j.AcmeJsonResource;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.PollableResource;
import org.shredzone.acme4j.Problem;
import org.shredzone.acme4j.Status;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.toolbox.JSON.Value;
import org.shredzone.acme4j.toolbox.JSONBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A generic challenge. It can be used as a base class for actual challenge
* implementations, but it is also used if the ACME server offers a proprietary challenge
* that is unknown to acme4j.
* <p>
* Subclasses must override {@link Challenge#acceptable(String)} so it only accepts its
* own type. {@link Challenge#prepareResponse(JSONBuilder)} can be overridden to put all
* required data to the challenge response.
*/
public class Challenge extends AcmeJsonResource implements PollableResource {
@Serial
private static final long serialVersionUID = 2338794776848388099L;
private static final Logger LOG = LoggerFactory.getLogger(Challenge.class);
protected static final String KEY_TYPE = "type";
protected static final String KEY_URL = "url";
protected static final String KEY_STATUS = "status";
protected static final String KEY_VALIDATED = "validated";
protected static final String KEY_ERROR = "error";
/**
* Creates a new generic {@link Challenge} object.
*
* @param login
* {@link Login} the resource is bound with
* @param data
* {@link JSON} challenge data
*/
public Challenge(Login login, JSON data) {
super(login, data.get(KEY_URL).asURL());
setJSON(data);
}
/**
* Returns the challenge type by name (e.g. "http-01").
*/
public String getType() {
return getJSON().get(KEY_TYPE).asString();
}
/**
* Returns the current status of the challenge.
* <p>
* Possible values are: {@link Status#PENDING}, {@link Status#PROCESSING},
* {@link Status#VALID}, {@link Status#INVALID}.
* <p>
* A challenge is only completed when it reaches either status {@link Status#VALID} or
* {@link Status#INVALID}.
*/
@Override
public Status getStatus() {
return getJSON().get(KEY_STATUS).asStatus();
}
/**
* Returns the validation date, if returned by the server.
*/
public Optional<Instant> getValidated() {
return getJSON().get(KEY_VALIDATED).map(Value::asInstant);
}
/**
* Returns a reason why the challenge has failed in the past, if returned by the
* server. If there are multiple errors, they can be found in
* {@link Problem#getSubProblems()}.
*/
public Optional<Problem> getError() {
return getJSON().get(KEY_ERROR).map(it -> it.asProblem(getLocation()));
}
/**
* Prepares the response message for triggering the challenge. Subclasses can add
* fields to the {@link JSONBuilder} as required by the challenge. Implementations of
* subclasses should make sure that {@link #prepareResponse(JSONBuilder)} of the
* superclass is invoked.
*
* @param response
* {@link JSONBuilder} to write the response to
*/
protected void prepareResponse(JSONBuilder response) {
// Do nothing here...
}
/**
* Checks if the type is acceptable to this challenge. This generic class only checks
* if the type is not blank. Subclasses should instead check if the given type matches
* expected challenge type.
*
* @param type
* Type to check
* @return {@code true} if acceptable, {@code false} if not
*/
protected boolean acceptable(String type) {
return type != null && !type.trim().isEmpty();
}
@Override
protected void setJSON(JSON json) {
var type = json.get(KEY_TYPE).asString();
if (!acceptable(type)) {
throw new AcmeProtocolException("incompatible type " + type + " for this challenge");
}
var loc = json.get(KEY_URL).asString();
if (!loc.equals(getLocation().toString())) {
throw new AcmeProtocolException("challenge has changed its location");
}
super.setJSON(json);
}
/**
* Triggers this {@link Challenge}. The ACME server is requested to validate the
* response. Note that the validation is performed asynchronously by the ACME server.
* <p>
* After a challenge is triggered, it changes to {@link Status#PENDING}. As soon as
* validation takes place, it changes to {@link Status#PROCESSING}. After validation
* the status changes to {@link Status#VALID} or {@link Status#INVALID}, depending on
* the outcome of the validation.
* <p>
* If the challenge requires a resource to be set on your side (e.g. a DNS record or
* an HTTP file), it <em>must</em> be reachable from public before {@link #trigger()}
* is invoked, and <em>must not</em> be taken down until the challenge has reached
* {@link Status#VALID} or {@link Status#INVALID}.
* <p>
* If this method is invoked a second time, the ACME server is requested to retry the
* validation. This can be useful if the client state has changed, for example after a
* firewall rule has been updated.
*
* @see #waitForCompletion(Duration)
*/
public void trigger() throws AcmeException {
LOG.debug("trigger");
try (var conn = getSession().connect()) {
var claims = new JSONBuilder();
prepareResponse(claims);
conn.sendSignedRequest(getLocation(), claims, getLogin());
setJSON(conn.readJsonResponse());
}
}
/**
* Waits until the challenge is completed.
* <p>
* Is is completed if it reaches either {@link Status#VALID} or
* {@link Status#INVALID}.
* <p>
* This method is synchronous and blocks the current thread.
*
* @param timeout
* Timeout until a terminal status must have been reached
* @return Status that was reached
* @since 3.4.0
*/
public Status waitForCompletion(Duration timeout)
throws AcmeException, InterruptedException {
return waitForStatus(EnumSet.of(Status.VALID, Status.INVALID), timeout);
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Dns01Challenge.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2015 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j.challenge;
import static org.shredzone.acme4j.toolbox.AcmeUtils.base64UrlEncode;
import static org.shredzone.acme4j.toolbox.AcmeUtils.sha256hash;
import java.io.Serial;
import org.shredzone.acme4j.Identifier;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.toolbox.JSON;
/**
* Implements the {@value TYPE} challenge. It requires a specific DNS record for domain
* validation. See the acme4j documentation for a detailed explanation.
*/
public class Dns01Challenge extends TokenChallenge {
@Serial
private static final long serialVersionUID = 6964687027713533075L;
/**
* Challenge type name: {@value}
*/
public static final String TYPE = "dns-01";
/**
* The prefix of the domain name to be used for the DNS TXT record.
*/
public static final String RECORD_NAME_PREFIX = "_acme-challenge";
/**
* Creates a new generic {@link Dns01Challenge} object.
*
* @param login
* {@link Login} the resource is bound with
* @param data
* {@link JSON} challenge data
*/
public Dns01Challenge(Login login, JSON data) {
super(login, data);
}
/**
* Converts a domain identifier to the Resource Record name to be used for the DNS TXT
* record.
*
* @param identifier
* Domain {@link Identifier} of the domain to be validated
* @return Resource Record name (e.g. {@code _acme-challenge.www.example.org.}, note
* the trailing full stop character).
* @since 4.0.0
*/
public String getRRName(Identifier identifier) {
return getRRName(identifier.getDomain());
}
/**
* Converts a domain identifier to the Resource Record name to be used for the DNS TXT
* record.
*
* @param domain
* Domain name to be validated
* @return Resource Record name (e.g. {@code _acme-challenge.www.example.org.}, note
* the trailing full stop character).
* @since 4.0.0
*/
public String getRRName(String domain) {
return RECORD_NAME_PREFIX + '.' + domain + '.';
}
/**
* Returns the digest string to be set in the domain's {@code _acme-challenge} TXT
* record.
*/
public String getDigest() {
return base64UrlEncode(sha256hash(getAuthorization()));
}
@Override
protected boolean acceptable(String type) {
return TYPE.equals(type);
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/challenge/DnsAccount01Challenge.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2025 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j.challenge;
import static org.shredzone.acme4j.toolbox.AcmeUtils.*;
import java.io.Serial;
import java.net.URL;
import java.util.Arrays;
import java.util.Locale;
import org.shredzone.acme4j.Identifier;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.toolbox.JSON;
/**
* Implements the {@value TYPE} challenge. It requires a specific DNS record for domain
* validation. See the acme4j documentation for a detailed explanation.
*
* @draft This class is currently based on an RFC draft. It may be changed or removed
* without notice to reflect future changes to the draft. SemVer rules do not apply here.
* @since 4.0.0
*/
public class DnsAccount01Challenge extends TokenChallenge {
@Serial
private static final long serialVersionUID = -1098129409378900733L;
/**
* Challenge type name: {@value}
*/
public static final String TYPE = "dns-account-01";
/**
* Creates a new generic {@link DnsAccount01Challenge} object.
*
* @param login
* {@link Login} the resource is bound with
* @param data
* {@link JSON} challenge data
*/
public DnsAccount01Challenge(Login login, JSON data) {
super(login, data);
}
/**
* Converts a domain identifier to the Resource Record name to be used for the DNS TXT
* record.
*
* @param identifier
* {@link Identifier} to be validated
* @return Resource Record name (e.g.
* {@code _ujmmovf2vn55tgye._acme-challenge.example.org.}, note the trailing full stop
* character).
*/
public String getRRName(Identifier identifier) {
return getRRName(identifier.getDomain());
}
/**
* Converts a domain identifier to the Resource Record name to be used for the DNS TXT
* record.
*
* @param domain
* Domain name to be validated
* @return Resource Record name (e.g.
* {@code _ujmmovf2vn55tgye._acme-challenge.example.org.}, note the trailing full stop
* character).
*/
public String getRRName(String domain) {
return getPrefix(getLogin().getAccount().getLocation()) + '.' + domain + '.';
}
/**
* Returns the digest string to be set in the domain's TXT record.
*/
public String getDigest() {
return base64UrlEncode(sha256hash(getAuthorization()));
}
/**
* Returns the prefix of an account location.
*/
private String getPrefix(URL accountLocation) {
var urlHash = sha256hash(accountLocation.toExternalForm());
var hash = base32Encode(Arrays.copyOfRange(urlHash, 0, 10));
return "_" + hash.toLowerCase(Locale.ENGLISH)
+ "._acme-challenge";
}
@Override
protected boolean acceptable(String type) {
return TYPE.equals(type);
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/challenge/DnsPersist01Challenge.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2026 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j.challenge;
import static java.util.Objects.requireNonNull;
import java.io.Serial;
import java.net.URISyntaxException;
import java.net.URL;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import edu.umd.cs.findbugs.annotations.Nullable;
import org.shredzone.acme4j.Identifier;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.AcmeUtils;
import org.shredzone.acme4j.toolbox.JSON;
/**
* Implements the {@value TYPE} challenge. It requires a specific DNS record for domain
* validation. See the acme4j documentation for a detailed explanation.
*
* @draft This class is currently based on an RFC draft. It may be changed or removed
* without notice to reflect future changes to the draft. SemVer rules do not apply here.
* @since 5.0.0
*/
public class DnsPersist01Challenge extends Challenge {
@Serial
private static final long serialVersionUID = 7532514098897449519L;
/**
* Challenge type name: {@value}
*/
public static final String TYPE = "dns-persist-01";
protected static final String KEY_ISSUER_DOMAIN_NAMES = "issuer-domain-names";
protected static final String RECORD_NAME_PREFIX = "_validation-persist";
protected static final String KEY_ACCOUNT_URI = "accounturi";
private static final int ISSUER_SIZE_LIMIT = 10; // according to the specs
private static final int DOMAIN_LENGTH_LIMIT = 253; // according to the specs
private @Nullable List<String> issuerDomainNames;
/**
* Creates a new generic {@link DnsPersist01Challenge} object.
*
* @param login
* {@link Login} the resource is bound with
* @param data
* {@link JSON} challenge data
*/
public DnsPersist01Challenge(Login login, JSON data) {
super(login, data);
}
/**
* Returns the list of issuer-domain-names from the CA. The list is guaranteed to
* have at least one element.
*/
public List<String> getIssuerDomainNames() {
if (issuerDomainNames == null) {
var domainNames = getJSON().get(KEY_ISSUER_DOMAIN_NAMES).asArray().stream()
.map(JSON.Value::asString)
.map(AcmeUtils::toAce)
.toList();
if (domainNames.isEmpty()) {
// malform check is mandatory according to the specification
throw new AcmeProtocolException("issuer-domain-names missing or empty");
}
if (domainNames.size() > ISSUER_SIZE_LIMIT) {
// malform check is mandatory according to the specification
throw new AcmeProtocolException("issuer-domain-names size limit exceeded: "
+ domainNames.size() + " > " + ISSUER_SIZE_LIMIT);
}
if (domainNames.stream().anyMatch(it -> it.endsWith("."))) {
throw new AcmeProtocolException("issuer-domain-names must not have trailing dots");
}
if (!domainNames.stream().allMatch(it -> it.length() <= DOMAIN_LENGTH_LIMIT)) {
throw new AcmeProtocolException("issuer-domain-names content too long");
}
issuerDomainNames = domainNames;
}
return Collections.unmodifiableList(issuerDomainNames);
}
/**
* Converts a domain identifier to the Resource Record name to be used for the DNS TXT
* record.
*
* @param identifier
* Domain {@link Identifier} of the domain to be validated
* @return Resource Record name (e.g. {@code _validation-persist.www.example.org.},
* note the trailing full stop character).
*/
public String getRRName(Identifier identifier) {
return getRRName(identifier.getDomain());
}
/**
* Converts a domain identifier to the Resource Record name to be used for the DNS TXT
* record.
*
* @param domain
* Domain name to be validated
* @return Resource Record name (e.g. {@code _validation-persist.www.example.org.},
* note the trailing full stop character).
*/
public String getRRName(String domain) {
return RECORD_NAME_PREFIX + '.' + AcmeUtils.toAce(domain) + '.';
}
/**
* Returns a builder for the RDATA value of the DNS TXT record.
*
* @return Builder for the RDATA
*/
public Builder buildRData() {
return new Builder(getLogin(), getIssuerDomainNames());
}
/**
* Convenience call to get a standard RDATA without optional tags.
*
* @return RRDATA
*/
public String getRData() {
return buildRData().build();
}
/**
* Returns the Account URI that is expected to request the validation.
*
* @since 5.1.0
*/
public URL getAccountUrl() {
return getJSON().get(KEY_ACCOUNT_URI).asURL();
}
@Override
protected void invalidate() {
super.invalidate();
issuerDomainNames = null;
}
@Override
protected boolean acceptable(String type) {
return TYPE.equals(type);
}
@Override
protected void setJSON(JSON json) {
super.setJSON(json);
// TODO: In a future release, KEY_ACCOUNT_URI is expected to be mandatory,
// and this check will always apply!
if (getJSON().contains(KEY_ACCOUNT_URI)) {
try {
var expectedAccount = getJSON().get(KEY_ACCOUNT_URI).asURI();
var actualAccount = getLogin().getAccount().getLocation().toURI();
if (!actualAccount.equals(expectedAccount)) {
throw new AcmeProtocolException("challenge is intended for a different account: " + expectedAccount);
}
} catch (URISyntaxException ex) {
throw new IllegalStateException("Account URL is not an URI?", ex);
}
}
}
/**
* Builder for RDATA.
* <p>
* The following default values are assumed unless overridden by one of the builder
* methods:
* <ul>
* <li>The first issuer domain name from the list of issuer domain names is used</li>
* <li>No wildcard domain</li>
* <li>No persistence limit</li>
* <li>Generate quote-enclosed strings</li>
* </ul>
*/
public static class Builder {
private final Login login;
private final List<String> issuerDomainNames;
private String issuer;
private boolean wildcard = false;
private boolean quotes = true;
private @Nullable Instant persistUntil = null;
private Builder(Login login, List<String> issuerDomainNames) {
this.login = login;
this.issuerDomainNames = issuerDomainNames;
this.issuer = issuerDomainNames.get(0);
}
/**
* Change the issuer domain name.
*
* @param issuer
* Issuer domain name, must be one of
* {@link DnsPersist01Challenge#getIssuerDomainNames()}.
*/
public Builder issuerDomainName(String issuer) {
requireNonNull(issuer, "issuer");
if (!issuerDomainNames.contains(issuer)) {
throw new IllegalArgumentException("Domain " + issuer + " is not in the list of issuer-domain-names");
}
this.issuer = issuer;
return this;
}
/**
* Request wildcard validation.
*/
public Builder wildcard() {
wildcard = true;
return this;
}
/**
* Instant until this RDATA is valid. The CA must not use this record after that.
*
* @param instant
* Persist until instant
*/
public Builder persistUntil(Instant instant) {
persistUntil = requireNonNull(instant, "instant");
return this;
}
/**
* Do not use quote-enclosed strings. Proper formatting of the resulting RDATA
* must be done externally!
*/
public Builder noQuotes() {
quotes = false;
return this;
}
/**
* Build the RDATA string for the DNS TXT record.
*/
public String build() {
var parts = new ArrayList<String>();
parts.add(issuer);
parts.add("accounturi=" + login.getAccount().getLocation());
if (wildcard) {
parts.add("policy=wildcard");
}
if (persistUntil != null) {
parts.add("persistUntil=" + persistUntil.getEpochSecond());
}
if (quotes) {
// Quotes inside the parts should be escaped. However, we don't expect
// that any part contains qoutes.
return '"' + String.join(";\" \" ", parts) + '"';
} else {
return String.join("; ", parts);
}
}
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Http01Challenge.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2015 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j.challenge;
import java.io.Serial;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.toolbox.JSON;
/**
* Implements the {@value TYPE} challenge. For domain validation, it requires a specific
* file that can be retrieved from the domain via HTTP. See the acme4j documentation for a
* detailed explanation.
*/
public class Http01Challenge extends TokenChallenge {
@Serial
private static final long serialVersionUID = 3322211185872544605L;
/**
* Challenge type name: {@value}
*/
public static final String TYPE = "http-01";
/**
* Creates a new generic {@link Http01Challenge} object.
*
* @param login
* {@link Login} the resource is bound with
* @param data
* {@link JSON} challenge data
*/
public Http01Challenge(Login login, JSON data) {
super(login, data);
}
/**
* Returns the token to be used for this challenge.
*/
@Override
public String getToken() {
return super.getToken();
}
@Override
protected boolean acceptable(String type) {
return TYPE.equals(type);
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsAlpn01Challenge.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2018 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j.challenge;
import static org.shredzone.acme4j.toolbox.AcmeUtils.sha256hash;
import java.io.IOException;
import java.io.Serial;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
import org.shredzone.acme4j.Identifier;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.util.CertificateUtils;
/**
* Implements the {@value TYPE} challenge. It requires a specific certificate that can be
* retrieved from the domain via HTTPS request. See the acme4j documentation for a
* detailed explanation.
*
* @since 2.1
*/
public class TlsAlpn01Challenge extends TokenChallenge {
@Serial
private static final long serialVersionUID = -5590351078176091228L;
/**
* Challenge type name: {@value}
*/
public static final String TYPE = "tls-alpn-01";
/**
* OID of the {@code acmeValidation} extension.
*/
public static final String ACME_VALIDATION_OID = "1.3.6.1.5.5.7.1.31";
/**
* {@code acme-tls/1} protocol.
*/
public static final String ACME_TLS_1_PROTOCOL = "acme-tls/1";
/**
* Creates a new generic {@link TlsAlpn01Challenge} object.
*
* @param login
* {@link Login} the resource is bound with
* @param data
* {@link JSON} challenge data
*/
public TlsAlpn01Challenge(Login login, JSON data) {
super(login, data);
}
/**
* Returns the value that is to be used as {@code acmeValidation} extension in
* the test certificate.
*/
public byte[] getAcmeValidation() {
return sha256hash(getAuthorization());
}
/**
* Creates a self-signed {@link X509Certificate} for this challenge. The certificate
* is valid for 7 days.
*
* @param keypair
* A domain {@link KeyPair} to be used for the challenge
* @param id
* The {@link Identifier} that is to be validated
* @return Created certificate
* @since 3.0.0
*/
public X509Certificate createCertificate(KeyPair keypair, Identifier id) {
try {
return CertificateUtils.createTlsAlpn01Certificate(keypair, id, getAcmeValidation());
} catch (IOException ex) {
throw new IllegalArgumentException("Bad certificate parameters", ex);
}
}
@Override
protected boolean acceptable(String type) {
return TYPE.equals(type);
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TokenChallenge.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2015 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j.challenge;
import static org.shredzone.acme4j.toolbox.AcmeUtils.base64UrlEncode;
import java.io.Serial;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.AcmeUtils;
import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.toolbox.JoseUtils;
/**
* A generic extension of {@link Challenge} that handles challenges with a {@code token}
* and {@code keyAuthorization}.
*/
public class TokenChallenge extends Challenge {
@Serial
private static final long serialVersionUID = 1634133407432681800L;
protected static final String KEY_TOKEN = "token";
/**
* Creates a new generic {@link TokenChallenge} object.
*
* @param login
* {@link Login} the resource is bound with
* @param data
* {@link JSON} challenge data
*/
public TokenChallenge(Login login, JSON data) {
super(login, data);
}
/**
* Gets the token.
*/
protected String getToken() {
var token = getJSON().get(KEY_TOKEN).asString();
if (!AcmeUtils.isValidBase64Url(token)) {
throw new AcmeProtocolException("Invalid token: " + token);
}
return token;
}
/**
* Computes the key authorization for the given token.
* <p>
* The default is {@code token + '.' + base64url(jwkThumbprint)}. Subclasses may
* override this method if a different algorithm is used.
*
* @param token
* Token to be used
* @return Key Authorization string for that token
* @since 2.12
*/
protected String keyAuthorizationFor(String token) {
var pk = getLogin().getPublicKey();
return token + '.' + base64UrlEncode(JoseUtils.thumbprint(pk));
}
/**
* Returns the authorization string.
* <p>
* The default uses {@link #keyAuthorizationFor(String)} to compute the key
* authorization of {@link #getToken()}. Subclasses may override this method if a
* different algorithm is used.
*/
public String getAuthorization() {
return keyAuthorizationFor(getToken());
}
}
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/challenge/package-info.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2020 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
/**
* This package contains all standard challenges, as well as base classes for challenges
* that are proprietary to a CA.
*/
@ReturnValuesAreNonnullByDefault
@DefaultAnnotationForParameters(NonNull.class)
@DefaultAnnotationForFields(NonNull.class)
package org.shredzone.acme4j.challenge;
import edu.umd.cs.findbugs.annotations.DefaultAnnotationForFields;
import edu.umd.cs.findbugs.annotations.DefaultAnnotationForParameters;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.ReturnValuesAreNonnullByDefault;
================================================
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/connector/Connection.java
================================================
/*
* acme4j - Java ACME client
*
* Copyright (C) 2015 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j.connector;
import java.net.URL;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.time.Instant;
import
gitextract_nzsxpl3o/
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── .gitlab-ci.yml
├── CONTRIBUTING.md
├── LICENSE-APL.txt
├── README.md
├── acme4j-client/
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ ├── .gitignore
│ │ │ ├── module-info.java
│ │ │ └── org/
│ │ │ └── shredzone/
│ │ │ └── acme4j/
│ │ │ ├── Account.java
│ │ │ ├── AccountBuilder.java
│ │ │ ├── AcmeJsonResource.java
│ │ │ ├── AcmeResource.java
│ │ │ ├── Authorization.java
│ │ │ ├── Certificate.java
│ │ │ ├── Identifier.java
│ │ │ ├── Login.java
│ │ │ ├── Metadata.java
│ │ │ ├── Order.java
│ │ │ ├── OrderBuilder.java
│ │ │ ├── PollableResource.java
│ │ │ ├── Problem.java
│ │ │ ├── RenewalInfo.java
│ │ │ ├── RevocationReason.java
│ │ │ ├── Session.java
│ │ │ ├── Status.java
│ │ │ ├── challenge/
│ │ │ │ ├── Challenge.java
│ │ │ │ ├── Dns01Challenge.java
│ │ │ │ ├── DnsAccount01Challenge.java
│ │ │ │ ├── DnsPersist01Challenge.java
│ │ │ │ ├── Http01Challenge.java
│ │ │ │ ├── TlsAlpn01Challenge.java
│ │ │ │ ├── TokenChallenge.java
│ │ │ │ └── package-info.java
│ │ │ ├── connector/
│ │ │ │ ├── Connection.java
│ │ │ │ ├── DefaultConnection.java
│ │ │ │ ├── HttpConnector.java
│ │ │ │ ├── NetworkSettings.java
│ │ │ │ ├── NonceHolder.java
│ │ │ │ ├── RequestSigner.java
│ │ │ │ ├── Resource.java
│ │ │ │ ├── ResourceIterator.java
│ │ │ │ ├── TrimmingInputStream.java
│ │ │ │ └── package-info.java
│ │ │ ├── exception/
│ │ │ │ ├── AcmeException.java
│ │ │ │ ├── AcmeLazyLoadingException.java
│ │ │ │ ├── AcmeNetworkException.java
│ │ │ │ ├── AcmeNotSupportedException.java
│ │ │ │ ├── AcmeProtocolException.java
│ │ │ │ ├── AcmeRateLimitedException.java
│ │ │ │ ├── AcmeServerException.java
│ │ │ │ ├── AcmeUnauthorizedException.java
│ │ │ │ ├── AcmeUserActionRequiredException.java
│ │ │ │ └── package-info.java
│ │ │ ├── package-info.java
│ │ │ ├── provider/
│ │ │ │ ├── AbstractAcmeProvider.java
│ │ │ │ ├── AcmeProvider.java
│ │ │ │ ├── ChallengeProvider.java
│ │ │ │ ├── ChallengeType.java
│ │ │ │ ├── GenericAcmeProvider.java
│ │ │ │ ├── actalis/
│ │ │ │ │ ├── ActalisAcmeProvider.java
│ │ │ │ │ └── package-info.java
│ │ │ │ ├── google/
│ │ │ │ │ ├── GoogleAcmeProvider.java
│ │ │ │ │ └── package-info.java
│ │ │ │ ├── letsencrypt/
│ │ │ │ │ ├── LetsEncryptAcmeProvider.java
│ │ │ │ │ └── package-info.java
│ │ │ │ ├── package-info.java
│ │ │ │ ├── pebble/
│ │ │ │ │ ├── PebbleAcmeProvider.java
│ │ │ │ │ └── package-info.java
│ │ │ │ ├── sslcom/
│ │ │ │ │ ├── SslComAcmeProvider.java
│ │ │ │ │ └── package-info.java
│ │ │ │ └── zerossl/
│ │ │ │ ├── ZeroSSLAcmeProvider.java
│ │ │ │ └── package-info.java
│ │ │ ├── toolbox/
│ │ │ │ ├── AcmeUtils.java
│ │ │ │ ├── JSON.java
│ │ │ │ ├── JSONBuilder.java
│ │ │ │ ├── JoseUtils.java
│ │ │ │ └── package-info.java
│ │ │ └── util/
│ │ │ ├── CSRBuilder.java
│ │ │ ├── CertificateUtils.java
│ │ │ ├── KeyPairUtils.java
│ │ │ └── package-info.java
│ │ ├── resources/
│ │ │ ├── .gitignore
│ │ │ ├── META-INF/
│ │ │ │ └── services/
│ │ │ │ └── org.shredzone.acme4j.provider.AcmeProvider
│ │ │ └── org/
│ │ │ └── shredzone/
│ │ │ └── acme4j/
│ │ │ └── provider/
│ │ │ └── pebble/
│ │ │ └── pebble.minica.pem
│ │ └── resources-filtered/
│ │ └── org/
│ │ └── shredzone/
│ │ └── acme4j/
│ │ └── version.properties
│ └── test/
│ ├── java/
│ │ ├── .gitignore
│ │ └── org/
│ │ └── shredzone/
│ │ └── acme4j/
│ │ ├── AccountBuilderTest.java
│ │ ├── AccountTest.java
│ │ ├── AcmeJsonResourceTest.java
│ │ ├── AcmeResourceTest.java
│ │ ├── AuthorizationTest.java
│ │ ├── CertificateTest.java
│ │ ├── IdentifierTest.java
│ │ ├── LoginTest.java
│ │ ├── OrderBuilderTest.java
│ │ ├── OrderTest.java
│ │ ├── ProblemTest.java
│ │ ├── RenewalInfoTest.java
│ │ ├── SessionTest.java
│ │ ├── StatusTest.java
│ │ ├── challenge/
│ │ │ ├── ChallengeTest.java
│ │ │ ├── Dns01ChallengeTest.java
│ │ │ ├── DnsAccount01ChallengeTest.java
│ │ │ ├── DnsPersist01ChallengeTest.java
│ │ │ ├── Http01ChallengeTest.java
│ │ │ ├── TlsAlpn01ChallengeTest.java
│ │ │ └── TokenChallengeTest.java
│ │ ├── connector/
│ │ │ ├── DefaultConnectionTest.java
│ │ │ ├── DummyConnection.java
│ │ │ ├── HttpConnectorTest.java
│ │ │ ├── NetworkSettingsTest.java
│ │ │ ├── ResourceIteratorTest.java
│ │ │ ├── ResourceTest.java
│ │ │ ├── SessionProviderTest.java
│ │ │ └── TrimmingInputStreamTest.java
│ │ ├── exception/
│ │ │ ├── AcmeExceptionTest.java
│ │ │ ├── AcmeLazyLoadingExceptionTest.java
│ │ │ ├── AcmeNetworkExceptionTest.java
│ │ │ ├── AcmeNotSupportedExceptionTest.java
│ │ │ ├── AcmeProtocolExceptionTest.java
│ │ │ ├── AcmeRateLimitedExceptionTest.java
│ │ │ └── AcmeUserActionRequiredExceptionTest.java
│ │ ├── provider/
│ │ │ ├── AbstractAcmeProviderTest.java
│ │ │ ├── GenericAcmeProviderTest.java
│ │ │ ├── TestableConnectionProvider.java
│ │ │ ├── actalis/
│ │ │ │ └── ActalisAcmeProviderTest.java
│ │ │ ├── google/
│ │ │ │ └── GoogleAcmeProviderTest.java
│ │ │ ├── letsencrypt/
│ │ │ │ └── LetsEncryptAcmeProviderTest.java
│ │ │ ├── pebble/
│ │ │ │ └── PebbleAcmeProviderTest.java
│ │ │ ├── sslcom/
│ │ │ │ └── SslComAcmeProviderTest.java
│ │ │ └── zerossl/
│ │ │ └── ZeroSSLAcmeProviderTest.java
│ │ ├── toolbox/
│ │ │ ├── AcmeUtilsTest.java
│ │ │ ├── JSONBuilderTest.java
│ │ │ ├── JSONTest.java
│ │ │ ├── JoseUtilsTest.java
│ │ │ └── TestUtils.java
│ │ └── util/
│ │ ├── CSRBuilderTest.java
│ │ ├── CertificateUtilsTest.java
│ │ └── KeyPairUtilsTest.java
│ └── resources/
│ ├── .gitignore
│ ├── META-INF/
│ │ └── services/
│ │ └── org.shredzone.acme4j.provider.AcmeProvider
│ ├── ari-example-cert.pem
│ ├── cert.pem
│ ├── certid-cert.pem
│ ├── csr.der
│ ├── domain-private.key
│ ├── domain-public.key
│ ├── json/
│ │ ├── authorizationChallenges.json
│ │ ├── canceledOrderResponse.json
│ │ ├── datatypes.json
│ │ ├── deactivateAccountResponse.json
│ │ ├── directory.json
│ │ ├── directoryNoMeta.json
│ │ ├── dns01Challenge.json
│ │ ├── dnsAccount01Challenge.json
│ │ ├── dnsPersist01Challenge.json
│ │ ├── finalizeAutoRenewResponse.json
│ │ ├── finalizeRequest.json
│ │ ├── finalizeResponse.json
│ │ ├── genericChallenge.json
│ │ ├── httpChallenge.json
│ │ ├── httpNoTokenChallenge.json
│ │ ├── modifyAccount.json
│ │ ├── modifyAccountResponse.json
│ │ ├── newAccount.json
│ │ ├── newAccountOnlyExisting.json
│ │ ├── newAccountResponse.json
│ │ ├── newAuthorizationRequest.json
│ │ ├── newAuthorizationRequestSub.json
│ │ ├── newAuthorizationResponse.json
│ │ ├── newAuthorizationResponseSub.json
│ │ ├── problem.json
│ │ ├── renewalInfo.json
│ │ ├── replacedCertificateRequest.json
│ │ ├── requestAutoRenewOrderRequest.json
│ │ ├── requestAutoRenewOrderResponse.json
│ │ ├── requestCertificateRequest.json
│ │ ├── requestCertificateRequestWithDate.json
│ │ ├── requestOrderRequest.json
│ │ ├── requestOrderRequestSub.json
│ │ ├── requestOrderResponse.json
│ │ ├── requestOrderResponseSub.json
│ │ ├── requestProfileOrderRequest.json
│ │ ├── requestProfileOrderResponse.json
│ │ ├── requestReplacesRequest.json
│ │ ├── requestReplacesResponse.json
│ │ ├── revokeCertificateRequest.json
│ │ ├── revokeCertificateWithReasonRequest.json
│ │ ├── tlsAlpnChallenge.json
│ │ ├── triggerHttpChallenge.json
│ │ ├── triggerHttpChallengeRequest.json
│ │ ├── triggerHttpChallengeResponse.json
│ │ ├── updateAccount.json
│ │ ├── updateAccountResponse.json
│ │ ├── updateAuthorizationResponse.json
│ │ ├── updateAuthorizationWildcardResponse.json
│ │ ├── updateAutoRenewOrderResponse.json
│ │ ├── updateHttpChallengeResponse.json
│ │ └── updateOrderResponse.json
│ ├── private.key
│ ├── public.key
│ └── simplelogger.properties
├── acme4j-example/
│ ├── .gitignore
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ ├── .gitignore
│ │ │ ├── module-info.java
│ │ │ └── org/
│ │ │ └── shredzone/
│ │ │ └── acme4j/
│ │ │ └── example/
│ │ │ └── ClientTest.java
│ │ └── resources/
│ │ ├── .gitignore
│ │ └── simplelogger.properties
│ └── test/
│ ├── java/
│ │ └── .gitignore
│ └── resources/
│ └── .gitignore
├── acme4j-it/
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ ├── docker/
│ │ │ ├── challtestsrv.dockerfile
│ │ │ └── challtestsrv.sh
│ │ ├── java/
│ │ │ ├── module-info.java
│ │ │ └── org/
│ │ │ └── shredzone/
│ │ │ └── acme4j/
│ │ │ └── it/
│ │ │ ├── BammBammClient.java
│ │ │ └── package-info.java
│ │ └── resources/
│ │ └── .gitignore
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── shredzone/
│ │ └── acme4j/
│ │ └── it/
│ │ ├── ProviderIT.java
│ │ ├── SoftFail.java
│ │ ├── SoftFailExtension.java
│ │ ├── boulder/
│ │ │ └── OrderHttpIT.java
│ │ └── pebble/
│ │ ├── AccountIT.java
│ │ ├── OrderIT.java
│ │ ├── OrderWildcardIT.java
│ │ ├── PebbleITBase.java
│ │ └── SessionIT.java
│ └── resources/
│ └── simplelogger.properties
├── acme4j-smime/
│ ├── .gitattributes
│ ├── pom.xml
│ ├── src/
│ │ ├── main/
│ │ │ ├── java/
│ │ │ │ ├── .gitignore
│ │ │ │ ├── module-info.java
│ │ │ │ └── org/
│ │ │ │ └── shredzone/
│ │ │ │ └── acme4j/
│ │ │ │ └── smime/
│ │ │ │ ├── EmailIdentifier.java
│ │ │ │ ├── challenge/
│ │ │ │ │ ├── EmailReply00Challenge.java
│ │ │ │ │ ├── EmailReply00ChallengeProvider.java
│ │ │ │ │ └── package-info.java
│ │ │ │ ├── csr/
│ │ │ │ │ ├── KeyUsageType.java
│ │ │ │ │ ├── SMIMECSRBuilder.java
│ │ │ │ │ └── package-info.java
│ │ │ │ ├── email/
│ │ │ │ │ ├── EmailProcessor.java
│ │ │ │ │ ├── ResponseBodyGenerator.java
│ │ │ │ │ ├── ResponseGenerator.java
│ │ │ │ │ └── package-info.java
│ │ │ │ ├── exception/
│ │ │ │ │ ├── AcmeInvalidMessageException.java
│ │ │ │ │ └── package-info.java
│ │ │ │ ├── package-info.java
│ │ │ │ └── wrapper/
│ │ │ │ ├── Mail.java
│ │ │ │ ├── SignedMail.java
│ │ │ │ ├── SignedMailBuilder.java
│ │ │ │ ├── SimpleMail.java
│ │ │ │ └── package-info.java
│ │ │ └── resources/
│ │ │ ├── .gitignore
│ │ │ └── META-INF/
│ │ │ └── services/
│ │ │ └── org.shredzone.acme4j.provider.ChallengeProvider
│ │ └── test/
│ │ ├── java/
│ │ │ ├── .gitignore
│ │ │ └── org/
│ │ │ └── shredzone/
│ │ │ └── acme4j/
│ │ │ └── smime/
│ │ │ ├── EmailIdentifierTest.java
│ │ │ ├── SMIMETests.java
│ │ │ ├── challenge/
│ │ │ │ └── EmailReply00ChallengeTest.java
│ │ │ ├── csr/
│ │ │ │ └── SMIMECSRBuilderTest.java
│ │ │ ├── email/
│ │ │ │ └── EmailProcessorTest.java
│ │ │ └── wrapper/
│ │ │ ├── SignedMailBuilderTest.java
│ │ │ └── SignedMailTest.java
│ │ └── resources/
│ │ ├── .gitignore
│ │ ├── 7508-fake-ca.jks
│ │ ├── 7508-valid-ca.jks
│ │ ├── email/
│ │ │ ├── challenge.eml
│ │ │ ├── invalid-cert-mismatch.eml
│ │ │ ├── invalid-nosan.eml
│ │ │ ├── invalid-protected-mail-from.eml
│ │ │ ├── invalid-protected-mail-subject.eml
│ │ │ ├── invalid-protected-mail-to.eml
│ │ │ ├── invalid-signed-mail.eml
│ │ │ ├── valid-mail-7508.eml
│ │ │ └── valid-mail.eml
│ │ ├── invalid-signer-privkey.pem
│ │ ├── invalid-signer.pem
│ │ ├── json/
│ │ │ ├── emailReplyChallenge.json
│ │ │ └── emailReplyChallengeMismatch.json
│ │ ├── key.pem
│ │ ├── valid-signer-nosan-privkey.pem
│ │ ├── valid-signer-nosan.pem
│ │ ├── valid-signer-privkey.pem
│ │ └── valid-signer.pem
│ └── tool/
│ ├── smime-generator.py
│ └── test-key-generator.sh
├── pom.xml
└── src/
└── doc/
├── docs/
│ ├── ca/
│ │ ├── actalis.md
│ │ ├── google.md
│ │ ├── index.md
│ │ ├── letsencrypt.md
│ │ ├── pebble.md
│ │ ├── sslcom.md
│ │ └── zerossl.md
│ ├── challenge/
│ │ ├── dns-01.md
│ │ ├── dns-account-01.md
│ │ ├── dns-persist-01.md
│ │ ├── email-reply-00.md
│ │ ├── http-01.md
│ │ ├── index.md
│ │ └── tls-alpn-01.md
│ ├── development/
│ │ ├── challenge.md
│ │ ├── index.md
│ │ ├── provider.md
│ │ └── testing.md
│ ├── example.md
│ ├── faq.md
│ ├── index.md
│ ├── migration.md
│ └── usage/
│ ├── account.md
│ ├── advanced.md
│ ├── connecting.md
│ ├── debugging.md
│ ├── errors.md
│ ├── exceptions.md
│ ├── index.md
│ ├── order.md
│ ├── persistence.md
│ ├── renewal.md
│ └── revocation.md
├── mkdocs.yml
└── theme/
├── breadcrumbs.html
├── css/
│ ├── font.css
│ ├── theme_custom.css
│ └── theme_pygments.css
├── footer.html
└── main.html
SYMBOL INDEX (1307 symbols across 143 files)
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/Account.java
class Account (line 44) | public class Account extends AcmeJsonResource {
method Account (line 55) | protected Account(Login login, URL location) {
method getTermsOfServiceAgreed (line 65) | public Optional<Boolean> getTermsOfServiceAgreed() {
method getContacts (line 75) | public List<URI> getContacts() {
method getStatus (line 89) | public Status getStatus() {
method hasExternalAccountBinding (line 98) | public boolean hasExternalAccountBinding() {
method getKeyIdentifier (line 108) | public Optional<String> getKeyIdentifier() {
method getOrders (line 126) | public Iterator<Order> getOrders() {
method newOrder (line 141) | public OrderBuilder newOrder() {
method preAuthorizeDomain (line 163) | public Authorization preAuthorizeDomain(String domain) throws AcmeExce...
method preAuthorize (line 189) | public Authorization preAuthorize(Identifier identifier) throws AcmeEx...
method changeKey (line 221) | public void changeKey(KeyPair newKeyPair) throws AcmeException {
method deactivate (line 252) | public void deactivate() throws AcmeException {
method modify (line 268) | public EditableAccount modify() {
class EditableAccount (line 275) | public class EditableAccount {
method EditableAccount (line 278) | private EditableAccount() {
method getContacts (line 290) | @SuppressFBWarnings("EI_EXPOSE_REP") // behavior is intended
method addContact (line 302) | public EditableAccount addContact(URI contact) {
method addContact (line 317) | public EditableAccount addContact(String contact) {
method addEmail (line 332) | public EditableAccount addEmail(String email) {
method commit (line 340) | public void commit() throws AcmeException {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/AccountBuilder.java
class AccountBuilder (line 60) | public class AccountBuilder {
method addContact (line 82) | public AccountBuilder addContact(URI contact) {
method addContact (line 99) | public AccountBuilder addContact(String contact) {
method addEmail (line 116) | public AccountBuilder addEmail(String email) {
method agreeToTermsOfService (line 135) | public AccountBuilder agreeToTermsOfService() {
method onlyExisting (line 152) | public AccountBuilder onlyExisting() {
method useKeyPair (line 170) | public AccountBuilder useKeyPair(KeyPair keyPair) {
method withKeyIdentifier (line 188) | public AccountBuilder withKeyIdentifier(String kid, SecretKey macKey) {
method withKeyIdentifier (line 214) | public AccountBuilder withKeyIdentifier(String kid, String encodedMacK...
method withMacAlgorithm (line 229) | public AccountBuilder withMacAlgorithm(String macAlgorithm) {
method create (line 249) | public Account create(Session session) throws AcmeException {
method createLogin (line 263) | public Login createLogin(Session session) throws AcmeException {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/AcmeJsonResource.java
class AcmeJsonResource (line 34) | public abstract class AcmeJsonResource extends AcmeResource {
method AcmeJsonResource (line 50) | protected AcmeJsonResource(Login login, URL location) {
method getJSON (line 66) | public JSON getJSON() {
method setJSON (line 83) | protected void setJSON(JSON data) {
method isValid (line 95) | protected boolean isValid() {
method invalidate (line 106) | protected void invalidate() {
method fetch (line 121) | public Optional<Instant> fetch() throws AcmeException {
method setRetryAfter (line 139) | protected void setRetryAfter(@Nullable Instant retryAfter) {
method getRetryAfter (line 153) | public Optional<Instant> getRetryAfter() {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/AcmeResource.java
class AcmeResource (line 31) | public abstract class AcmeResource implements Serializable {
method AcmeResource (line 46) | protected AcmeResource(Login login, URL location) {
method getLogin (line 54) | protected Login getLogin() {
method getSession (line 64) | protected Session getSession() {
method rebind (line 78) | public void rebind(Login login) {
method getLocation (line 88) | public URL getLocation() {
method finalize (line 92) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/Authorization.java
class Authorization (line 38) | public class Authorization extends AcmeJsonResource implements PollableR...
method Authorization (line 43) | protected Authorization(Login login, URL location) {
method getIdentifier (line 56) | public Identifier getIdentifier() {
method getStatus (line 67) | @Override
method getExpires (line 75) | public Optional<Instant> getExpires() {
method isWildcard (line 85) | public boolean isWildcard() {
method isSubdomainAuthAllowed (line 97) | public boolean isSubdomainAuthAllowed() {
method getChallenges (line 106) | public List<Challenge> getChallenges() {
method findChallenge (line 131) | @SuppressWarnings("unchecked")
method findChallenge (line 150) | public <T extends Challenge> Optional<T> findChallenge(Class<T> type) {
method waitForCompletion (line 172) | public Status waitForCompletion(Duration timeout)
method deactivate (line 180) | public void deactivate() throws AcmeException {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/Certificate.java
class Certificate (line 55) | public class Certificate extends AcmeResource {
method Certificate (line 65) | protected Certificate(Login login, URL certUrl) {
method download (line 79) | public void download() throws AcmeException {
method getCertificate (line 95) | public X509Certificate getCertificate() {
method getCertificateChain (line 107) | public List<X509Certificate> getCertificateChain() {
method getAlternates (line 117) | public List<URL> getAlternates() {
method getAlternateCertificates (line 128) | public List<Certificate> getAlternateCertificates() {
method isIssuedBy (line 147) | public boolean isIssuedBy(String issuer) {
method findCertificate (line 166) | public Optional<Certificate> findCertificate(String issuer) {
method writeCertificate (line 182) | public void writeCertificate(Writer out) throws IOException {
method getRenewalInfoLocation (line 198) | public Optional<URL> getRenewalInfoLocation() {
method hasRenewalInfo (line 223) | public boolean hasRenewalInfo() {
method getRenewalInfo (line 234) | @SuppressFBWarnings("EI_EXPOSE_REP") // behavior is intended
method revoke (line 247) | public void revoke() throws AcmeException {
method revoke (line 261) | public void revoke(@Nullable RevocationReason reason) throws AcmeExcep...
method revoke (line 282) | public static void revoke(Login login, X509Certificate cert, @Nullable...
method revoke (line 321) | public static void revoke(Session session, KeyPair domainKeyPair, X509...
method lazyDownload (line 346) | private void lazyDownload() {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/Identifier.java
class Identifier (line 40) | public class Identifier implements Serializable {
method Identifier (line 77) | public Identifier(String type, String value) {
method Identifier (line 88) | public Identifier(JSON json) {
method Identifier (line 101) | private Identifier(Identifier identifier) {
method dns (line 112) | public static Identifier dns(String domain) {
method ip (line 123) | public static Identifier ip(InetAddress ip) {
method ip (line 135) | public static Identifier ip(String ip) {
method withAncestorDomain (line 152) | public Identifier withAncestorDomain(String domain) {
method allowSubdomainAuth (line 167) | public Identifier allowSubdomainAuth() {
method getType (line 178) | public String getType() {
method getValue (line 185) | public String getValue() {
method getDomain (line 196) | public String getDomain() {
method getIP (line 208) | public InetAddress getIP() {
method toMap (line 220) | public Map<String, Object> toMap() {
method expectType (line 232) | private void expectType(String type) {
method toString (line 238) | @Override
method equals (line 246) | @Override
method hashCode (line 254) | @Override
method finalize (line 259) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/Login.java
class Login (line 55) | public class Login {
method Login (line 71) | public Login(URL accountLocation, KeyPair keyPair, Session session) {
method getSession (line 80) | @SuppressFBWarnings("EI_EXPOSE_REP") // behavior is intended
method getPublicKey (line 90) | public PublicKey getPublicKey() {
method getAccount (line 99) | @SuppressFBWarnings("EI_EXPOSE_REP") // behavior is intended
method bindAuthorization (line 112) | public Authorization bindAuthorization(URL location) {
method bindCertificate (line 124) | public Certificate bindCertificate(URL location) {
method bindOrder (line 135) | public Order bindOrder(URL location) {
method bindRenewalInfo (line 148) | public RenewalInfo bindRenewalInfo(URL location) {
method bindRenewalInfo (line 161) | public RenewalInfo bindRenewalInfo(X509Certificate certificate) throws...
method bindChallenge (line 184) | public Challenge bindChallenge(URL location) {
method bindChallenge (line 207) | public <C extends Challenge> C bindChallenge(URL location, Class<C> ty...
method createChallenge (line 223) | public Challenge createChallenge(JSON data) {
method newOrder (line 237) | public OrderBuilder newOrder() {
method setKeyPair (line 245) | protected void setKeyPair(KeyPair keyPair) {
method createJoseRequest (line 263) | public JSONBuilder createJoseRequest(URL url, @Nullable JSONBuilder pa...
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/Metadata.java
class Metadata (line 32) | public class Metadata {
method Metadata (line 42) | public Metadata(JSON meta) {
method getTermsOfService (line 49) | public Optional<URI> getTermsOfService() {
method getWebsite (line 57) | public Optional<URL> getWebsite() {
method getCaaIdentities (line 65) | public Collection<String> getCaaIdentities() {
method isExternalAccountRequired (line 76) | public boolean isExternalAccountRequired() {
method isAutoRenewalEnabled (line 85) | public boolean isAutoRenewalEnabled() {
method getAutoRenewalMinLifetime (line 96) | public Duration getAutoRenewalMinLifetime() {
method getAutoRenewalMaxDuration (line 111) | public Duration getAutoRenewalMaxDuration() {
method isAutoRenewalGetAllowed (line 125) | public boolean isAutoRenewalGetAllowed() {
method isProfileAllowed (line 143) | public boolean isProfileAllowed() {
method isProfileAllowed (line 157) | public boolean isProfileAllowed(String profile) {
method getProfiles (line 170) | public Set<String> getProfiles() {
method getProfileDescription (line 189) | public Optional<String> getProfileDescription(String profile) {
method isSubdomainAuthAllowed (line 203) | public boolean isSubdomainAuthAllowed() {
method getJSON (line 211) | public JSON getJSON() {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/Order.java
class Order (line 45) | public class Order extends AcmeJsonResource implements PollableResource {
method Order (line 53) | protected Order(Login login, URL location) {
method getStatus (line 64) | @Override
method getError (line 72) | public Optional<Problem> getError() {
method getExpires (line 79) | public Optional<Instant> getExpires() {
method getIdentifiers (line 88) | public List<Identifier> getIdentifiers() {
method getNotBefore (line 99) | public Optional<Instant> getNotBefore() {
method getNotAfter (line 106) | public Optional<Instant> getNotAfter() {
method getAuthorizations (line 114) | public List<Authorization> getAuthorizations() {
method getFinalizeLocation (line 132) | public URL getFinalizeLocation() {
method getCertificate (line 143) | @SuppressFBWarnings("EI_EXPOSE_REP") // behavior is intended
method isAutoRenewalCertificate (line 162) | public boolean isAutoRenewalCertificate() {
method execute (line 186) | public void execute(KeyPair domainKeyPair) throws AcmeException {
method execute (line 210) | public void execute(KeyPair domainKeyPair, Consumer<CSRBuilder> builde...
method execute (line 239) | public void execute(PKCS10CertificationRequest csr) throws AcmeExcepti...
method execute (line 261) | public void execute(byte[] csr) throws AcmeException {
method waitUntilReady (line 286) | public Status waitUntilReady(Duration timeout)
method waitForCompletion (line 304) | public Status waitForCompletion(Duration timeout)
method isAutoRenewing (line 314) | public boolean isAutoRenewing() {
method getAutoRenewalStartDate (line 326) | public Optional<Instant> getAutoRenewalStartDate() {
method getAutoRenewalEndDate (line 341) | public Instant getAutoRenewalEndDate() {
method getAutoRenewalLifetime (line 355) | public Duration getAutoRenewalLifetime() {
method getAutoRenewalLifetimeAdjust (line 370) | public Optional<Duration> getAutoRenewalLifetimeAdjust() {
method isAutoRenewalGetEnabled (line 386) | public boolean isAutoRenewalGetEnabled() {
method cancelAutoRenewal (line 402) | public void cancelAutoRenewal() throws AcmeException {
method getProfile (line 423) | public String getProfile() {
method invalidate (line 427) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/OrderBuilder.java
class OrderBuilder (line 42) | public class OrderBuilder {
method OrderBuilder (line 65) | protected OrderBuilder(Login login) {
method domain (line 77) | public OrderBuilder domain(String domain) {
method domains (line 90) | public OrderBuilder domains(String... domains) {
method domains (line 106) | public OrderBuilder domains(Collection<String> domains) {
method identifier (line 119) | public OrderBuilder identifier(Identifier identifier) {
method identifiers (line 132) | public OrderBuilder identifiers(Collection<Identifier> identifiers) {
method notBefore (line 143) | public OrderBuilder notBefore(Instant notBefore) {
method notAfter (line 157) | public OrderBuilder notAfter(Instant notAfter) {
method autoRenewal (line 174) | public OrderBuilder autoRenewal() {
method autoRenewalStart (line 193) | public OrderBuilder autoRenewalStart(Instant start) {
method autoRenewalEnd (line 211) | public OrderBuilder autoRenewalEnd(Instant end) {
method autoRenewalLifetime (line 229) | public OrderBuilder autoRenewalLifetime(Duration duration) {
method autoRenewalLifetimeAdjust (line 246) | public OrderBuilder autoRenewalLifetimeAdjust(Duration duration) {
method autoRenewalEnableGet (line 267) | public OrderBuilder autoRenewalEnableGet() {
method profile (line 287) | public OrderBuilder profile(String profile) {
method replaces (line 304) | public OrderBuilder replaces(String uniqueId) {
method replaces (line 321) | public OrderBuilder replaces(X509Certificate certificate) {
method replaces (line 337) | public OrderBuilder replaces(Certificate certificate) {
method create (line 346) | public Order create() throws AcmeException {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/PollableResource.java
type PollableResource (line 35) | public interface PollableResource {
method getStatus (line 45) | Status getStatus();
method fetch (line 52) | Optional<Instant> fetch() throws AcmeException;
method waitForStatus (line 69) | default Status waitForStatus(Set<Status> statusSet, Duration timeout)
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java
class Problem (line 34) | public class Problem implements Serializable {
method Problem (line 49) | public Problem(JSON problem, URL baseUrl) {
method getType (line 57) | public URI getType() {
method getTitle (line 76) | public Optional<String> getTitle() {
method getDetail (line 86) | public Optional<String> getDetail() {
method getInstance (line 94) | public Optional<URI> getInstance() {
method getIdentifier (line 111) | public Optional<Identifier> getIdentifier() {
method getSubProblems (line 120) | public List<Problem> getSubProblems() {
method asJSON (line 133) | public JSON asJSON() {
method toString (line 146) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/RenewalInfo.java
class RenewalInfo (line 35) | public class RenewalInfo extends AcmeJsonResource {
method RenewalInfo (line 38) | protected RenewalInfo(Login login, URL location) {
method getSuggestedWindowStart (line 46) | public Instant getSuggestedWindowStart() {
method getSuggestedWindowEnd (line 54) | public Instant getSuggestedWindowEnd() {
method getExplanation (line 62) | public Optional<URL> getExplanation() {
method renewalIsNotRequired (line 75) | public boolean renewalIsNotRequired(Instant instant) {
method renewalIsRecommended (line 93) | public boolean renewalIsRecommended(Instant instant) {
method renewalIsOverdue (line 111) | public boolean renewalIsOverdue(Instant instant) {
method getRandomProposal (line 142) | public Optional<Instant> getRandomProposal(@Nullable TemporalAmount fr...
method assertValidTimeWindow (line 167) | private void assertValidTimeWindow() {
method fetch (line 173) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/RevocationReason.java
type RevocationReason (line 24) | public enum RevocationReason {
method RevocationReason (line 39) | RevocationReason(int reasonCode) {
method getReasonCode (line 46) | public int getReasonCode() {
method code (line 58) | public static RevocationReason code(int reasonCode) {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/Session.java
class Session (line 58) | public class Session {
method Session (line 86) | public Session(String serverUri) {
method Session (line 100) | public Session(URI serverUri) {
method Session (line 131) | public Session(URI serverUri, AcmeProvider provider) {
method login (line 149) | public Login login(URL accountLocation, KeyPair accountKeyPair) {
method getServerUri (line 156) | public URI getServerUri() {
method lockNonce (line 168) | public NonceHolder lockNonce() {
method getLocale (line 192) | @Nullable
method setLocale (line 203) | public void setLocale(@Nullable Locale locale) {
method getLanguageHeader (line 214) | public String getLanguageHeader() {
method networkSettings (line 224) | @SuppressFBWarnings("EI_EXPOSE_REP") // behavior is intended
method provider (line 234) | public AcmeProvider provider() {
method connect (line 243) | public Connection connect() {
method getHttpClient (line 256) | public HttpClient getHttpClient() {
method resourceUrl (line 278) | public URL resourceUrl(Resource resource) throws AcmeException {
method resourceUrlOptional (line 292) | public Optional<URL> resourceUrlOptional(Resource resource) throws Acm...
method getMetadata (line 304) | public Metadata getMetadata() throws AcmeException {
method getDirectoryLastModified (line 316) | @Nullable
method setDirectoryLastModified (line 330) | public void setDirectoryLastModified(@Nullable ZonedDateTime directory...
method getDirectoryExpires (line 342) | @Nullable
method setDirectoryExpires (line 356) | public void setDirectoryExpires(@Nullable ZonedDateTime directoryExpir...
method hasDirectory (line 367) | public boolean hasDirectory() {
method purgeDirectoryCache (line 377) | public void purgeDirectoryCache() {
method readDirectory (line 388) | private void readDirectory() throws AcmeException {
method finalize (line 414) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/Status.java
type Status (line 22) | public enum Status {
method parse (line 87) | public static Status parse(String str) {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java
class Challenge (line 44) | public class Challenge extends AcmeJsonResource implements PollableResou...
method Challenge (line 63) | public Challenge(Login login, JSON data) {
method getType (line 71) | public String getType() {
method getStatus (line 84) | @Override
method getValidated (line 92) | public Optional<Instant> getValidated() {
method getError (line 101) | public Optional<Problem> getError() {
method prepareResponse (line 114) | protected void prepareResponse(JSONBuilder response) {
method acceptable (line 127) | protected boolean acceptable(String type) {
method setJSON (line 131) | @Override
method trigger (line 167) | public void trigger() throws AcmeException {
method waitForCompletion (line 191) | public Status waitForCompletion(Duration timeout)
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Dns01Challenge.java
class Dns01Challenge (line 29) | public class Dns01Challenge extends TokenChallenge {
method Dns01Challenge (line 51) | public Dns01Challenge(Login login, JSON data) {
method getRRName (line 65) | public String getRRName(Identifier identifier) {
method getRRName (line 79) | public String getRRName(String domain) {
method getDigest (line 87) | public String getDigest() {
method acceptable (line 91) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/challenge/DnsAccount01Challenge.java
class DnsAccount01Challenge (line 35) | public class DnsAccount01Challenge extends TokenChallenge {
method DnsAccount01Challenge (line 52) | public DnsAccount01Challenge(Login login, JSON data) {
method getRRName (line 66) | public String getRRName(Identifier identifier) {
method getRRName (line 80) | public String getRRName(String domain) {
method getDigest (line 87) | public String getDigest() {
method getPrefix (line 94) | private String getPrefix(URL accountLocation) {
method acceptable (line 101) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/challenge/DnsPersist01Challenge.java
class DnsPersist01Challenge (line 41) | public class DnsPersist01Challenge extends Challenge {
method DnsPersist01Challenge (line 67) | public DnsPersist01Challenge(Login login, JSON data) {
method getIssuerDomainNames (line 75) | public List<String> getIssuerDomainNames() {
method getRRName (line 115) | public String getRRName(Identifier identifier) {
method getRRName (line 128) | public String getRRName(String domain) {
method buildRData (line 137) | public Builder buildRData() {
method getRData (line 146) | public String getRData() {
method getAccountUrl (line 155) | public URL getAccountUrl() {
method invalidate (line 159) | @Override
method acceptable (line 165) | @Override
method setJSON (line 170) | @Override
class Builder (line 200) | public static class Builder {
method Builder (line 208) | private Builder(Login login, List<String> issuerDomainNames) {
method issuerDomainName (line 221) | public Builder issuerDomainName(String issuer) {
method wildcard (line 233) | public Builder wildcard() {
method persistUntil (line 244) | public Builder persistUntil(Instant instant) {
method noQuotes (line 253) | public Builder noQuotes() {
method build (line 261) | public String build() {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Http01Challenge.java
class Http01Challenge (line 26) | public class Http01Challenge extends TokenChallenge {
method Http01Challenge (line 43) | public Http01Challenge(Login login, JSON data) {
method getToken (line 50) | @Override
method acceptable (line 55) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsAlpn01Challenge.java
class TlsAlpn01Challenge (line 35) | public class TlsAlpn01Challenge extends TokenChallenge {
method TlsAlpn01Challenge (line 62) | public TlsAlpn01Challenge(Login login, JSON data) {
method getAcmeValidation (line 70) | public byte[] getAcmeValidation() {
method createCertificate (line 85) | public X509Certificate createCertificate(KeyPair keypair, Identifier i...
method acceptable (line 93) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TokenChallenge.java
class TokenChallenge (line 30) | public class TokenChallenge extends Challenge {
method TokenChallenge (line 44) | public TokenChallenge(Login login, JSON data) {
method getToken (line 51) | protected String getToken() {
method keyAuthorizationFor (line 70) | protected String keyAuthorizationFor(String token) {
method getAuthorization (line 82) | public String getAuthorization() {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/connector/Connection.java
type Connection (line 39) | public interface Connection extends AutoCloseable {
method resetNonce (line 47) | void resetNonce(Session session) throws AcmeException;
method sendRequest (line 64) | int sendRequest(URL url, Session session, @Nullable ZonedDateTime ifMo...
method sendCertificateRequest (line 81) | int sendCertificateRequest(URL url, Login login) throws AcmeException;
method sendSignedPostAsGetRequest (line 97) | int sendSignedPostAsGetRequest(URL url, Login login) throws AcmeExcept...
method sendSignedRequest (line 115) | int sendSignedRequest(URL url, JSONBuilder claims, Login login) throws...
method sendSignedRequest (line 134) | int sendSignedRequest(URL url, JSONBuilder claims, Session session, Re...
method readJsonResponse (line 142) | JSON readJsonResponse() throws AcmeException;
method readCertificates (line 149) | List<X509Certificate> readCertificates() throws AcmeException;
method getRetryAfter (line 156) | Optional<Instant> getRetryAfter();
method getNonce (line 163) | Optional<String> getNonce();
method getLocation (line 174) | URL getLocation();
method getLastModified (line 183) | Optional<ZonedDateTime> getLastModified();
method getExpiration (line 192) | Optional<ZonedDateTime> getExpiration();
method getLinks (line 204) | Collection<URL> getLinks(String relation);
method close (line 209) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java
class DefaultConnection (line 65) | public class DefaultConnection implements Connection {
method DefaultConnection (line 109) | public DefaultConnection(HttpConnector httpConnector) {
method resetNonce (line 114) | @Override
method sendRequest (line 145) | @Override
method sendCertificateRequest (line 177) | @Override
method sendSignedPostAsGetRequest (line 183) | @Override
method sendSignedRequest (line 189) | @Override
method sendSignedRequest (line 195) | @Override
method readJsonResponse (line 201) | @Override
method readCertificates (line 214) | @Override
method getNonce (line 230) | @Override
method getLocation (line 248) | @Override
method getLastModified (line 260) | @Override
method getExpiration (line 274) | @Override
method getLinks (line 301) | @Override
method close (line 308) | @Override
method sendRequest (line 326) | protected void sendRequest(Session session, URL url, Consumer<HttpRequ...
method sendSignedRequest (line 356) | protected int sendSignedRequest(URL url, @Nullable JSONBuilder claims,
method performRequest (line 394) | private int performRequest(URL url, @Nullable JSONBuilder claims, Sess...
method getRetryAfter (line 424) | @Override
method parseRetryAfterHeader (line 441) | private Instant parseRetryAfterHeader(String header) {
method getResponseBody (line 464) | private InputStream getResponseBody() throws IOException {
method throwAcmeException (line 483) | private void throwAcmeException() throws AcmeException {
method expectContentType (line 529) | private void expectContentType(Set<String> expectedTypes) {
method getResponse (line 546) | private HttpResponse<InputStream> getResponse() {
method assertConnectionIsClosed (line 556) | private void assertConnectionIsClosed() {
method logHeaders (line 565) | private void logHeaders() {
method collectLinks (line 584) | private Collection<String> collectLinks(String relation) {
method resolveRelative (line 603) | private URL resolveRelative(String link) {
method resolveUri (line 618) | private URI resolveUri(String uri) {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/connector/HttpConnector.java
class HttpConnector (line 31) | public class HttpConnector {
method defaultUserAgent (line 58) | public static String defaultUserAgent() {
method HttpConnector (line 70) | @SuppressFBWarnings("EI_EXPOSE_REP2") // behavior is intended
method createRequestBuilder (line 85) | public HttpRequest.Builder createRequestBuilder(URL url) {
method getHttpClient (line 101) | public HttpClient getHttpClient() {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/connector/NetworkSettings.java
class NetworkSettings (line 30) | public class NetworkSettings {
method NetworkSettings (line 42) | public NetworkSettings() {
method getProxySelector (line 59) | public ProxySelector getProxySelector() {
method setProxySelector (line 70) | public void setProxySelector(@Nullable ProxySelector proxySelector) {
method getAuthenticator (line 79) | public @Nullable Authenticator getAuthenticator() {
method setAuthenticator (line 89) | public void setAuthenticator(@Nullable Authenticator authenticator) {
method getTimeout (line 96) | public Duration getTimeout() {
method setTimeout (line 106) | public void setTimeout(Duration timeout) {
method isCompressionEnabled (line 119) | public boolean isCompressionEnabled() {
method setCompressionEnabled (line 132) | public void setCompressionEnabled(boolean compression) {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/connector/NonceHolder.java
type NonceHolder (line 27) | public interface NonceHolder extends AutoCloseable {
method getNonce (line 31) | @Nullable
method setNonce (line 37) | void setNonce(@Nullable String nonce);
method close (line 42) | void close();
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/connector/RequestSigner.java
type RequestSigner (line 27) | @FunctionalInterface
method createRequest (line 50) | JSONBuilder createRequest(URL url, @Nullable JSONBuilder payload, @Nul...
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/connector/Resource.java
type Resource (line 19) | public enum Resource {
method Resource (line 31) | Resource(String path) {
method path (line 40) | public String path() {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/connector/ResourceIterator.java
class ResourceIterator (line 39) | public class ResourceIterator<T extends AcmeResource> implements Iterato...
method ResourceIterator (line 61) | public ResourceIterator(Login login, String field, @Nullable URL start...
method hasNext (line 74) | @Override
method next (line 99) | @Override
method remove (line 117) | @Override
method fetch (line 126) | private void fetch() {
method readAndQueue (line 142) | private void readAndQueue() throws AcmeException {
method fillUrlList (line 158) | private void fillUrlList(JSON json) {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/connector/TrimmingInputStream.java
class TrimmingInputStream (line 25) | public class TrimmingInputStream extends InputStream {
method TrimmingInputStream (line 36) | public TrimmingInputStream(InputStream in) {
method read (line 40) | @Override
method available (line 65) | @Override
method close (line 83) | @Override
method isLineSeparator (line 92) | private static boolean isLineSeparator(int ch) {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeException.java
class AcmeException (line 21) | public class AcmeException extends Exception {
method AcmeException (line 28) | public AcmeException() {
method AcmeException (line 38) | public AcmeException(String msg) {
method AcmeException (line 50) | public AcmeException(String msg, Throwable cause) {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeLazyLoadingException.java
class AcmeLazyLoadingException (line 29) | public class AcmeLazyLoadingException extends RuntimeException {
method AcmeLazyLoadingException (line 44) | public AcmeLazyLoadingException(AcmeResource resource, AcmeException c...
method AcmeLazyLoadingException (line 61) | public AcmeLazyLoadingException(Class<? extends AcmeResource> type, UR...
method getType (line 70) | public Class<? extends AcmeResource> getType() {
method getLocation (line 77) | public URL getLocation() {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeNetworkException.java
class AcmeNetworkException (line 23) | public class AcmeNetworkException extends AcmeException {
method AcmeNetworkException (line 33) | public AcmeNetworkException(IOException cause) {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeNotSupportedException.java
class AcmeNotSupportedException (line 23) | public class AcmeNotSupportedException extends AcmeProtocolException {
method AcmeNotSupportedException (line 33) | public AcmeNotSupportedException(String feature) {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeProtocolException.java
class AcmeProtocolException (line 23) | public class AcmeProtocolException extends RuntimeException {
method AcmeProtocolException (line 33) | public AcmeProtocolException(String msg) {
method AcmeProtocolException (line 45) | public AcmeProtocolException(String msg, Throwable cause) {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeRateLimitedException.java
class AcmeRateLimitedException (line 31) | public class AcmeRateLimitedException extends AcmeServerException {
method AcmeRateLimitedException (line 50) | public AcmeRateLimitedException(Problem problem, @Nullable Instant ret...
method getRetryAfter (line 61) | public Optional<Instant> getRetryAfter() {
method getDocuments (line 69) | public Collection<URL> getDocuments() {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeServerException.java
class AcmeServerException (line 29) | public class AcmeServerException extends AcmeException {
method AcmeServerException (line 41) | public AcmeServerException(Problem problem) {
method getType (line 49) | public URI getType() {
method getProblem (line 56) | public Problem getProblem() {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeUnauthorizedException.java
class AcmeUnauthorizedException (line 24) | public class AcmeUnauthorizedException extends AcmeServerException {
method AcmeUnauthorizedException (line 34) | public AcmeUnauthorizedException(Problem problem) {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeUserActionRequiredException.java
class AcmeUserActionRequiredException (line 31) | public class AcmeUserActionRequiredException extends AcmeServerException {
method AcmeUserActionRequiredException (line 46) | public AcmeUserActionRequiredException(Problem problem, @Nullable URI ...
method getTermsOfServiceUri (line 55) | public Optional<URI> getTermsOfServiceUri() {
method getInstance (line 63) | public URL getInstance() {
method toString (line 74) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/provider/AbstractAcmeProvider.java
class AbstractAcmeProvider (line 48) | public abstract class AbstractAcmeProvider implements AcmeProvider {
method connect (line 53) | @Override
method directory (line 58) | @Override
method challengeMap (line 86) | private static Map<String, ChallengeProvider> challengeMap() {
method createChallenge (line 129) | @Override
method createHttpConnector (line 159) | protected HttpConnector createHttpConnector(NetworkSettings settings, ...
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/provider/AcmeProvider.java
type AcmeProvider (line 37) | public interface AcmeProvider {
method accepts (line 47) | boolean accepts(URI serverUri);
method resolve (line 58) | URL resolve(URI serverUri);
method createHttpClient (line 71) | default HttpClient createHttpClient(NetworkSettings networkSettings) {
method connect (line 96) | Connection connect(URI serverUri, NetworkSettings networkSettings, Htt...
method directory (line 112) | @Nullable
method createChallenge (line 125) | @Nullable
method getProposedEabMacAlgorithm (line 137) | default Optional<String> getProposedEabMacAlgorithm() {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/provider/ChallengeProvider.java
type ChallengeProvider (line 25) | @FunctionalInterface
method create (line 37) | Challenge create(Login login, JSON data);
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/provider/GenericAcmeProvider.java
class GenericAcmeProvider (line 26) | public class GenericAcmeProvider extends AbstractAcmeProvider {
method accepts (line 28) | @Override
method resolve (line 34) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/provider/actalis/ActalisAcmeProvider.java
class ActalisAcmeProvider (line 38) | public class ActalisAcmeProvider extends AbstractAcmeProvider {
method accepts (line 42) | @Override
method resolve (line 48) | @Override
method directory (line 65) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/provider/google/GoogleAcmeProvider.java
class GoogleAcmeProvider (line 35) | public class GoogleAcmeProvider extends AbstractAcmeProvider {
method accepts (line 40) | @Override
method resolve (line 46) | @Override
method getProposedEabMacAlgorithm (line 65) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProvider.java
class LetsEncryptAcmeProvider (line 34) | public class LetsEncryptAcmeProvider extends AbstractAcmeProvider {
method accepts (line 39) | @Override
method resolve (line 45) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/provider/pebble/PebbleAcmeProvider.java
class PebbleAcmeProvider (line 50) | public class PebbleAcmeProvider extends AbstractAcmeProvider {
method accepts (line 55) | @Override
method resolve (line 60) | @Override
method parsePath (line 85) | private URL parsePath(String path) throws MalformedURLException {
method createHttpClient (line 103) | @Override
method createPebbleTrustManagerFactory (line 128) | protected TrustManagerFactory createPebbleTrustManagerFactory() {
method createPebbleSSLContext (line 151) | private SSLContext createPebbleSSLContext() {
method readPemFile (line 166) | private Optional<KeyStore> readPemFile(String resource) {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/provider/sslcom/SslComAcmeProvider.java
class SslComAcmeProvider (line 39) | public class SslComAcmeProvider extends AbstractAcmeProvider {
method accepts (line 46) | @Override
method resolve (line 52) | @Override
method directory (line 75) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/provider/zerossl/ZeroSSLAcmeProvider.java
class ZeroSSLAcmeProvider (line 32) | public class ZeroSSLAcmeProvider extends AbstractAcmeProvider {
method accepts (line 36) | @Override
method resolve (line 42) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/AcmeUtils.java
class AcmeUtils (line 50) | public final class AcmeUtils {
type PemLabel (line 80) | public enum PemLabel {
method PemLabel (line 88) | PemLabel(String label) {
method toString (line 92) | @Override
method AcmeUtils (line 99) | private AcmeUtils() {
method sha256hash (line 110) | public static byte[] sha256hash(String z) {
method hexEncode (line 127) | public static String hexEncode(byte[] data) {
method base64UrlEncode (line 144) | public static String base64UrlEncode(byte[] data) {
method base64UrlDecode (line 155) | public static byte[] base64UrlDecode(String base64) {
method base32Encode (line 166) | public static String base32Encode(byte[] data) {
method isValidBase64Url (line 222) | public static boolean isValidBase64Url(@Nullable String base64) {
method toAce (line 239) | public static String toAce(String domain) {
method parseTimestamp (line 254) | public static Instant parseTimestamp(String str) {
method localeToLanguageHeader (line 295) | public static String localeToLanguageHeader(@Nullable Locale locale) {
method stripErrorPrefix (line 321) | @Nullable
method writeToPem (line 340) | public static void writeToPem(byte[] encoded, PemLabel label, Writer out)
method getContentType (line 356) | @Nullable
method validateContact (line 379) | public static void validateContact(URI contact) {
method getRenewalUniqueIdentifier (line 399) | public static String getRenewalUniqueIdentifier(X509Certificate certif...
method getRawInteger (line 432) | private static byte[] getRawInteger(ASN1Integer integer) {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/JSON.java
class JSON (line 57) | public final class JSON implements Serializable {
method JSON (line 72) | private JSON(Map<String, Object> data) {
method JSON (line 84) | private JSON(String path, Map<String, Object> data) {
method parse (line 96) | public static JSON parse(InputStream in) throws IOException {
method parse (line 110) | public static JSON parse(String json) {
method fromMap (line 129) | public static JSON fromMap(Map<String, Object> data) {
method empty (line 138) | public static JSON empty() {
method keySet (line 147) | public Set<String> keySet() {
method contains (line 158) | public boolean contains(String key) {
method get (line 169) | public Value get(String key) {
method getFeature (line 184) | public Value getFeature(String key) {
method toString (line 193) | @Override
method toMap (line 203) | public Map<String,Object> toMap() {
class Array (line 210) | public static final class Array implements Iterable<Value> {
method Array (line 222) | private Array(String path, List<Object> data) {
method size (line 232) | public int size() {
method isEmpty (line 239) | public boolean isEmpty() {
method get (line 250) | public Value get(int index) {
method stream (line 259) | public Stream<Value> stream() {
method iterator (line 266) | @Override
class Value (line 278) | public static final class Value {
method Value (line 290) | private Value(String path, @Nullable Object val) {
method isPresent (line 300) | public boolean isPresent() {
method optional (line 311) | public Optional<Value> optional() {
method onFeature (line 324) | public Value onFeature(String feature) {
method map (line 341) | public <T> Optional<T> map(Function <Value, T> mapper) {
method asString (line 348) | public String asString() {
method asObject (line 355) | @SuppressWarnings("unchecked")
method asEncodedObject (line 365) | public JSON asEncodedObject() {
method asProblem (line 380) | public Problem asProblem(URL baseUrl) {
method asIdentifier (line 389) | public Identifier asIdentifier() {
method asArray (line 399) | @SuppressWarnings("unchecked")
method asInt (line 415) | public int asInt() {
method asBoolean (line 422) | public boolean asBoolean() {
method asURI (line 429) | public URI asURI() {
method asURL (line 440) | public URL asURL() {
method asInstant (line 451) | public Instant asInstant() {
method asDuration (line 464) | public Duration asDuration() {
method asBinary (line 471) | public byte[] asBinary() {
method asStatus (line 478) | public Status asStatus() {
method required (line 488) | private Object required() {
method required (line 503) | private <T> T required(Class<T> type) {
method equals (line 513) | @Override
method hashCode (line 521) | @Override
class ValueIterator (line 530) | private static class ValueIterator implements Iterator<Value> {
method ValueIterator (line 534) | public ValueIterator(Array array) {
method hasNext (line 538) | @Override
method next (line 543) | @Override
method remove (line 551) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/JSONBuilder.java
class JSONBuilder (line 43) | public class JSONBuilder {
method put (line 56) | public JSONBuilder put(String key, @Nullable Object value) {
method put (line 71) | public JSONBuilder put(String key, @Nullable Instant value) {
method put (line 92) | public JSONBuilder put(String key, @Nullable Duration value) {
method putBase64 (line 111) | public JSONBuilder putBase64(String key, byte[] data) {
method putKey (line 124) | public JSONBuilder putKey(String key, PublicKey publickey) {
method object (line 138) | public JSONBuilder object(String key) {
method array (line 153) | public JSONBuilder array(String key, Collection<?> values) {
method toMap (line 163) | public Map<String, Object> toMap() {
method toJSON (line 172) | public JSON toJSON() {
method toString (line 179) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/JoseUtils.java
class JoseUtils (line 42) | public final class JoseUtils {
method JoseUtils (line 46) | private JoseUtils() {
method createJoseRequest (line 68) | public static JSONBuilder createJoseRequest(URL url, KeyPair keypair,
method createExternalAccountBinding (line 124) | public static Map<String, Object> createExternalAccountBinding(String ...
method publicKeyToJWK (line 155) | public static Map<String, Object> publicKeyToJWK(PublicKey key) {
method jwkToPublicKey (line 171) | public static PublicKey jwkToPublicKey(Map<String, Object> jwk) {
method thumbprint (line 186) | public static byte[] thumbprint(PublicKey key) {
method keyAlgorithm (line 205) | public static String keyAlgorithm(JsonWebKey jwk) {
method macKeyAlgorithm (line 232) | public static String macKeyAlgorithm(SecretKey macKey) {
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/util/CSRBuilder.java
class CSRBuilder (line 59) | public class CSRBuilder {
method addDomain (line 80) | public void addDomain(String domain) {
method addDomains (line 92) | public void addDomains(Collection<String> domains) {
method addDomains (line 104) | public void addDomains(String... domains) {
method addIP (line 116) | public void addIP(InetAddress address) {
method addIPs (line 127) | public void addIPs(Collection<InetAddress> ips) {
method addIPs (line 138) | public void addIPs(InetAddress... ips) {
method addIdentifier (line 149) | public void addIdentifier(Identifier id) {
method addIdentifiers (line 167) | public void addIdentifiers(Collection<Identifier> ids) {
method addIdentifiers (line 178) | public void addIdentifiers(Identifier... ids) {
method addValue (line 195) | public void addValue(String attName, String value) {
method addValue (line 213) | public void addValue(ASN1ObjectIdentifier oid, String value) {
method setCommonName (line 231) | public void setCommonName(String cn) {
method setOrganization (line 240) | public void setOrganization(String o) {
method setOrganizationalUnit (line 249) | public void setOrganizationalUnit(String ou) {
method setLocality (line 258) | public void setLocality(String l) {
method setState (line 267) | public void setState(String st) {
method setCountry (line 276) | public void setCountry(String c) {
method sign (line 286) | public void sign(KeyPair keypair) throws IOException {
method getCSR (line 323) | public PKCS10CertificationRequest getCSR() {
method getEncoded (line 334) | public byte[] getEncoded() throws IOException {
method write (line 345) | public void write(Writer w) throws IOException {
method write (line 362) | public void write(OutputStream out) throws IOException {
method toString (line 366) | @Override
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/util/CertificateUtils.java
class CertificateUtils (line 62) | public final class CertificateUtils {
method CertificateUtils (line 72) | private CertificateUtils() {
method readCSR (line 84) | public static PKCS10CertificationRequest readCSR(InputStream in) throw...
method createTlsAlpn01Certificate (line 108) | public static X509Certificate createTlsAlpn01Certificate(KeyPair keypa...
method createTestRootCertificate (line 156) | public static X509Certificate createTestRootCertificate(String subject,
method createTestIntermediateCertificate (line 196) | public static X509Certificate createTestIntermediateCertificate(String...
method createTestCertificate (line 242) | public static X509Certificate createTestCertificate(PKCS10Certificatio...
method buildCertificate (line 287) | private static X509Certificate buildCertificate(Function<ContentSigner...
FILE: acme4j-client/src/main/java/org/shredzone/acme4j/util/KeyPairUtils.java
class KeyPairUtils (line 40) | public class KeyPairUtils {
method KeyPairUtils (line 42) | private KeyPairUtils() {
method createKeyPair (line 55) | public static KeyPair createKeyPair() {
method createKeyPair (line 66) | public static KeyPair createKeyPair(int keysize) {
method createECKeyPair (line 83) | public static KeyPair createECKeyPair(String name) {
method readKeyPair (line 104) | public static KeyPair readKeyPair(Reader r) throws IOException {
method writeKeyPair (line 122) | public static void writeKeyPair(KeyPair keypair, Writer w) throws IOEx...
method writePublicKey (line 138) | public static void writePublicKey(PublicKey key, Writer w) throws IOEx...
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/AccountBuilderTest.java
class AccountBuilderTest (line 46) | public class AccountBuilderTest {
method testRegistration (line 54) | @Test
method testRegistrationWithKid (line 112) | @ParameterizedTest
method testRejectInvalidMacAlg (line 198) | @ParameterizedTest
method testOnlyExistingRegistration (line 210) | @Test
method testEmailAddresses (line 248) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/AccountTest.java
class AccountTest (line 50) | public class AccountTest {
method testUpdateAccount (line 59) | @Test
method testLazyLoading (line 125) | @Test
method testPreAuthorizeDomain (line 175) | @Test
method testPreAuthorizeDomainSubdomainsFails (line 223) | @Test
method testPreAuthorizeDomainSubdomains (line 245) | @Test
method testNoPreAuthorizeDomain (line 294) | @Test
method testAuthorizeBadDomain (line 329) | @Test
method testChangeKey (line 352) | @Test
method testChangeSameKey (line 416) | @Test
method testDeactivate (line 432) | @Test
method testNewOrder (line 461) | @Test
method testModify (line 475) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/AcmeJsonResourceTest.java
class AcmeJsonResourceTest (line 34) | public class AcmeJsonResourceTest {
method testLoginConstructor (line 42) | @Test
method testSetJson (line 62) | @Test
method testRetryAfter (line 86) | @Test
method testInvalidate (line 105) | @Test
method assertUpdateInvoked (line 135) | private static void assertUpdateInvoked(AcmeJsonResource resource, int...
class DummyJsonResource (line 143) | private static class DummyJsonResource extends AcmeJsonResource {
method DummyJsonResource (line 149) | public DummyJsonResource(Login login, URL location) {
method DummyJsonResource (line 153) | public DummyJsonResource(Login login, URL location, JSON json, @Null...
method fetch (line 159) | @Override
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/AcmeResourceTest.java
class AcmeResourceTest (line 34) | public class AcmeResourceTest {
method testConstructor (line 39) | @Test
method testSerialization (line 54) | @Test
method testRebind (line 100) | @Test
class DummyResource (line 117) | private static class DummyResource extends AcmeResource {
method DummyResource (line 120) | public DummyResource(Login login, URL location) {
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java
class AuthorizationTest (line 44) | public class AuthorizationTest {
method testFindChallenge (line 54) | @Test
method testFindChallengeByType (line 81) | @Test
method testFailDuplicateChallenges (line 106) | @Test
method testUpdate (line 117) | @Test
method testWildcard (line 158) | @Test
method testLazyLoading (line 195) | @Test
method testUpdateRetryAfter (line 240) | @Test
method testDeactivate (line 289) | @Test
method createChallengeAuthorization (line 322) | private Authorization createChallengeAuthorization() throws IOException {
class NonExistingChallenge (line 340) | private static class NonExistingChallenge extends Challenge {
method NonExistingChallenge (line 341) | public NonExistingChallenge(Login login, JSON data) {
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/CertificateTest.java
class CertificateTest (line 48) | public class CertificateTest {
method testDownload (line 58) | @Test
method testRevokeCertificate (line 153) | @Test
method testRevokeCertificateWithReason (line 201) | @Test
method testRevocationReason (line 249) | @Test
method testRevokeCertificateByKeyPair (line 258) | @Test
method testRenewalInfo (line 285) | @Test
method testMarkedAsReplaced (line 364) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/IdentifierTest.java
class IdentifierTest (line 29) | public class IdentifierTest {
method testConstants (line 31) | @Test
method testGetters (line 37) | @Test
method testDns (line 61) | @Test
method testNoDns (line 74) | @Test
method testIp (line 81) | @Test
method testNoIp (line 99) | @Test
method testAncestorDomain (line 106) | @Test
method testAllowSubdomainAuth (line 144) | @Test
method testEquals (line 168) | @Test
method testNull (line 189) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/LoginTest.java
class LoginTest (line 43) | public class LoginTest {
method testConstructor (line 50) | @Test
method testBinder (line 70) | @Test
method testKeyChange (line 97) | @Test
method testCreateChallenge (line 114) | @Test
method testBindChallenge (line 147) | @Test
method testNewOrder (line 190) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/OrderBuilderTest.java
class OrderBuilderTest (line 42) | public class OrderBuilderTest {
method testOrderCertificate (line 50) | @Test
method testAutoRenewOrderCertificate (line 133) | @Test
method testOrderCertificateWithAncestor (line 197) | @Test
method testOrderCertificateWithAncestorFails (line 255) | @Test
method testAutoRenewOrderCertificateFails (line 278) | @Test
method testAutoRenewNotMixed (line 299) | @Test
method testProfileOrderCertificate (line 344) | @Test
method testUnsupportedProfileOrderCertificateFails (line 390) | @Test
method testProfileOrderCertificateFails (line 414) | @Test
method testARIReplaces (line 435) | @Test
method testARIReplaceFails (line 475) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/OrderTest.java
class OrderTest (line 38) | public class OrderTest {
method testUpdate (line 46) | @Test
method testLazyLoading (line 117) | @Test
method testFinalize (line 161) | @Test
method testAutoRenewUpdate (line 226) | @Test
method testAutoRenewFinalize (line 269) | @Test
method testCancel (line 311) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/ProblemTest.java
class ProblemTest (line 30) | public class ProblemTest {
method testProblem (line 32) | @Test
method testToString (line 79) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/RenewalInfoTest.java
class RenewalInfoTest (line 39) | public class RenewalInfoTest {
method testGetters (line 46) | @Test
method testRandomProposal (line 129) | @Test
method testDateAssertion (line 159) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/SessionTest.java
class SessionTest (line 42) | public class SessionTest {
method testConstructor (line 47) | @Test
method testGettersAndSetters (line 76) | @Test
method testLogin (line 115) | @Test
method testDirectory (line 133) | @Test
method testNoMeta (line 209) | @Test
method testLocale (line 257) | @Test
method testGetHttpClientWithReuse (line 281) | @Test
method testGetHttpClientThreadSafety (line 295) | @Test
method testConnectionsShareHttpClient (line 327) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/StatusTest.java
class StatusTest (line 25) | public class StatusTest {
method testParse (line 30) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java
class ChallengeTest (line 44) | public class ChallengeTest {
method testUnmarshal (line 50) | @Test
method testRespond (line 75) | @Test
method testNotAcceptable (line 88) | @Test
method testTrigger (line 98) | @Test
method testUpdate (line 130) | @Test
method testUpdateRetryAfter (line 160) | @Test
method testBadUnmarshall (line 197) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/challenge/Dns01ChallengeTest.java
class Dns01ChallengeTest (line 32) | public class Dns01ChallengeTest {
method testDnsChallenge (line 39) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/challenge/DnsAccount01ChallengeTest.java
class DnsAccount01ChallengeTest (line 32) | class DnsAccount01ChallengeTest {
method testDnsChallenge (line 39) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/challenge/DnsPersist01ChallengeTest.java
class DnsPersist01ChallengeTest (line 36) | class DnsPersist01ChallengeTest {
method testDnsChallenge (line 43) | @Test
method testBuilder (line 73) | @Test
method testBuilderNoQuotes (line 106) | @Test
method testConstraintChecks (line 123) | @Test
method createDomainList (line 181) | private String[] createDomainList(int length) {
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/challenge/Http01ChallengeTest.java
class Http01ChallengeTest (line 31) | public class Http01ChallengeTest {
method testHttpChallenge (line 42) | @Test
method testNoTokenSet (line 60) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsAlpn01ChallengeTest.java
class TlsAlpn01ChallengeTest (line 35) | public class TlsAlpn01ChallengeTest {
method testTlsAlpn01Challenge (line 46) | @Test
method testTlsAlpn01Certificate (line 65) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TokenChallengeTest.java
class TokenChallengeTest (line 28) | public class TokenChallengeTest {
method testInvalidToken (line 33) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java
class DefaultConnectionTest (line 67) | @WireMockTest
method setup (line 89) | @BeforeEach
method testNoNonceFromHeader (line 113) | @Test
method testGetNonceFromHeader (line 128) | @Test
method testGetNonceFromHeaderFailed (line 147) | @Test
method testGetNonceFromHeaderHttpError (line 174) | @Test
method testInvalidNonceFromHeader (line 199) | @Test
method testResetNonceSucceedsIfNoncePresent (line 222) | @Test
method testResetNonceThrowsException (line 241) | @Test
method testGetAbsoluteLocation (line 261) | @Test
method testGetRelativeLocation (line 277) | @Test
method testGetLink (line 293) | @Test
method testGetMultiLink (line 313) | @Test
method testGetMultiHeaderFieldLink (line 334) | @Test
method testGetNoLink (line 355) | @Test
method testNoLocation (line 368) | @Test
method testHandleRetryAfterHeaderDate (line 384) | @Test
method testHandleRetryAfterHeaderDelta (line 401) | @Test
method testHandleRetryAfterHeaderNull (line 421) | @Test
method testAccept (line 438) | @Test
method testAcceptThrowsException (line 459) | @Test
method testAcceptThrowsUserActionRequiredException (line 490) | @Test
method testAcceptThrowsRateLimitedException (line 524) | @Test
method testAcceptThrowsOtherException (line 561) | @Test
method testAcceptThrowsNoTypeException (line 590) | @Test
method testAcceptThrowsServerException (line 613) | @Test
method testSendRequest (line 637) | @Test
method testSendRequestIfModifiedSince (line 656) | @Test
method testSendSignedRequest (line 681) | @Test
method testSendSignedPostAsGetRequest (line 739) | @Test
method testSendCertificateRequest (line 795) | @Test
method testSendSignedRequestNoKid (line 826) | @Test
method testSendSignedRequestNoNonce (line 890) | @Test
method testReadJsonResponse (line 905) | @Test
method testReadCertificate (line 930) | @Test
method testReadBadCertificate (line 956) | @Test
method testLastModifiedUnset (line 986) | @Test
method testLastModifiedSet (line 996) | @Test
method testLastModifiedInvalid (line 1012) | @Test
method testExpirationUnset (line 1027) | @Test
method testExpirationNoCache (line 1037) | @Test
method testExpirationMaxAgeZero (line 1049) | @Test
method testExpirationMaxAgeButNoCache (line 1061) | @Test
method testExpirationMaxAge (line 1073) | @Test
method testExpirationExpires (line 1089) | @Test
method testExpirationInvalidExpires (line 1105) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/connector/DummyConnection.java
class DummyConnection (line 34) | public class DummyConnection implements Connection {
method resetNonce (line 36) | @Override
method sendRequest (line 41) | @Override
method sendCertificateRequest (line 46) | @Override
method sendSignedPostAsGetRequest (line 51) | @Override
method sendSignedRequest (line 56) | @Override
method sendSignedRequest (line 62) | @Override
method readJsonResponse (line 67) | @Override
method readCertificates (line 72) | @Override
method getRetryAfter (line 77) | @Override
method getNonce (line 82) | @Override
method getLocation (line 87) | @Override
method getLastModified (line 92) | @Override
method getExpiration (line 97) | @Override
method getLinks (line 102) | @Override
method close (line 107) | @Override
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/connector/HttpConnectorTest.java
class HttpConnectorTest (line 26) | public class HttpConnectorTest {
method testRequestBuilderDefaultValues (line 32) | @Test
method testUserAgent (line 50) | @Test
method testGetHttpClient (line 60) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/connector/NetworkSettingsTest.java
class NetworkSettingsTest (line 30) | public class NetworkSettingsTest {
method testGettersAndSetters (line 35) | @Test
method testInvalidTimeouts (line 62) | @Test
method testSystemProperty (line 77) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/connector/ResourceIteratorTest.java
class ResourceIteratorTest (line 41) | public class ResourceIteratorTest {
method setup (line 50) | @BeforeEach
method nullTest (line 66) | @Test
method iteratorTest (line 80) | @Test
method nextHasNextTest (line 95) | @Test
method removeTest (line 119) | @Test
method createIterator (line 135) | private Iterator<Authorization> createIterator(URL first) throws IOExc...
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/connector/ResourceTest.java
class ResourceTest (line 24) | public class ResourceTest {
method testPath (line 29) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/connector/SessionProviderTest.java
class SessionProviderTest (line 36) | public class SessionProviderTest {
method testNone (line 42) | @Test
method testConnectURI (line 53) | @Test
method testDuplicate (line 69) | @Test
class Provider1 (line 77) | public static class Provider1 implements AcmeProvider {
method accepts (line 78) | @Override
method connect (line 85) | @Override
method resolve (line 90) | @Override
method directory (line 95) | @Override
method createChallenge (line 100) | @Override
class Provider2 (line 106) | public static class Provider2 implements AcmeProvider {
method accepts (line 107) | @Override
method connect (line 113) | @Override
method resolve (line 118) | @Override
method directory (line 123) | @Override
method createChallenge (line 128) | @Override
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/connector/TrimmingInputStreamTest.java
class TrimmingInputStreamTest (line 27) | public class TrimmingInputStreamTest {
method testEmpty (line 38) | @Test
method testLineBreakOnly (line 44) | @Test
method testTrim (line 56) | @Test
method testTrimEndOnly (line 62) | @Test
method testTrimStartOnly (line 68) | @Test
method testTrimFull (line 74) | @Test
method testAvailable (line 80) | @Test
method trimByStream (line 91) | private String trimByStream(String str) throws IOException {
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeExceptionTest.java
class AcmeExceptionTest (line 25) | public class AcmeExceptionTest {
method testAcmeException (line 27) | @Test
method testMessageAcmeException (line 34) | @Test
method testCausedAcmeException (line 42) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeLazyLoadingExceptionTest.java
class AcmeLazyLoadingExceptionTest (line 30) | public class AcmeLazyLoadingExceptionTest {
method testAcmeLazyLoadingException (line 34) | @Test
class TestResource (line 50) | private static class TestResource extends AcmeResource {
method TestResource (line 54) | public TestResource(Login login, URL location) {
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeNetworkExceptionTest.java
class AcmeNetworkExceptionTest (line 25) | public class AcmeNetworkExceptionTest {
method testAcmeNetworkException (line 27) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeNotSupportedExceptionTest.java
class AcmeNotSupportedExceptionTest (line 23) | public class AcmeNotSupportedExceptionTest {
method testAcmeNotSupportedException (line 25) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeProtocolExceptionTest.java
class AcmeProtocolExceptionTest (line 23) | public class AcmeProtocolExceptionTest {
method testAcmeProtocolException (line 25) | @Test
method testCausedAcmeProtocolException (line 34) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeRateLimitedExceptionTest.java
class AcmeRateLimitedExceptionTest (line 30) | public class AcmeRateLimitedExceptionTest {
method testAcmeRateLimitedException (line 35) | @Test
method testNullAcmeRateLimitedException (line 57) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeUserActionRequiredExceptionTest.java
class AcmeUserActionRequiredExceptionTest (line 27) | public class AcmeUserActionRequiredExceptionTest {
method testAcmeUserActionRequiredException (line 32) | @Test
method testNullAcmeUserActionRequiredException (line 53) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java
class AbstractAcmeProviderTest (line 53) | public class AbstractAcmeProviderTest {
method testConnect (line 62) | @Test
method testResources (line 86) | @Test
method testResourcesCacheControl (line 111) | @Test
method testResourcesNotExprired (line 154) | @Test
method testResourcesExprired (line 177) | @Test
method testResourcesIfModifiedSince (line 219) | @Test
method testCreateChallenge (line 254) | @Test
class TestAbstractAcmeProvider (line 310) | private static class TestAbstractAcmeProvider extends AbstractAcmeProv...
method TestAbstractAcmeProvider (line 313) | public TestAbstractAcmeProvider() {
method TestAbstractAcmeProvider (line 317) | public TestAbstractAcmeProvider(Connection connection) {
method accepts (line 321) | @Override
method resolve (line 327) | @Override
method connect (line 333) | @Override
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/provider/GenericAcmeProviderTest.java
class GenericAcmeProviderTest (line 29) | public class GenericAcmeProviderTest {
method testAccepts (line 34) | @Test
method testResolve (line 46) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/provider/TestableConnectionProvider.java
class TestableConnectionProvider (line 42) | public class TestableConnectionProvider extends DummyConnection implemen...
method putTestResource (line 57) | public void putTestResource(Resource r, URL u) {
method putMetadata (line 69) | public void putMetadata(String key, Object value) {
method putTestChallenge (line 84) | public void putTestChallenge(String type, BiFunction<Login, JSON, Chal...
method getChallenge (line 95) | public Challenge getChallenge(String type) {
method createSession (line 105) | public Session createSession() {
method createLogin (line 112) | public Login createLogin() throws IOException {
method getAccountKeyPair (line 117) | public KeyPair getAccountKeyPair() {
method getRetryAfter (line 121) | @Override
method accepts (line 126) | @Override
method resolve (line 131) | @Override
method connect (line 136) | @Override
method directory (line 141) | @Override
method createChallenge (line 149) | @Override
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/provider/actalis/ActalisAcmeProviderTest.java
class ActalisAcmeProviderTest (line 26) | public class ActalisAcmeProviderTest {
method testAccepts (line 33) | @Test
method testResolve (line 49) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/provider/google/GoogleAcmeProviderTest.java
class GoogleAcmeProviderTest (line 29) | public class GoogleAcmeProviderTest {
method testAccepts (line 37) | @Test
method testResolve (line 54) | @Test
method testMacAlgorithm (line 68) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProviderTest.java
class LetsEncryptAcmeProviderTest (line 29) | public class LetsEncryptAcmeProviderTest {
method testAccepts (line 37) | @Test
method testResolve (line 55) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/provider/pebble/PebbleAcmeProviderTest.java
class PebbleAcmeProviderTest (line 38) | public class PebbleAcmeProviderTest {
method testAccepts (line 43) | @Test
method testResolve (line 63) | @Test
method testCreatePebbleTrustManagerFactory (line 93) | @Test
method testCreateHttpClient (line 143) | @Test
method loadPebbleCertificate (line 174) | private X509Certificate loadPebbleCertificate() throws Exception {
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/provider/sslcom/SslComAcmeProviderTest.java
class SslComAcmeProviderTest (line 29) | public class SslComAcmeProviderTest {
method testAccepts (line 39) | @Test
method testResolve (line 60) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/provider/zerossl/ZeroSSLAcmeProviderTest.java
class ZeroSSLAcmeProviderTest (line 29) | public class ZeroSSLAcmeProviderTest {
method testAccepts (line 36) | @Test
method testResolve (line 52) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/AcmeUtilsTest.java
class AcmeUtilsTest (line 48) | public class AcmeUtilsTest {
method setup (line 50) | @BeforeAll
method testPrivateConstructor (line 58) | @Test
method testSha256HashHexEncode (line 69) | @Test
method testBase64UrlEncode (line 78) | @Test
method testBase64UrlDecode (line 87) | @Test
method testBase32Encode (line 96) | @ParameterizedTest
method testBase64UrlValid (line 113) | @ParameterizedTest
method testBase64UrlInvalid (line 126) | @ParameterizedTest
method testToAce (line 142) | @Test
method testParser (line 167) | @ParameterizedTest
method provideTimestamps (line 173) | private static Stream<Arguments> provideTimestamps() {
method testInvalid (line 208) | @Test
method testLocaleToLanguageHeader (line 227) | @Test
method testStripErrorPrefix (line 246) | @Test
method testWriteToPem (line 257) | @Test
method testGetContentTypeForJson (line 283) | @ParameterizedTest
method testGetContentType (line 299) | @Test
method testValidateContact (line 312) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/JSONBuilderTest.java
class JSONBuilderTest (line 33) | public class JSONBuilderTest {
method testEmpty (line 38) | @Test
method testBasics (line 48) | @Test
method testDate (line 78) | @Test
method testBase64 (line 94) | @Test
method testKey (line 109) | @Test
method testObject (line 132) | @Test
method testArray (line 149) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/JSONTest.java
class JSONTest (line 45) | public class JSONTest {
method testEmpty (line 52) | @Test
method testParsers (line 62) | @Test
method testParsersBadJSON (line 88) | @Test
method testObject (line 98) | @Test
method testArray (line 114) | @Test
method testEmptyArray (line 126) | @Test
method testArrayIterator (line 139) | @Test
method testArrayStream (line 167) | @Test
method testGetter (line 185) | @Test
method testNullGetter (line 231) | @Test
method testWrongGetter (line 289) | @Test
method testSerialization (line 325) | @Test
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/JoseUtilsTest.java
class JoseUtilsTest (line 39) | public class JoseUtilsTest {
method testCreateJosePostRequest (line 47) | @Test
method testCreateJosePostAsGetRequest (line 87) | @Test
method testCreateJoseKeyChangeRequest (line 123) | @Test
method testCreateExternalAccountBinding (line 164) | @ParameterizedTest
method testPublicKeyToJWK (line 186) | @Test
method testJWKToPublicKey (line 198) | @Test
method testThumbprint (line 211) | @Test
method testRsaKey (line 221) | @Test
method testP256ECKey (line 233) | @Test
method testP384ECKey (line 245) | @Test
method testP521ECKey (line 257) | @Test
method testMacKey (line 269) | @Test
method assertExternalAccountBinding (line 291) | public static void assertExternalAccountBinding(String serialized, URL...
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/TestUtils.java
class TestUtils (line 61) | public final class TestUtils {
method TestUtils (line 81) | private TestUtils() {
method getResourceAsByteArray (line 92) | public static byte[] getResourceAsByteArray(String name) throws IOExce...
method getJSON (line 111) | public static JSON getJSON(String key) {
method session (line 122) | public static Session session() {
method login (line 130) | public static Login login() {
method url (line 146) | public static URL url(String url) {
method session (line 160) | public static Session session(final AcmeProvider provider) {
method createKeyPair (line 183) | public static KeyPair createKeyPair() {
method createDomainKeyPair (line 208) | public static KeyPair createDomainKeyPair() throws IOException {
method createECKeyPair (line 231) | public static KeyPair createECKeyPair(String name) throws IOException {
method createSecretKey (line 249) | public static SecretKey createSecretKey(String algorithm) throws IOExc...
method createCertificate (line 268) | public static List<X509Certificate> createCertificate(String resource)...
method createProblem (line 290) | public static Problem createProblem(URI type, String detail, @Nullable...
method main (line 305) | public static void main(String... args) throws Exception {
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/util/CSRBuilderTest.java
class CSRBuilderTest (line 52) | public class CSRBuilderTest {
method setup (line 60) | @BeforeAll
method testGenerate (line 71) | @Test
method testECCGenerate (line 88) | @Test
method testNoDomain (line 105) | @Test
method testUnknownType (line 118) | @Test
method testNoSign (line 131) | @Test
method testAddAttrValues (line 161) | @Test
method createBuilderWithValues (line 205) | private CSRBuilder createBuilderWithValues() throws UnknownHostExcepti...
method csrTest (line 245) | private void csrTest(PKCS10CertificationRequest csr) {
method writerTest (line 293) | private void writerTest(CSRBuilder builder) throws IOException {
method getIP (line 335) | private static InetAddress getIP(ASN1Encodable name) {
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/util/CertificateUtilsTest.java
class CertificateUtilsTest (line 50) | public class CertificateUtilsTest {
method testReadCSR (line 55) | @Test
method testPrivateConstructor (line 79) | @Test
method testCreateTlsAlpn01Certificate (line 92) | @Test
method testCreateTlsAlpn01CertificateWithIp (line 135) | @Test
method testCreateTestRootCertificate (line 151) | @Test
method testCreateTestIntermediateCertificate (line 175) | @Test
method testCreateTestCertificate (line 207) | @Test
method getSANs (line 249) | private Set<String> getSANs(X509Certificate cert) throws CertificatePa...
method getIpSANs (line 268) | private Set<InetAddress> getIpSANs(X509Certificate cert) throws Certif...
FILE: acme4j-client/src/test/java/org/shredzone/acme4j/util/KeyPairUtilsTest.java
class KeyPairUtilsTest (line 34) | public class KeyPairUtilsTest {
method setup (line 38) | @BeforeAll
method testCreateStandardKeyPair (line 46) | @Test
method testCreateKeyPair (line 59) | @Test
method testWriteAndRead (line 72) | @Test
method testCreateECCKeyPair (line 105) | @Test
method testWriteAndReadEC (line 115) | @Test
method testPrivateConstructor (line 161) | @Test
FILE: acme4j-example/src/main/java/org/shredzone/acme4j/example/ClientTest.java
class ClientTest (line 70) | public class ClientTest {
type ChallengeType (line 106) | private enum ChallengeType {HTTP, DNS}
method fetchCertificate (line 115) | public void fetchCertificate(Collection<String> domains) throws IOExce...
method loadOrCreateUserKeyPair (line 176) | private KeyPair loadOrCreateUserKeyPair() throws IOException {
method loadOrCreateDomainKeyPair (line 199) | private KeyPair loadOrCreateDomainKeyPair() throws IOException {
method findOrRegisterAccount (line 227) | private Account findOrRegisterAccount(Session session, KeyPair account...
method authorize (line 261) | private void authorize(Authorization auth) throws AcmeException, Inter...
method httpChallenge (line 314) | public Challenge httpChallenge(Authorization auth) throws AcmeException {
method dnsChallenge (line 355) | public Challenge dnsChallenge(Authorization auth) throws AcmeException {
method acceptChallenge (line 385) | public void acceptChallenge(String message) throws AcmeException {
method completeChallenge (line 402) | public void completeChallenge(String message) {
method acceptAgreement (line 416) | public void acceptAgreement(URI agreement) throws AcmeException {
method main (line 432) | public static void main(String... args) {
FILE: acme4j-it/src/main/java/org/shredzone/acme4j/it/BammBammClient.java
class BammBammClient (line 33) | public class BammBammClient {
method BammBammClient (line 44) | public BammBammClient(String baseUrl) {
method httpAddToken (line 56) | public void httpAddToken(String token, String challenge) throws IOExce...
method httpRemoveToken (line 69) | public void httpRemoveToken(String token) throws IOException {
method dnsAddARecord (line 85) | public void dnsAddARecord(String domain, String ip) throws IOException {
method dnsRemoveARecord (line 98) | public void dnsRemoveARecord(String domain) throws IOException {
method dnsAddTxtRecord (line 113) | public void dnsAddTxtRecord(String domain, String txt) throws IOExcept...
method dnsRemoveTxtRecord (line 126) | public void dnsRemoveTxtRecord(String domain) throws IOException {
method dnsAddCnameRecord (line 142) | public void dnsAddCnameRecord(String domain, String cname) throws IOEx...
method dnsRemoveCnameRecord (line 156) | public void dnsRemoveCnameRecord(String domain) throws IOException {
method dnsAddServFailRecord (line 169) | public void dnsAddServFailRecord(String domain) throws IOException {
method dnsRemoveServFailRecord (line 182) | public void dnsRemoveServFailRecord(String domain) throws IOException {
method tlsAlpnAddCertificate (line 196) | public void tlsAlpnAddCertificate(String domain, String keyauth) throw...
method tlsAlpnRemoveCertificate (line 209) | public void tlsAlpnRemoveCertificate(String domain) throws IOException {
method sendRequest (line 223) | private void sendRequest(String call, String body) throws IOException {
FILE: acme4j-it/src/test/java/org/shredzone/acme4j/it/ProviderIT.java
class ProviderIT (line 38) | public class ProviderIT {
method testActalis (line 43) | @Test
method testGoogle (line 56) | @Test
method testLetsEncrypt (line 76) | @Test
method testPebble (line 96) | @Test
method testSslCom (line 112) | @Test
method testSslComStaging (line 137) | @Test
method testZeroSsl (line 163) | @Test
FILE: acme4j-it/src/test/java/org/shredzone/acme4j/it/SoftFailExtension.java
class SoftFailExtension (line 24) | public class SoftFailExtension implements TestExecutionExceptionHandler {
method handleTestExecutionException (line 25) | @Override
FILE: acme4j-it/src/test/java/org/shredzone/acme4j/it/boulder/OrderHttpIT.java
class OrderHttpIT (line 33) | public class OrderHttpIT {
method testHttpValidation (line 45) | @Test
method boulderURI (line 89) | protected URI boulderURI() {
method createKeyPair (line 98) | protected KeyPair createKeyPair() {
FILE: acme4j-it/src/test/java/org/shredzone/acme4j/it/pebble/AccountIT.java
class AccountIT (line 35) | public class AccountIT extends PebbleITBase {
method testCreate (line 40) | @Test
method testReCreate (line 73) | @Test
method testCreateOnlyExisting (line 105) | @Test
method testNotExisting (line 134) | @Test
method testModify (line 147) | @Test
method testKeyChange (line 176) | @Test
method testDeactivate (line 204) | @Test
FILE: acme4j-it/src/test/java/org/shredzone/acme4j/it/pebble/OrderIT.java
class OrderIT (line 47) | public class OrderIT extends PebbleITBase {
method testHttpValidation (line 55) | @ParameterizedTest
method testDnsValidation (line 75) | @ParameterizedTest
method testDnsAccountValidation (line 97) | @ParameterizedTest
method testDnsPersistValidation (line 119) | @ParameterizedTest
method testTlsAlpnValidation (line 142) | @ParameterizedTest
method testDomainKeyRevocation (line 164) | @Test
method orderCertificate (line 192) | private void orderCertificate(String domain, Validator validator, Revo...
method standardRevoker (line 278) | private static void standardRevoker(Session session, Certificate certi...
method domainKeyRevoker (line 288) | private static void domainKeyRevoker(Session session, Certificate cert...
type Validator (line 294) | @FunctionalInterface
method prepare (line 296) | Challenge prepare(Authorization auth) throws Exception;
type Revoker (line 299) | @FunctionalInterface
method revoke (line 301) | void revoke(Session session, Certificate certificate, KeyPair keyPair,
FILE: acme4j-it/src/test/java/org/shredzone/acme4j/it/pebble/OrderWildcardIT.java
class OrderWildcardIT (line 35) | public class OrderWildcardIT extends PebbleITBase {
method testDnsValidation (line 44) | @Test
method testDnsPersistValidation (line 117) | @Test
FILE: acme4j-it/src/test/java/org/shredzone/acme4j/it/pebble/PebbleITBase.java
class PebbleITBase (line 43) | public abstract class PebbleITBase {
method performCleanup (line 53) | @AfterEach
method cleanup (line 61) | protected void cleanup(CleanupCallback callback) {
method pebbleURI (line 68) | protected URI pebbleURI() {
method getBammBammClient (line 75) | protected BammBammClient getBammBammClient() {
method createKeyPair (line 87) | protected KeyPair createKeyPair() {
method assertIsPebbleUrl (line 98) | protected void assertIsPebbleUrl(URL url) {
method updateAuth (line 112) | protected void updateAuth(Authorization auth) {
method updateOrder (line 126) | protected void updateOrder(Order order) {
type CleanupCallback (line 134) | @FunctionalInterface
method cleanup (line 136) | void cleanup() throws Exception;
FILE: acme4j-it/src/test/java/org/shredzone/acme4j/it/pebble/SessionIT.java
class SessionIT (line 30) | public class SessionIT extends PebbleITBase {
method testResources (line 32) | @Test
method testMetadata (line 41) | @Test
FILE: acme4j-smime/src/main/java/org/shredzone/acme4j/smime/EmailIdentifier.java
class EmailIdentifier (line 28) | public class EmailIdentifier extends Identifier {
method EmailIdentifier (line 45) | private EmailIdentifier(String value) {
method email (line 56) | public static EmailIdentifier email(String email) {
method email (line 68) | public static EmailIdentifier email(InternetAddress email) {
method getEmailAddress (line 79) | public InternetAddress getEmailAddress() {
FILE: acme4j-smime/src/main/java/org/shredzone/acme4j/smime/challenge/EmailReply00Challenge.java
class EmailReply00Challenge (line 34) | public class EmailReply00Challenge extends TokenChallenge {
method EmailReply00Challenge (line 53) | public EmailReply00Challenge(Login login, JSON data) {
method getFrom (line 62) | public String getFrom() {
method getExpectedSender (line 74) | public InternetAddress getExpectedSender() {
method getToken (line 91) | public String getToken(String part1) {
method getTokenPart2 (line 99) | public String getTokenPart2() {
method getAuthorization (line 106) | @Override
method getAuthorization (line 118) | public String getAuthorization(String part1) {
method acceptable (line 123) | @Override
FILE: acme4j-smime/src/main/java/org/shredzone/acme4j/smime/challenge/EmailReply00ChallengeProvider.java
class EmailReply00ChallengeProvider (line 28) | @ChallengeType(EmailReply00Challenge.TYPE)
method create (line 31) | @Override
FILE: acme4j-smime/src/main/java/org/shredzone/acme4j/smime/csr/KeyUsageType.java
type KeyUsageType (line 23) | public enum KeyUsageType {
method KeyUsageType (line 42) | KeyUsageType(int keyUsage) {
method getKeyUsageBits (line 49) | public int getKeyUsageBits() {
FILE: acme4j-smime/src/main/java/org/shredzone/acme4j/smime/csr/SMIMECSRBuilder.java
class SMIMECSRBuilder (line 64) | public class SMIMECSRBuilder {
method addEmail (line 79) | public void addEmail(InternetAddress email) {
method addEmails (line 92) | public void addEmails(Collection<InternetAddress> emails) {
method addEmails (line 102) | public void addEmails(InternetAddress... emails) {
method addIdentifier (line 112) | public void addIdentifier(Identifier id) {
method addIdentifiers (line 131) | public void addIdentifiers(Collection<Identifier> ids) {
method addIdentifiers (line 141) | public void addIdentifiers(Identifier... ids) {
method addValue (line 160) | public void addValue(String attName, String value) throws AddressExcep...
method addValue (line 180) | public void addValue(ASN1ObjectIdentifier oid, String value) throws Ad...
method setOrganization (line 193) | public void setOrganization(String o) {
method setOrganizationalUnit (line 202) | public void setOrganizationalUnit(String ou) {
method setLocality (line 211) | public void setLocality(String l) {
method setState (line 220) | public void setState(String st) {
method setCountry (line 229) | public void setCountry(String c) {
method setKeyUsageType (line 239) | public void setKeyUsageType(KeyUsageType keyUsageType) {
method sign (line 250) | public void sign(KeyPair keypair) throws IOException {
method getCSR (line 287) | public PKCS10CertificationRequest getCSR() {
method getEncoded (line 298) | public byte[] getEncoded() throws IOException {
method write (line 309) | public void write(Writer w) throws IOException {
method write (line 326) | public void write(OutputStream out) throws IOException {
method toString (line 330) | @Override
FILE: acme4j-smime/src/main/java/org/shredzone/acme4j/smime/email/EmailProcessor.java
class EmailProcessor (line 50) | public final class EmailProcessor {
method plainMessage (line 75) | public static EmailProcessor plainMessage(Message message)
method signedMessage (line 96) | public static EmailProcessor signedMessage(Message message)
method builder (line 107) | public static Builder builder() {
method EmailProcessor (line 121) | private EmailProcessor(Mail message) throws AcmeInvalidMessageException {
method expectedFrom (line 152) | public EmailProcessor expectedFrom(InternetAddress expectedSender) {
method expectedTo (line 173) | public EmailProcessor expectedTo(InternetAddress expectedRecipient) {
method expectedIdentifier (line 195) | public EmailProcessor expectedIdentifier(Identifier expectedIdentifier) {
method getSender (line 211) | public InternetAddress getSender() {
method getRecipient (line 218) | public InternetAddress getRecipient() {
method getReplyTo (line 227) | public Collection<InternetAddress> getReplyTo() {
method getMessageId (line 236) | public Optional<String> getMessageId() {
method getToken1 (line 243) | public String getToken1() {
method withChallenge (line 257) | public EmailProcessor withChallenge(EmailReply00Challenge challenge) {
method withChallenge (line 282) | public EmailProcessor withChallenge(Login login, URL challengeLocation) {
method getToken (line 291) | public String getToken() {
method getAuthorization (line 302) | public String getAuthorization() {
method respond (line 312) | public ResponseGenerator respond() {
method checkChallengePresent (line 320) | private void checkChallengePresent() {
class Builder (line 333) | public static class Builder {
method Builder (line 337) | private Builder() {
method skipVerification (line 345) | public Builder skipVerification() {
method caCerts (line 354) | public Builder caCerts() {
method trustStore (line 367) | public Builder trustStore(KeyStore trustStore) {
method certificate (line 384) | public Builder certificate(X509Certificate certificate) {
method pkixParameters (line 395) | public Builder pkixParameters(PKIXParameters param) {
method mailSession (line 407) | public Builder mailSession(Session session) {
method strict (line 416) | public Builder strict() {
method relaxed (line 425) | public Builder relaxed() {
method build (line 441) | public EmailProcessor build(Message message) throws AcmeInvalidMessa...
FILE: acme4j-smime/src/main/java/org/shredzone/acme4j/smime/email/ResponseBodyGenerator.java
type ResponseBodyGenerator (line 37) | @FunctionalInterface
method setContent (line 54) | void setContent(Message response, String responseBody) throws Messagin...
FILE: acme4j-smime/src/main/java/org/shredzone/acme4j/smime/email/ResponseGenerator.java
class ResponseGenerator (line 38) | public class ResponseGenerator {
method ResponseGenerator (line 53) | public ResponseGenerator(EmailProcessor processor) {
method withHeader (line 67) | public ResponseGenerator withHeader(@Nullable String header) {
method withFooter (line 86) | public ResponseGenerator withFooter(@Nullable String footer) {
method withGenerator (line 107) | public ResponseGenerator withGenerator(@Nullable ResponseBodyGenerator...
method generateResponse (line 120) | public Message generateResponse() throws MessagingException {
method generateResponse (line 135) | public Message generateResponse(Session session) throws MessagingExcep...
method defaultBodyGenerator (line 176) | private void defaultBodyGenerator(Message response, String responseBody)
FILE: acme4j-smime/src/main/java/org/shredzone/acme4j/smime/exception/AcmeInvalidMessageException.java
class AcmeInvalidMessageException (line 42) | public class AcmeInvalidMessageException extends AcmeException {
method AcmeInvalidMessageException (line 54) | public AcmeInvalidMessageException(String msg) {
method AcmeInvalidMessageException (line 68) | public AcmeInvalidMessageException(String msg, List<ErrorBundle> error...
method AcmeInvalidMessageException (line 81) | public AcmeInvalidMessageException(String msg, Throwable cause) {
method getErrors (line 98) | public List<ErrorBundle> getErrors() {
FILE: acme4j-smime/src/main/java/org/shredzone/acme4j/smime/wrapper/Mail.java
type Mail (line 28) | public interface Mail {
method getFrom (line 37) | InternetAddress getFrom() throws AcmeInvalidMessageException;
method getTo (line 46) | InternetAddress getTo() throws AcmeInvalidMessageException;
method getSubject (line 53) | String getSubject() throws AcmeInvalidMessageException;
method getReplyTo (line 63) | Collection<InternetAddress> getReplyTo() throws AcmeInvalidMessageExce...
method getMessageId (line 72) | Optional<String> getMessageId() throws AcmeInvalidMessageException;
method isAutoSubmitted (line 82) | boolean isAutoSubmitted() throws AcmeInvalidMessageException;
FILE: acme4j-smime/src/main/java/org/shredzone/acme4j/smime/wrapper/SignedMail.java
class SignedMail (line 51) | public class SignedMail implements Mail {
method SignedMail (line 64) | SignedMail() {
method importUntrustedHeaders (line 73) | public void importUntrustedHeaders(Enumeration<Header> en) {
method importTrustedHeaders (line 95) | public void importTrustedHeaders(Enumeration<Header> en) throws AcmeIn...
method importTrustedHeadersRelaxed (line 122) | public void importTrustedHeadersRelaxed(Enumeration<Header> en) {
method importSignatureHeaders (line 144) | public void importSignatureHeaders(SignerInformation si) throws AcmeIn...
method getFrom (line 190) | @Override
method getTo (line 199) | @Override
method getSubject (line 208) | @Override
method getMessageId (line 213) | @Override
method getReplyTo (line 222) | @Override
method isAutoSubmitted (line 245) | @Override
method getMissingSecuredHeaders (line 260) | public Set<String> getMissingSecuredHeaders() {
method checkDuplicatedField (line 283) | protected void checkDuplicatedField(String header, String value, boole...
method deleteField (line 308) | protected void deleteField(String header, String value, boolean relaxe...
method modifyField (line 328) | protected void modifyField(String header, String value, boolean relaxe...
method fetchTrustedHeader (line 348) | private String fetchTrustedHeader(String name) throws AcmeInvalidMessa...
method toString (line 369) | @Override
class MailHeader (line 381) | private static class MailHeader {
method MailHeader (line 392) | public MailHeader(String name, String value) {
method setTrusted (line 402) | public MailHeader setTrusted() {
method nameEquals (line 417) | public boolean nameEquals(@Nullable String expected, boolean relaxed) {
method valueEquals (line 439) | public boolean valueEquals(@Nullable String expected, boolean relaxe...
method toString (line 453) | @Override
FILE: acme4j-smime/src/main/java/org/shredzone/acme4j/smime/wrapper/SignedMailBuilder.java
class SignedMailBuilder (line 58) | public class SignedMailBuilder {
method withCaCertsTrustStore (line 70) | public SignedMailBuilder withCaCertsTrustStore() {
method withSignCert (line 83) | public SignedMailBuilder withSignCert(X509Certificate signCert) {
method withTrustStore (line 104) | public SignedMailBuilder withTrustStore(KeyStore trustStore)
method withPKIXParameters (line 118) | public SignedMailBuilder withPKIXParameters(PKIXParameters param) {
method withMailSession (line 130) | public SignedMailBuilder withMailSession(Session mailSession) {
method relaxed (line 145) | public SignedMailBuilder relaxed(boolean relaxed) {
method build (line 162) | public SignedMail build(Message message) throws AcmeInvalidMessageExce...
method validateSignature (line 232) | @SuppressWarnings("unchecked")
method validateSigatureSender (line 267) | @SuppressWarnings("unchecked")
method getCaCertsTrustStore (line 301) | protected static KeyStore getCaCertsTrustStore() {
FILE: acme4j-smime/src/main/java/org/shredzone/acme4j/smime/wrapper/SimpleMail.java
class SimpleMail (line 38) | public class SimpleMail implements Mail {
method SimpleMail (line 44) | public SimpleMail(Message message) {
method getFrom (line 48) | @Override
method getTo (line 67) | @Override
method getSubject (line 86) | @Override
method getReplyTo (line 99) | @Override
method getMessageId (line 115) | @Override
method isAutoSubmitted (line 131) | @Override
FILE: acme4j-smime/src/test/java/org/shredzone/acme4j/smime/EmailIdentifierTest.java
class EmailIdentifierTest (line 30) | public class EmailIdentifierTest {
method testConstants (line 32) | @Test
method testEmail (line 37) | @ParameterizedTest
method provideTestEmails (line 49) | public static Stream<Arguments> provideTestEmails() throws AddressExce...
FILE: acme4j-smime/src/test/java/org/shredzone/acme4j/smime/SMIMETests.java
class SMIMETests (line 42) | public abstract class SMIMETests {
method email (line 56) | protected InternetAddress email(String address) {
method mockMessage (line 71) | protected MimeMessage mockMessage(String name) {
method mockAccountKey (line 84) | protected KeyPair mockAccountKey() {
method mockLogin (line 97) | protected Login mockLogin() {
method mockChallenge (line 110) | protected EmailReply00Challenge mockChallenge(String name) {
method getJSON (line 121) | protected JSON getJSON(String key) {
method readCertificate (line 136) | protected X509Certificate readCertificate(String name) throws IOExcept...
FILE: acme4j-smime/src/test/java/org/shredzone/acme4j/smime/challenge/EmailReply00ChallengeTest.java
class EmailReply00ChallengeTest (line 30) | public class EmailReply00ChallengeTest extends SMIMETests {
method testCreateChallenge (line 35) | @Test
method testEmailReplyChallenge (line 47) | @Test
method testInvalidGetAuthorization (line 64) | @Test
class TestAcmeProvider (line 76) | private static class TestAcmeProvider extends AbstractAcmeProvider {
method accepts (line 77) | @Override
method resolve (line 82) | @Override
FILE: acme4j-smime/src/test/java/org/shredzone/acme4j/smime/csr/SMIMECSRBuilderTest.java
class SMIMECSRBuilderTest (line 52) | public class SMIMECSRBuilderTest {
method setup (line 56) | @BeforeAll
method testSMIMEGenerate (line 66) | @Test
method testSMIMEEncryptOnly (line 105) | @Test
method testSMIMESigningOnly (line 119) | @Test
method testSMIMESigningAndEncryption (line 133) | @Test
method testAddAttrValues (line 149) | @Test
method smimeCsrTest (line 203) | private void smimeCsrTest(PKCS10CertificationRequest csr) {
method keyUsageTest (line 251) | private void keyUsageTest(PKCS10CertificationRequest csr, Integer expe...
method writerTest (line 268) | private void writerTest(SMIMECSRBuilder builder) throws IOException {
method testNoEmail (line 304) | @Test
method testNoSign (line 315) | @Test
FILE: acme4j-smime/src/test/java/org/shredzone/acme4j/smime/email/EmailProcessorTest.java
class EmailProcessorTest (line 40) | public class EmailProcessorTest extends SMIMETests {
method setup (line 47) | @BeforeAll
method testEmailParser (line 52) | @Test
method testValidSignature (line 67) | @Test
method testInvalidSignature (line 76) | @Test
method testValidSignatureButNoSAN (line 93) | @Test
method testSANDoesNotMatchFrom (line 104) | @Test
method testInvalidProtectedFromHeader (line 118) | @Test
method testInvalidProtectedToHeader (line 132) | @Test
method testInvalidProtectedSubjectHeader (line 143) | @Test
method testNonStrictInvalidProtectedSubjectHeader (line 154) | @Test
method testValidSignatureRfc7508 (line 167) | @Disabled
method testInvalidSignatureRfc7508 (line 183) | @Disabled
method textExpectedFromFails (line 195) | @Test
method textExpectedToFails (line 205) | @Test
method textExpectedIdentifierFails1 (line 215) | @Test
method textExpectedIdentifierFails2 (line 225) | @Test
method textNoChallengeFails1 (line 235) | @Test
method textNoChallengeFails2 (line 245) | @Test
method textNoChallengeFails3 (line 255) | @Test
method testChallenge (line 265) | @Test
method testChallengeMismatch (line 276) | @Test
method testResponse (line 287) | @Test
method testResponseWithHeaderFooter (line 299) | @Test
method testResponseWithCallback (line 316) | @Test
method assertResponse (line 329) | private void assertResponse(Message response, String expectedBody)
FILE: acme4j-smime/src/test/java/org/shredzone/acme4j/smime/wrapper/SignedMailBuilderTest.java
class SignedMailBuilderTest (line 25) | public class SignedMailBuilderTest {
method testDefaultTrustStoreIsCreated (line 27) | @Test
FILE: acme4j-smime/src/test/java/org/shredzone/acme4j/smime/wrapper/SignedMailTest.java
class SignedMailTest (line 31) | public class SignedMailTest {
method testCheckDuplicatedStrictGood (line 33) | @Test
method testCheckDuplicatedStrictBad (line 46) | @Test
method testCheckDuplicatedRelaxedGood (line 59) | @Test
method testCheckDuplicatedRelaxedBad (line 72) | @Test
method testDeleteFieldStrictGood (line 85) | @Test
method testDeleteFieldStrictBad (line 100) | @Test
method testDeleteFieldRelaxedGood (line 113) | @Test
method testDeleteFieldRelaxedBad (line 128) | @Test
method testModifyFieldStrictGood (line 141) | @Test
method testModifyFieldStrictBad (line 154) | @Test
method testModifyFieldRelaxedGood (line 167) | @Test
method testModifyFieldRelaxedBad (line 180) | @Test
method testImportUntrusted (line 193) | @Test
method testImportTrustedStrict (line 209) | @Test
method testImportTrustedRelaxed (line 227) | @Test
method testImportStrictFails (line 244) | @Test
method testFromEmpty (line 255) | @Test
method testFromUntrusted (line 263) | @Test
method testFromTrusted (line 275) | @Test
method testToEmpty (line 285) | @Test
method testToUntrusted (line 293) | @Test
method testToTrusted (line 305) | @Test
method testSubjectEmpty (line 315) | @Test
method testSubjectUntrusted (line 323) | @Test
method testSubjectTrusted (line 335) | @Test
method testMessageIdEmpty (line 345) | @Test
method testMessageId (line 351) | @Test
method testReplyToEmpty (line 361) | @Test
method testReplyTo (line 367) | @Test
method testIsAutoSubmitted (line 381) | @Test
method testIsNotAutoSubmitted (line 391) | @Test
method testIsAutoSubmittedMissing (line 401) | @Test
method testMissingSecuredHeadersEmpty (line 407) | @Test
method testMissingSecuredHeadersGood (line 413) | @Test
method testMissingSecuredHeadersTrustedBad (line 425) | @Test
method testMissingSecuredHeadersUntustedBad (line 436) | @Test
method withHeaders (line 448) | private Enumeration<Header> withHeaders(String... kv) {
FILE: acme4j-smime/tool/smime-generator.py
function makebuf (line 30) | def makebuf(text):
function signmail (line 33) | def signmail(text, sender, recipient, subject, privkey, pubkey,
Condensed preview — 327 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,293K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 31,
"preview": "github: shred\nko_fi: shredzone\n"
},
{
"path": ".github/workflows/ci.yml",
"chars": 452,
"preview": "name: CI\n\non:\n pull_request:\n branches:\n - master\n push:\n branches:\n - master\n\njobs:\n build:\n runs"
},
{
"path": ".gitignore",
"chars": 7,
"preview": "target\n"
},
{
"path": ".gitlab-ci.yml",
"chars": 778,
"preview": "stages:\n - build\n - test\n\nvariables:\n DOCKER_TLS_CERTDIR: \"/certs\"\n DOCKER_HOST: tcp://docker:2376\n DOCKER_TLS_VERI"
},
{
"path": "CONTRIBUTING.md",
"chars": 2784,
"preview": "# Contributing to _acme4j_\n\nThank you for taking your time to contribute!\n\n## Acceptance Criteria\n\nThese criteria must b"
},
{
"path": "LICENSE-APL.txt",
"chars": 11358,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 4206,
"preview": "# ACME Java Client   2015 Richard \"Shred\" Kör"
},
{
"path": "acme4j-client/src/main/java/.gitignore",
"chars": 0,
"preview": ""
},
{
"path": "acme4j-client/src/main/java/module-info.java",
"chars": 1729,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2020 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/Account.java",
"chars": 12642,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/AccountBuilder.java",
"chars": 11242,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/AcmeJsonResource.java",
"chars": 5301,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2018 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/AcmeResource.java",
"chars": 2872,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/Authorization.java",
"chars": 6448,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/Certificate.java",
"chars": 12876,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/Identifier.java",
"chars": 7566,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2018 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/Login.java",
"chars": 9264,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2018 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/Metadata.java",
"chars": 6692,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/Order.java",
"chars": 14633,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2017 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/OrderBuilder.java",
"chars": 14152,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2017 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/PollableResource.java",
"chars": 3346,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2024 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java",
"chars": 5414,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2017 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/RenewalInfo.java",
"chars": 6954,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2023 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/RevocationReason.java",
"chars": 1801,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/Session.java",
"chars": 14530,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/Status.java",
"chars": 2325,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java",
"chars": 7081,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Dns01Challenge.java",
"chars": 2905,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/challenge/DnsAccount01Challenge.java",
"chars": 3296,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2025 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/challenge/DnsPersist01Challenge.java",
"chars": 9549,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2026 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Http01Challenge.java",
"chars": 1622,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsAlpn01Challenge.java",
"chars": 2923,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2018 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TokenChallenge.java",
"chars": 2651,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/challenge/package-info.java",
"chars": 1000,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2020 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/connector/Connection.java",
"chars": 7143,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java",
"chars": 22981,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2023 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/connector/HttpConnector.java",
"chars": 3357,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/connector/NetworkSettings.java",
"chars": 4023,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2019 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/connector/NonceHolder.java",
"chars": 1193,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2026 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/connector/RequestSigner.java",
"chars": 1682,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2026 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/connector/Resource.java",
"chars": 1049,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/connector/ResourceIterator.java",
"chars": 4901,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/connector/TrimmingInputStream.java",
"chars": 2773,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2018 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/connector/package-info.java",
"chars": 994,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2020 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeException.java",
"chars": 1300,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeLazyLoadingException.java",
"chars": 2641,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeNetworkException.java",
"chars": 1054,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeNotSupportedException.java",
"chars": 1143,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2023 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeProtocolException.java",
"chars": 1413,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeRateLimitedException.java",
"chars": 2466,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeServerException.java",
"chars": 1591,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeUnauthorizedException.java",
"chars": 1108,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeUserActionRequiredException.java",
"chars": 2562,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/exception/package-info.java",
"chars": 1310,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2020 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/package-info.java",
"chars": 1015,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2020 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/provider/AbstractAcmeProvider.java",
"chars": 6363,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/provider/AcmeProvider.java",
"chars": 5016,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/provider/ChallengeProvider.java",
"chars": 1100,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2021 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/provider/ChallengeType.java",
"chars": 970,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2021 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/provider/GenericAcmeProvider.java",
"chars": 1288,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/provider/actalis/ActalisAcmeProvider.java",
"chars": 2908,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2025 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/provider/actalis/package-info.java",
"chars": 1056,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2025 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/provider/google/GoogleAcmeProvider.java",
"chars": 2355,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2024 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/provider/google/package-info.java",
"chars": 1059,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2024 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProvider.java",
"chars": 2255,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/package-info.java",
"chars": 1051,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2020 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/provider/package-info.java",
"chars": 1450,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2020 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/provider/pebble/PebbleAcmeProvider.java",
"chars": 7117,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2017 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/provider/pebble/package-info.java",
"chars": 1077,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2020 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/provider/sslcom/SslComAcmeProvider.java",
"chars": 3763,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2024 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/provider/sslcom/package-info.java",
"chars": 1026,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2020 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/provider/zerossl/ZeroSSLAcmeProvider.java",
"chars": 1833,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2024 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/provider/zerossl/package-info.java",
"chars": 1028,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2024 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/AcmeUtils.java",
"chars": 15519,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/JSON.java",
"chars": 15966,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/JSONBuilder.java",
"chars": 4805,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/JoseUtils.java",
"chars": 9014,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2019 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/package-info.java",
"chars": 1039,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2020 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/util/CSRBuilder.java",
"chars": 11956,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/util/CertificateUtils.java",
"chars": 12504,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/util/KeyPairUtils.java",
"chars": 4554,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/java/org/shredzone/acme4j/util/package-info.java",
"chars": 983,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2020 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/main/resources/.gitignore",
"chars": 0,
"preview": ""
},
{
"path": "acme4j-client/src/main/resources/META-INF/services/org.shredzone.acme4j.provider.AcmeProvider",
"chars": 602,
"preview": "\n# Actalis: https://www.actalis.com/\norg.shredzone.acme4j.provider.actalis.ActalisAcmeProvider\n\n# Google Trust Services:"
},
{
"path": "acme4j-client/src/main/resources/org/shredzone/acme4j/provider/pebble/pebble.minica.pem",
"chars": 1187,
"preview": "-----BEGIN CERTIFICATE-----\nMIIDPzCCAiegAwIBAgIIU0Xm9UFdQxUwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE\nAxMVbWluaWNhIHJvb3QgY2EgNTM"
},
{
"path": "acme4j-client/src/main/resources-filtered/org/shredzone/acme4j/version.properties",
"chars": 26,
"preview": "version=${project.version}"
},
{
"path": "acme4j-client/src/test/java/.gitignore",
"chars": 0,
"preview": ""
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/AccountBuilderTest.java",
"chars": 9221,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/AccountTest.java",
"chars": 19068,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/AcmeJsonResourceTest.java",
"chars": 5536,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2018 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/AcmeResourceTest.java",
"chars": 4072,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java",
"chars": 12665,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/CertificateTest.java",
"chars": 15370,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/IdentifierTest.java",
"chars": 7478,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2018 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/LoginTest.java",
"chars": 7179,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2018 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/OrderBuilderTest.java",
"chars": 19490,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2017 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/OrderTest.java",
"chars": 13535,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2017 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/ProblemTest.java",
"chars": 4331,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2017 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/RenewalInfoTest.java",
"chars": 6742,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2023 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/SessionTest.java",
"chars": 13549,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/StatusTest.java",
"chars": 1231,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java",
"chars": 7177,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/challenge/Dns01ChallengeTest.java",
"chars": 2349,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/challenge/DnsAccount01ChallengeTest.java",
"chars": 2458,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2025 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/challenge/DnsPersist01ChallengeTest.java",
"chars": 8170,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2026 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/challenge/Http01ChallengeTest.java",
"chars": 2401,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsAlpn01ChallengeTest.java",
"chars": 3415,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2018 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TokenChallengeTest.java",
"chars": 1469,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2019 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java",
"chars": 43208,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/connector/DummyConnection.java",
"chars": 3036,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/connector/HttpConnectorTest.java",
"chars": 2276,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/connector/NetworkSettingsTest.java",
"chars": 3720,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2019 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/connector/ResourceIteratorTest.java",
"chars": 5335,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/connector/ResourceTest.java",
"chars": 1549,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/connector/SessionProviderTest.java",
"chars": 4567,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/connector/TrimmingInputStreamTest.java",
"chars": 3237,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2018 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeExceptionTest.java",
"chars": 1426,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeLazyLoadingExceptionTest.java",
"chars": 1939,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeNetworkExceptionTest.java",
"chars": 988,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeNotSupportedExceptionTest.java",
"chars": 1026,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2023 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeProtocolExceptionTest.java",
"chars": 1328,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeRateLimitedExceptionTest.java",
"chars": 2384,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeUserActionRequiredExceptionTest.java",
"chars": 2604,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java",
"chars": 13376,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/provider/GenericAcmeProviderTest.java",
"chars": 1976,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/provider/TestableConnectionProvider.java",
"chars": 5047,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/provider/actalis/ActalisAcmeProviderTest.java",
"chars": 2231,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2025 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/provider/google/GoogleAcmeProviderTest.java",
"chars": 2835,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2024 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProviderTest.java",
"chars": 2847,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/provider/pebble/PebbleAcmeProviderTest.java",
"chars": 8303,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2017 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/provider/sslcom/SslComAcmeProviderTest.java",
"chars": 3640,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2024 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/provider/zerossl/ZeroSSLAcmeProviderTest.java",
"chars": 2251,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2024 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/AcmeUtilsTest.java",
"chars": 12200,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/JSONBuilderTest.java",
"chars": 4803,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/JSONTest.java",
"chars": 12570,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2016 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/JoseUtilsTest.java",
"chars": 12884,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2019 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/TestUtils.java",
"chars": 12511,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/util/CSRBuilderTest.java",
"chars": 13698,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/util/CertificateUtilsTest.java",
"chars": 11385,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/java/org/shredzone/acme4j/util/KeyPairUtilsTest.java",
"chars": 5595,
"preview": "/*\n * acme4j - Java ACME client\n *\n * Copyright (C) 2015 Richard \"Shred\" Körber\n * http://acme4j.shredzone.org\n *\n * L"
},
{
"path": "acme4j-client/src/test/resources/.gitignore",
"chars": 0,
"preview": ""
},
{
"path": "acme4j-client/src/test/resources/META-INF/services/org.shredzone.acme4j.provider.AcmeProvider",
"chars": 144,
"preview": "# Testing\norg.shredzone.acme4j.connector.SessionProviderTest$Provider1\n\n# Testing2\norg.shredzone.acme4j.connector.Sessio"
},
{
"path": "acme4j-client/src/test/resources/ari-example-cert.pem",
"chars": 497,
"preview": "-----BEGIN CERTIFICATE-----\nMIIBQzCB66ADAgECAgUAh2VDITAKBggqhkjOPQQDAjAVMRMwEQYDVQQDEwpFeGFt\ncGxlIENBMCIYDzAwMDEwMTAxMDA"
},
{
"path": "acme4j-client/src/test/resources/cert.pem",
"chars": 2258,
"preview": "-----BEGIN CERTIFICATE-----\nMIIDFzCCAf+gAwIBAgIIYZRPVr9ji5UwDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UE\nAxMdUGViYmxlIEludGVybWVkaWF"
},
{
"path": "acme4j-client/src/test/resources/certid-cert.pem",
"chars": 1701,
"preview": "-----BEGIN CERTIFICATE-----\nMIIBQzCB66ADAgECAgUAh2VDITAKBggqhkjOPQQDAjAVMRMwEQYDVQQDEwpFeGFt\ncGxlIENBMCIYDzAwMDEwMTAxMDA"
},
{
"path": "acme4j-client/src/test/resources/json/authorizationChallenges.json",
"chars": 612,
"preview": "{\n \"challenges\": [\n {\n \"type\": \"http-01\",\n \"url\": \"https://example.com/authz/asdf/0\",\n \"token\": \"Ilir"
},
{
"path": "acme4j-client/src/test/resources/json/canceledOrderResponse.json",
"chars": 27,
"preview": "{\n \"status\": \"canceled\"\n}\n"
},
{
"path": "acme4j-client/src/test/resources/json/datatypes.json",
"chars": 511,
"preview": "{\n \"text\": \"lorem ipsum\",\n \"number\": 123,\n \"boolean\": true,\n \"uri\": \"mailto:foo@example.com\",\n \"url\": \"http://examp"
},
{
"path": "acme4j-client/src/test/resources/json/deactivateAccountResponse.json",
"chars": 30,
"preview": "{\n \"status\": \"deactivated\"\n}\n"
},
{
"path": "acme4j-client/src/test/resources/json/directory.json",
"chars": 891,
"preview": "{\n \"newNonce\": \"https://example.com/acme/new-nonce\",\n \"newAccount\": \"https://example.com/acme/new-account\",\n \"newOrde"
},
{
"path": "acme4j-client/src/test/resources/json/directoryNoMeta.json",
"chars": 163,
"preview": "{\n \"newAccount\": \"https://example.com/acme/new-account\",\n \"newAuthz\": \"https://example.com/acme/new-authz\",\n \"newOrde"
},
{
"path": "acme4j-client/src/test/resources/json/dns01Challenge.json",
"chars": 149,
"preview": "{\n \"type\": \"dns-01\",\n \"url\": \"https://example.com/acme/authz/0\",\n \"status\": \"pending\",\n \"token\": \"pNvmJivs0WCko2suV7"
},
{
"path": "acme4j-client/src/test/resources/json/dnsAccount01Challenge.json",
"chars": 171,
"preview": "{\n \"type\": \"dns-account-01\",\n \"url\": \"https://example.com/acme/chall/i00MGYwLWIx\",\n \"status\": \"pending\",\n \"token\": \""
},
{
"path": "acme4j-client/src/test/resources/json/dnsPersist01Challenge.json",
"chars": 222,
"preview": "{\n \"type\": \"dns-persist-01\",\n \"url\": \"https://ca.example/acme/authz/1234/0\",\n \"status\": \"pending\",\n \"accounturi\": \"h"
},
{
"path": "acme4j-client/src/test/resources/json/finalizeAutoRenewResponse.json",
"chars": 648,
"preview": "{\n \"status\": \"valid\",\n \"expires\": \"2015-03-01T14:09:00Z\",\n \"identifiers\": [\n {\n \"type\": \"dns\",\n \"value\":"
},
{
"path": "acme4j-client/src/test/resources/json/finalizeRequest.json",
"chars": 879,
"preview": "{\n \"csr\": \"MIIChDCCAWwCAQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCPemmumcNGR0hsPo-2"
},
{
"path": "acme4j-client/src/test/resources/json/finalizeResponse.json",
"chars": 524,
"preview": "{\n \"status\": \"valid\",\n \"expires\": \"2015-03-01T14:09:00Z\",\n \"identifiers\": [\n {\n \"type\": \"dns\",\n \"value\":"
},
{
"path": "acme4j-client/src/test/resources/json/genericChallenge.json",
"chars": 287,
"preview": "{\n \"type\": \"generic-01\",\n \"status\": \"invalid\",\n \"url\": \"http://example.com/challenge/123\",\n \"validated\": \"2015-12-12"
},
{
"path": "acme4j-client/src/test/resources/json/httpChallenge.json",
"chars": 150,
"preview": "{\n \"type\": \"http-01\",\n \"url\": \"https://example.com/acme/authz/0\",\n \"status\": \"pending\",\n \"token\": \"rSoI9JpyvFi-ltdnB"
},
{
"path": "acme4j-client/src/test/resources/json/httpNoTokenChallenge.json",
"chars": 92,
"preview": "{\n \"type\": \"http-01\",\n \"url\": \"https://example.com/acme/authz/0\",\n \"status\": \"pending\"\n}\n"
},
{
"path": "acme4j-client/src/test/resources/json/modifyAccount.json",
"chars": 114,
"preview": "{\n \"contact\": [\n \"mailto:foo@example.com\",\n \"mailto:foo2@example.com\",\n \"mailto:foo3@example.com\"\n ]\n}\n"
},
{
"path": "acme4j-client/src/test/resources/json/modifyAccountResponse.json",
"chars": 146,
"preview": "{\n \"termsOfServiceAgreed\": true,\n \"contact\": [\n \"mailto:foo@example.com\",\n \"mailto:foo2@example.com\",\n \"mailt"
},
{
"path": "acme4j-client/src/test/resources/json/newAccount.json",
"chars": 84,
"preview": "{\n \"termsOfServiceAgreed\": true,\n \"contact\": [\n \"mailto:foo@example.com\"\n ]\n}\n"
},
{
"path": "acme4j-client/src/test/resources/json/newAccountOnlyExisting.json",
"chars": 33,
"preview": "{\n \"onlyReturnExisting\": true\n}\n"
},
{
"path": "acme4j-client/src/test/resources/json/newAccountResponse.json",
"chars": 159,
"preview": "{\n \"status\": \"valid\",\n \"termsOfServiceAgreed\": true,\n \"contact\": [\n \"mailto:foo@example.com\"\n ],\n \"orders\": \"htt"
},
{
"path": "acme4j-client/src/test/resources/json/newAuthorizationRequest.json",
"chars": 72,
"preview": "{\n \"identifier\": {\n \"type\": \"dns\",\n \"value\": \"example.org\"\n }\n}\n"
},
{
"path": "acme4j-client/src/test/resources/json/newAuthorizationRequestSub.json",
"chars": 106,
"preview": "{\n \"identifier\": {\n \"type\": \"dns\",\n \"value\": \"example.org\",\n \"subdomainAuthAllowed\": true\n }\n}\n"
},
{
"path": "acme4j-client/src/test/resources/json/newAuthorizationResponse.json",
"chars": 424,
"preview": "{\n \"status\": \"pending\",\n \"identifier\": {\n \"type\": \"dns\",\n \"value\": \"example.org\"\n },\n \"challenges\": [\n {\n "
},
{
"path": "acme4j-client/src/test/resources/json/newAuthorizationResponseSub.json",
"chars": 302,
"preview": "{\n \"status\": \"pending\",\n \"identifier\": {\n \"type\": \"dns\",\n \"value\": \"example.org\"\n },\n \"challenges\": [\n {\n "
},
{
"path": "acme4j-client/src/test/resources/json/problem.json",
"chars": 668,
"preview": "{\n \"type\": \"urn:ietf:params:acme:error:malformed\",\n \"title\": \"Some of the identifiers requested were rejected\",\n \"det"
},
{
"path": "acme4j-client/src/test/resources/json/renewalInfo.json",
"chars": 179,
"preview": "{\n \"suggestedWindow\": {\n \"start\": \"2021-01-03T00:00:00Z\",\n \"end\": \"2021-01-07T00:00:00Z\"\n },\n \"explanationURL\":"
},
{
"path": "acme4j-client/src/test/resources/json/replacedCertificateRequest.json",
"chars": 162,
"preview": "{\n \"certID\": \"MFswCwYJYIZIAWUDBAIBBCCeWLRusNLb--vmWOkxm34qDjTMWkc3utIhOMoMwKDqbgQg2iiKWySZrD-6c88HMZ6vhIHZPamChLlzGHeZ7"
},
{
"path": "acme4j-client/src/test/resources/json/requestAutoRenewOrderRequest.json",
"chars": 285,
"preview": "{\n \"identifiers\": [\n {\n \"type\": \"dns\",\n \"value\": \"example.org\"\n }\n ],\n \"auto-renewal\": {\n \"start-d"
},
{
"path": "acme4j-client/src/test/resources/json/requestAutoRenewOrderResponse.json",
"chars": 523,
"preview": "{\n \"status\": \"pending\",\n \"expires\": \"2016-01-10T00:00:00Z\",\n \"identifiers\": [\n {\n \"type\": \"dns\",\n \"value"
},
{
"path": "acme4j-client/src/test/resources/json/requestCertificateRequest.json",
"chars": 880,
"preview": "{\n \"csr\": \"MIIChDCCAWwCAQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCPemmumcNGR0hsPo-2"
},
{
"path": "acme4j-client/src/test/resources/json/requestCertificateRequestWithDate.json",
"chars": 957,
"preview": "{\n \"csr\": \"MIIChDCCAWwCAQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCPemmumcNGR0hsPo-2"
},
{
"path": "acme4j-client/src/test/resources/json/requestOrderRequest.json",
"chars": 617,
"preview": "{\n \"identifiers\": [\n {\n \"type\": \"dns\",\n \"value\": \"example.com\"\n },\n {\n \"type\": \"dns\",\n \"va"
},
{
"path": "acme4j-client/src/test/resources/json/requestOrderRequestSub.json",
"chars": 214,
"preview": "{\n \"identifiers\": [\n {\n \"type\": \"dns\",\n \"value\": \"foo.bar.example.com\",\n \"ancestorDomain\": \"example.c"
},
{
"path": "acme4j-client/src/test/resources/json/requestOrderResponse.json",
"chars": 855,
"preview": "{\n \"status\": \"pending\",\n \"expires\": \"2016-01-10T00:00:00Z\",\n \"identifiers\": [\n {\n \"type\": \"dns\",\n \"value"
},
{
"path": "acme4j-client/src/test/resources/json/requestOrderResponseSub.json",
"chars": 412,
"preview": "{\n \"status\": \"pending\",\n \"expires\": \"2016-01-10T00:00:00Z\",\n \"identifiers\": [\n {\n \"type\": \"dns\",\n \"value"
},
{
"path": "acme4j-client/src/test/resources/json/requestProfileOrderRequest.json",
"chars": 113,
"preview": "{\n \"identifiers\": [\n {\n \"type\": \"dns\",\n \"value\": \"example.org\"\n }\n ],\n \"profile\": \"classic\"\n}\n"
},
{
"path": "acme4j-client/src/test/resources/json/requestProfileOrderResponse.json",
"chars": 351,
"preview": "{\n \"status\": \"pending\",\n \"expires\": \"2016-01-10T00:00:00Z\",\n \"identifiers\": [\n {\n \"type\": \"dns\",\n \"value"
},
{
"path": "acme4j-client/src/test/resources/json/requestReplacesRequest.json",
"chars": 142,
"preview": "{\n \"identifiers\": [\n {\n \"type\": \"dns\",\n \"value\": \"example.org\"\n }\n ],\n \"replaces\": \"aYhba4dGQEHhs3uEe"
},
{
"path": "acme4j-client/src/test/resources/json/requestReplacesResponse.json",
"chars": 327,
"preview": "{\n \"status\": \"pending\",\n \"expires\": \"2016-01-10T00:00:00Z\",\n \"identifiers\": [\n {\n \"type\": \"dns\",\n \"value"
},
{
"path": "acme4j-client/src/test/resources/json/revokeCertificateRequest.json",
"chars": 1084,
"preview": "{\n \"certificate\": \"MIIDFzCCAf-gAwIBAgIIYZRPVr9ji5UwDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UEAxMdUGViYmxlIEludGVybWVkaWF0ZSBDQSA2"
},
{
"path": "acme4j-client/src/test/resources/json/revokeCertificateWithReasonRequest.json",
"chars": 1099,
"preview": "{\n \"certificate\": \"MIIDFzCCAf-gAwIBAgIIYZRPVr9ji5UwDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UEAxMdUGViYmxlIEludGVybWVkaWF0ZSBDQSA2"
},
{
"path": "acme4j-client/src/test/resources/json/tlsAlpnChallenge.json",
"chars": 154,
"preview": "{\n \"type\": \"tls-alpn-01\",\n \"url\": \"https://example.com/acme/authz/0\",\n \"status\": \"pending\",\n \"token\": \"rSoI9JpyvFi-l"
},
{
"path": "acme4j-client/src/test/resources/json/triggerHttpChallenge.json",
"chars": 135,
"preview": "{\n \"type\": \"http-01\",\n \"status\": \"pending\",\n \"url\": \"https://example.com/acme/some-location\",\n \"token\": \"IlirfxKKXAs"
},
{
"path": "acme4j-client/src/test/resources/json/triggerHttpChallengeRequest.json",
"chars": 4,
"preview": "{\n}\n"
},
{
"path": "acme4j-client/src/test/resources/json/triggerHttpChallengeResponse.json",
"chars": 135,
"preview": "{\n \"type\": \"http-01\",\n \"status\": \"pending\",\n \"url\": \"https://example.com/acme/some-location\",\n \"token\": \"IlirfxKKXAs"
},
{
"path": "acme4j-client/src/test/resources/json/updateAccount.json",
"chars": 3,
"preview": "{}\n"
},
{
"path": "acme4j-client/src/test/resources/json/updateAccountResponse.json",
"chars": 894,
"preview": "{\n \"status\": \"valid\",\n \"contact\": [\n \"mailto:foo2@example.com\"\n ],\n \"termsOfServiceAgreed\": true,\n \"orders\": \"ht"
},
{
"path": "acme4j-client/src/test/resources/json/updateAuthorizationResponse.json",
"chars": 637,
"preview": "{\n \"status\": \"valid\",\n \"expires\": \"2016-01-02T17:12:40Z\",\n \"identifier\": {\n \"type\": \"dns\",\n \"value\": \"example.o"
},
{
"path": "acme4j-client/src/test/resources/json/updateAuthorizationWildcardResponse.json",
"chars": 325,
"preview": "{\n \"status\": \"valid\",\n \"expires\": \"2016-01-02T17:12:40Z\",\n \"wildcard\": true,\n \"identifier\": {\n \"type\": \"dns\",\n "
},
{
"path": "acme4j-client/src/test/resources/json/updateAutoRenewOrderResponse.json",
"chars": 220,
"preview": "{\n \"status\": \"valid\",\n \"auto-renewal\": {\n \"start-date\": \"2016-01-01T00:00:00Z\",\n \"end-date\": \"2017-01-01T00:00:0"
},
{
"path": "acme4j-client/src/test/resources/json/updateHttpChallengeResponse.json",
"chars": 246,
"preview": "{\n \"type\": \"http-01\",\n \"url\": \"https://example.com/acme/some-location\",\n \"status\": \"valid\",\n \"token\": \"IlirfxKKXAsHt"
},
{
"path": "acme4j-client/src/test/resources/json/updateOrderResponse.json",
"chars": 632,
"preview": "{\n \"status\": \"pending\",\n \"expires\": \"2015-03-01T14:09:00Z\",\n \"identifiers\": [\n {\n \"type\": \"dns\",\n \"value"
},
{
"path": "acme4j-client/src/test/resources/simplelogger.properties",
"chars": 57,
"preview": "\norg.slf4j.simpleLogger.log.org.shredzone.acme4j = debug\n"
},
{
"path": "acme4j-example/.gitignore",
"chars": 18,
"preview": "*.key\n*.crt\n*.csr\n"
}
]
// ... and 127 more files (download for full content)
About this extraction
This page contains the full source code of the shred/acme4j GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 327 files (1.2 MB), approximately 312.8k tokens, and a symbol index with 1307 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.