{"id":13781255,"url":"https://github.com/exceptionfactory/jagged","last_synced_at":"2026-01-17T16:33:03.858Z","repository":{"id":181345188,"uuid":"665817898","full_name":"exceptionfactory/jagged","owner":"exceptionfactory","description":"Java implementation of age encryption","archived":false,"fork":false,"pushed_at":"2025-09-13T20:59:05.000Z","size":431,"stargazers_count":32,"open_issues_count":0,"forks_count":4,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-09-13T22:22:56.313Z","etag":null,"topics":["age-encryption","encryption"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/exceptionfactory.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-07-13T04:32:19.000Z","updated_at":"2025-09-13T20:59:09.000Z","dependencies_parsed_at":"2023-07-15T04:34:18.145Z","dependency_job_id":"43d1b553-0309-4e19-8434-0fa36b17b221","html_url":"https://github.com/exceptionfactory/jagged","commit_stats":null,"previous_names":["exceptionfactory/jagged"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/exceptionfactory/jagged","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exceptionfactory%2Fjagged","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exceptionfactory%2Fjagged/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exceptionfactory%2Fjagged/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exceptionfactory%2Fjagged/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/exceptionfactory","download_url":"https://codeload.github.com/exceptionfactory/jagged/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exceptionfactory%2Fjagged/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28511868,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T13:38:16.342Z","status":"ssl_error","status_checked_at":"2026-01-17T13:37:44.060Z","response_time":85,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["age-encryption","encryption"],"created_at":"2024-08-03T18:01:24.308Z","updated_at":"2026-01-17T16:33:03.848Z","avatar_url":"https://github.com/exceptionfactory.png","language":"Java","funding_links":[],"categories":["Implementations"],"sub_categories":[],"readme":"# Jagged\n\nJava implementation of age encryption\n\n[![build](https://github.com/exceptionfactory/jagged/actions/workflows/build.yml/badge.svg)](https://github.com/exceptionfactory/jagged/actions/workflows/build.yml)\n[![codecov](https://codecov.io/gh/exceptionfactory/jagged/branch/main/graph/badge.svg?token=SM7PEI00HT)](https://codecov.io/gh/exceptionfactory/jagged)\n[![vulnerabilities](https://snyk.io/test/github/exceptionfactory/jagged/badge.svg)](https://snyk.io/test/github/exceptionfactory/jagged)\n[![javadoc](https://javadoc.io/badge2/com.exceptionfactory.jagged/jagged-api/javadoc.svg)](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-api)\n[![maven-central](https://img.shields.io/maven-central/v/com.exceptionfactory.jagged/jagged-api)](https://central.sonatype.com/artifact/com.exceptionfactory.jagged/jagged-api)\n[![age-encryption.org specification](https://img.shields.io/badge/age--encryption.org-v1-blueviolet)](https://age-encryption.org/v1)\n\n# Build Requirements\n\n- Java 21\n- Maven 3.9\n\n# Runtime Requirements\n\n- Java 25, 21, 17, or 11\n- Java 8 with [Bouncy Castle Security Provider](https://bouncycastle.org/docs/docs1.8on/org/bouncycastle/jce/provider/BouncyCastleProvider.html)\n\n## Java Cryptography Architecture\n\nJagged uses the\n[Java Cryptography Architecture](https://docs.oracle.com/en/java/javase/17/security/java-cryptography-architecture-jca-reference-guide.html)\nframework for the following algorithms:\n\n- `ChaCha20-Poly1305` with [javax.crypto.Cipher](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/javax/crypto/Cipher.html)\n- `HmacSHA256` with [javax.crypto.Mac](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/javax/crypto/Mac.html)\n- `PBKDF2WithHmacSHA256` with [javax.crypto.SecretKeyFactory](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/javax/crypto/SecretKeyFactory.html)\n- `RSA` with [java.security.KeyFactory](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/security/KeyFactory.html)\n- `RSA/ECB/OAEPPadding` with [javax.crypto.Cipher](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/javax/crypto/Cipher.html)\n- `X25519` with [javax.crypto.KeyAgreement](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/javax/crypto/KeyAgreement.html)\n- `X25519` with [java.security.KeyFactory](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/security/KeyFactory.html)\n- `X25519` with [java.security.KeyPairGenerator](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/security/KeyPairGenerator.html)\n\n[JEP 324](https://openjdk.org/jeps/324) introduced X25519 Key Agreement in Java 11. \n[JEP 329](https://openjdk.org/jeps/329) added ChaCha20-Poly1305 in Java 11.\n\nJagged does not require additional dependencies when running on Java 11 or higher.\n\nJagged on Java 8 requires an additional\n[Security Provider](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/security/Provider.html)\nto support X25519 and ChaCha20-Poly1305.\n\n## Bouncy Castle Security Provider\n\nThe [Bouncy Castle](https://bouncycastle.org/java.html) framework includes the\n[BouncyCastleProvider](https://bouncycastle.org/docs/docs1.8on/org/bouncycastle/jce/provider/BouncyCastleProvider.html)\nwhich can be installed to support using Jagged on Java 8.\n\nThe `jagged-x25519` library requires access to X25519 encoded keys. The default behavior of the Bouncy Castle library\nincludes the public key together with the private key in the encoded representation, which differs from the standard\nJava implementation. The Jagged library provides conversion between encoded formats.\n\n# Versioning\n\nJagged follows the [Semantic Versioning Specification 2.0.0](https://semver.org/).\n\n# Features\n\nJagged supports streaming encryption and decryption using standard recipient types.\n\n- Encryption and decryption of [binary age files](https://github.com/C2SP/C2SP/blob/main/age.md#encrypted-file-format)\n- Encryption and decryption of [armored age files](https://github.com/C2SP/C2SP/blob/main/age.md#ascii-armor)\n- [X25519](https://github.com/C2SP/C2SP/blob/main/age.md#the-x25519-recipient-type) recipients and identities\n- [scrypt](https://github.com/C2SP/C2SP/blob/main/age.md#the-scrypt-recipient-type) recipients and identities\n- [ssh-rsa](https://github.com/FiloSottile/age/blob/main/README.md#ssh-keys) recipients and identities\n- [ssh-ed25519](https://github.com/FiloSottile/age/blob/main/README.md#ssh-keys) recipients and identities\n\n# Specifications\n\nJagged supports version 1 of the [age-encryption.org](https://age-encryption.org/v1) specification.\n\nThe age encryption specification builds on a number of common cryptographic algorithms and encoding standards.\n\n## Formatting Standards\n\nFiles encrypted using the age specification include a textual\n[header](https://github.com/C2SP/C2SP/blob/main/age.md#header) and binary\n[payload](https://github.com/C2SP/C2SP/blob/main/age.md#payload).\n\nFile headers include a [message authentication code](https://github.com/C2SP/C2SP/blob/main/age.md#header-mac) computed\nusing HMAC-SHA-256.\n\n- [RFC 2104](https://www.rfc-editor.org/rfc/rfc2104.html) HMAC: Keyed-Hashing for Message Authentication\n\nFile headers include [recipient stanza](https://github.com/C2SP/C2SP/blob/main/age.md#recipient-stanza) binary body\nelements encoded using Base64 Canonical Encoding.\n\n- [RFC 4648](https://www.rfc-editor.org/rfc/rfc4648.html) The Base16, Base32, and Base64 Data Encodings\n\nFile payloads use a key derived using HKDF-SHA-256.\n\n- [RFC 5869](https://www.rfc-editor.org/rfc/rfc5869.html) HMAC-based Extract-and-Expand Key Derivation Function (HKDF)\n\nFile payload encryption uses ChaCha20-Poly1305 for as the algorithm for Authenticated Encryption with Additional Data.\n\n- [RFC 7539](https://www.rfc-editor.org/rfc/rfc7539.html) ChaCha20 and Poly1305 for IETF Protocols\n\n## Recipient Standards\n\nStandard recipient types include asymmetric encryption using \n[X25519](https://github.com/C2SP/C2SP/blob/main/age.md#the-x25519-recipient-type) and passphrase encryption using\n[scrypt](https://github.com/C2SP/C2SP/blob/main/age.md#the-scrypt-recipient-type).\n\nThe X25519 type uses Curve25519 for Elliptic Curve Diffie-Hellman shared secret key exchanges.\n\n- [RFC 7748](https://www.rfc-editor.org/rfc/rfc7748.html) Elliptic Curves for Security\n\nThe X25519 type uses Bech32 for encoding public keys and private keys.\n\n- [BIP 0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) Base32 address format\n\nThe X25519 type encrypts a File Key with ChaCha20-Poly1305 using a key derived with HKDF-SHA-256.\n\nThe scrypt type uses a passphrase and configurable work factor with other preset values to derive the key for encrypting\na File Key.\n\n- [RFC 7914](https://www.rfc-editor.org/rfc/rfc7914.html) The scrypt Password-Based Key Derivation Function\n\nThe scrypt type encrypts a File Key with ChaCha20-Poly1305.\n\nThe ssh-ed25519 and ssh-rsa types support reading private key pairs formatted using OpenSSH Private Key Version 1.\n\n- [OpenSSH PROTOCOL.key](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key)\n\nThe ssh-ed25519 type uses Curve25519 for Elliptic Curve Diffie-Hellman shared secret key exchanges based on computing\nequivalent values from keys described in the Edwards-curve Digital Signature Algorithm edwards25519.\n\n- [RFC 8032](https://www.rfc-editor.org/rfc/rfc8032) Edwards-Curve Digital Signature Algorithm\n\nThe ssh-ed25519 type reads SSH public keys encoded according to the SSH protocol.\n\n- [RFC 8709](https://www.rfc-editor.org/rfc/rfc8709) Ed25519 and Ed448 Public Key Algorithms for the Secure Shell (SSH) Protocol\n\nThe ssh-ed25519 type encrypts a File Key with ChaCha20-Poly1305.\n\nThe ssh-rsa type reads SSH public keys encoded according to the SSH protocol.\n\n- [RFC 4253](https://www.rfc-editor.org/rfc/rfc4253) The Secure Shell (SSH) Transport Layer Protocol\n\nThe ssh-rsa type encrypts a File Key with RSA-OAEP.\n\n- [RFC 8017](https://www.rfc-editor.org/rfc/rfc8017) PKCS #1: RSA Cryptography Specifications Version 2.2\n\n# Modules\n\nJagged consists of multiple modules supporting different aspects of the age encryption specification.\n\n- jagged-api\n- jagged-bech32\n- jagged-framework\n- jagged-scrypt\n- jagged-ssh\n- jagged-test\n- jagged-x25519\n\n## jagged-api\n\nThe\n[jagged-api](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-api/latest/com/exceptionfactory/jagged/package-summary.html)\nmodule contains the core public interfaces for encryption and decryption operations. The module\ncontains interfaces and classes in the `com.exceptionfactory.jagged` package, which provide integration and extension\npoints for other components.\n\nThe\n[FileKey](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-api/latest/com/exceptionfactory/jagged/FileKey.html)\nclass implements\n[java.crypto.SecretKey](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/javax/crypto/SecretKey.html)\nand supports the primary contract for age identities and recipients.\n\nThe\n[RecipientStanza](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-api/latest/com/exceptionfactory/jagged/RecipientStanza.html)\ninterface follows the pattern of the age [Stanza](https://pkg.go.dev/filippo.io/age#Stanza),\nproviding access to the Type, Arguments, and binary Body elements.\n\nThe\n[RecipientStanzaReader](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-api/latest/com/exceptionfactory/jagged/RecipientStanzaReader.html)\ninterface serves as the age [Identity](https://pkg.go.dev/filippo.io/age#Identity) \nabstraction, responsible for reading `RecipientStanza` objects and return a decrypted `FileKey`.\n\nThe \n[RecipientStanzaWriter](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-api/latest/com/exceptionfactory/jagged/RecipientStanzaWriter.html)\ninterface follows the age [Recipient](https://pkg.go.dev/filippo.io/age#Recipient)\nabstraction, responsible for wrapping a `FileKey` and returning a collection of `RecipientStanza` objects.\n\nThe\n[EncryptingChannelFactory](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-api/latest/com/exceptionfactory/jagged/EncryptingChannelFactory.html)\ninterface wraps a provided\n[WritableByteChannel](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/nio/channels/WritableByteChannel.html) and returns\na `WritableByteChannel` that supports streaming encryption to one or more recipients based on supplied\n`RecipientStanzaWriter` instances.\n\nThe\n[DecryptingChannelFactory](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-api/latest/com/exceptionfactory/jagged/DecryptingChannelFactory.html)\ninterface wraps a provided\n[ReadableByteChannel](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/nio/channels/ReadableByteChannel.html) and returns\na `ReadableByteChannel` that supports streaming decryption for a matched identity based on supplied\n`RecipientStanzaReader` instances.\n\n## jagged-bech32\n\nThe \n[jagged-bech32](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-bech32/latest/com/exceptionfactory/jagged/bech32/package-summary.html)\nmodule contains an implementation of the Bech32 encoding specification defined according to\n[Bitcoin Improvement Proposal 0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki). Bech32 encoding\nsupports a standard representation of X25519 private and public keys. The\n[Bech32](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-bech32/latest/com/exceptionfactory/jagged/bech32/Bech32.html)\nclass follows the pattern of\n[java.util.Base64](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Base64.html) and encloses\n[Bech32.Decoder](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-bech32/latest/com/exceptionfactory/jagged/bech32/Bech32.Decoder.html)\nand\n[Bech32.Encoder](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-bech32/latest/com/exceptionfactory/jagged/bech32/Bech32.Encoder.html)\ninterfaces. Bech32 encoding consists of a Human-Readable Part prefix, a separator, and data part that\nends with a checksum.\n\n## jagged-framework\n\nThe \n[jagged-framework](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-framework/latest/index.html)\nmodule includes shared components for common cryptographic operations.\n\nThe `stream` package includes the\n[StandardDecryptingChannelFactory](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-framework/latest/com/exceptionfactory/jagged/framework/stream/StandardDecryptingChannelFactory.html)\nand\n[StandardEncryptingChannelFactory](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-framework/latest/com/exceptionfactory/jagged/framework/stream/StandardEncryptingChannelFactory.html)\nclasses,\nwhich implement the corresponding public interfaces for streaming cipher operations.\n\nThe `armor` packaged includes the\n[ArmoredDecryptingChannelFactory](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-framework/latest/com/exceptionfactory/jagged/framework/armor/ArmoredDecryptingChannelFactory.html)\nand\n[ArmoredEncryptingChannelFactory](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-framework/latest/com/exceptionfactory/jagged/framework/armor/ArmoredEncryptingChannelFactory.html)\nclasses,\nsupporting reading and writing ASCII armored files with standard PEM header and footer lines.\n\n## jagged-scrypt\n\nThe\n[jagged-scrypt](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-scrypt/latest/com/exceptionfactory/jagged/scrypt/package-summary.html)\nmodule supports encryption and decryption using a passphrase and configurable work factor.\n\nThe\n[ScryptRecipientStanzaReaderFactory](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-scrypt/latest/com/exceptionfactory/jagged/scrypt/ScryptRecipientStanzaReaderFactory.html)\ncreates instances of `RecipientStanzaReader` using a passphrase.\n\nThe\n[ScryptRecipientStanzaWriterFactory](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-scrypt/latest/com/exceptionfactory/jagged/scrypt/ScryptRecipientStanzaWriterFactory.html)\ncreates instances of `RecipientStanzaWriter` using a passphrase and \na work factor with a minimum value of 2 and a maximum value of 20.\n\nThe module includes a custom implementation of the scrypt key derivation function with predefined settings that\nmatch age encryption scrypt recipient specifications.\n\n## jagged-ssh\n\nThe\n[jagged-ssh](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-ssh/latest/com/exceptionfactory/jagged/ssh/package-summary.html)\nmodule supports encryption and decryption using public and private SSH key pairs. The SSH key pair\nimplementation is compatible with the [agessh](https://pkg.go.dev/filippo.io/age/agessh) package, which defines\nrecipient stanzas with an algorithm and an encoded fingerprint of the public key.\n\nThe\n[SshEd25519RecipientStanzaReaderFactory](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-ssh/latest/com/exceptionfactory/jagged/ssh/SshEd25519RecipientStanzaReaderFactory.html)\ncreates instances of `RecipientStanzaReader` using an\n[OpenSSH Version 1 Private Key](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key).\n\nThe\n[SshEd25519RecipientStanzaWriterFactory](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-ssh/latest/com/exceptionfactory/jagged/ssh/SshEd25519RecipientStanzaWriterFactory.html)\ncreates instances of `RecipientStanzaWriter` using an SSH Ed25519 public\nkey encoded according to [RFC 8709 Section 4](https://www.rfc-editor.org/rfc/rfc8709#name-public-key-format).\n\nThe\n[SshRsaRecipientStanzaReaderFactory](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-ssh/latest/com/exceptionfactory/jagged/ssh/SshRsaRecipientStanzaReaderFactory.html)\ncreates instances of `RecipientStanzaReader` using an RSA private key or an\n[OpenSSH Version 1 Private Key](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key).\n\nThe\n[SshRsaRecipientStanzaWriterFactory](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-ssh/latest/com/exceptionfactory/jagged/ssh/SshRsaRecipientStanzaWriterFactory.html)\ncreates instances of `RecipientStanzaWriter` using an RSA public key or an SSH\nRSA public key encoded according to [RFC 4253 Section 6.6](https://www.rfc-editor.org/rfc/rfc4253#section-6.6).\n\nThe SSH Ed25519 implementation uses Elliptic Curve Diffie-Hellman with Curve25519 as defined in\n[RFC 7748 Section 6.1](https://www.rfc-editor.org/rfc/rfc7748.html#section-6.1). As integrated in the age reference\nimplementation, the SSH Ed25519 implementation converts the public key coordinate from the twisted Edwards curve to the\ncorresponding coordinate on the Montgomery curve according to the birational maps described in\n[RFC 7748 Section 4.1](https://www.rfc-editor.org/rfc/rfc7748#section-4.1). The implementation converts the Ed25519\nprivate key seed to the corresponding X25519 private key using the first 32 bytes of an `SHA-512` hash of the seed.\nThe SSH Ed25519 implementation uses ChaCha20-Poly1305 for encrypting and decrypting File Keys.\n\nThe SSH RSA implementation uses Optimal Asymmetric Encryption Padding as defined in\n[RFC 8017 Section 7.1](https://www.rfc-editor.org/rfc/rfc8017#section-7.1). Following the age implementation, RSA OAEP\ncipher operations use `SHA-256` as the hash algorithm with the mask generation function.\n\n## jagged-x25519\n\nThe\n[jagged-x25519](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-x25519/latest/com/exceptionfactory/jagged/x25519/package-summary.html)\nmodule supports encryption and decryption using public and private key pairs. Key generation and\nkey agreement functions use the Java Cryptography Architecture framework. Key encoding and decoding functions use the\n`jagged-bech32` library.\n\nThe\n[X25519KeyFactory](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-x25519/latest/com/exceptionfactory/jagged/x25519/X25519KeyFactory.html)\nclass implements\n[java.security.KeyFactory](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/security/KeyFactory.html)\nand supports translating an encoded X25519 private key to the corresponding X25519 public key. The `translateKey` method\naccepts an instance of the\n[javax.crypto.spec.SecretKeySpec](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/javax/crypto/spec/SecretKeySpec.html)\nclass. The `SecretKeySpec` must be constructed with the `key` byte array containing the encoded private key, and with\n`X25519` set as the value of the `algorithm` argument.\n\nThe \n[X25519KeyPairGenerator](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-x25519/latest/com/exceptionfactory/jagged/x25519/X25519KeyPairGenerator.html)\nclass implements\n[java.security.KeyPairGenerator](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/security/KeyPairGenerator.html)\nand returns public and private key pairs encoded using Bech32.\n\nThe\n[X25519RecipientStanzaReaderFactory](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-x25519/latest/com/exceptionfactory/jagged/x25519/X25519RecipientStanzaReaderFactory.html)\ncreates instances of `RecipientStanzaReader` using a private key encoded using\nBech32. Encoded private keys begin with `AGE-SECRET-KEY-1` as the Bech32 Human-Readable Part and separator.\n\nThe\n[X25519RecipientStanzaWriterFactory](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-x25519/latest/com/exceptionfactory/jagged/x25519/X25519RecipientStanzaWriterFactory.html)\ncreates instances of `RecipientStanzaWriter` using a public key encoded using\nBech32. Encoded public keys begin with `age1` as the Bech32 Human-Readable Part and separator.\n\n## jagged-test\n\nThe `jagged-test` module includes framework tests for [age test vectors](https://github.com/C2SP/CCTV/tree/main/age)\ndefined in the [Community Cryptography Test Vectors](https://github.com/C2SP/CCTV) project. The\n`CommunityCryptographyTest` runs a test method for each file in the test data directory. The `FrameworkTest` class\nexercises binary and armored encryption and decryption methods using supported recipient types.\n\n# Building\n\nRun the following Maven command to build the libraries:\n\n```\n./mvnw clean install\n```\n\n## Code Quality\n\nJagged uses the following build plugins and services to evaluate code quality:\n\n- [Apache Maven Checkstyle Plugin](https://maven.apache.org/plugins/maven-checkstyle-plugin/)\n- [Apache Maven PMD Plugin](https://maven.apache.org/plugins/maven-pmd-plugin/)\n- [Codecov](https://about.codecov.io/)\n- [GitHub CodeQL](https://codeql.github.com/)\n- [JaCoCo Maven Plugin](https://www.jacoco.org/jacoco/trunk/doc/maven.html)\n- [SpotBugs Maven Plugin](https://spotbugs.github.io/spotbugs-maven-plugin/)\n\n# Integrating\n\nJagged supports streaming encryption and decryption using\n[Java NIO](https://docs.oracle.com/en/java/javase/17/core/java-nio.html) buffers and channels. Java NIO supports\nefficient file read and write operations, minimizing memory impact using instances of\n[java.nio.ByteBuffer](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/nio/ByteBuffer.html) to process\nbytes. The\n[java.nio.channel.Channels](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/nio/channels/Channels.html)\nclass provides several methods supporting interoperation with Java IO streams.\n\nThe [X25519](https://github.com/C2SP/C2SP/blob/main/age.md#the-x25519-recipient-type) recipient type with\n[binary](https://github.com/C2SP/C2SP/blob/main/age.md#encrypted-file-format) formatting provides the\noptimal solution for integrating age encryption. X25519 public and private keys encoded using Bech32 avoid the cost of\npassword-based key derivation, and binary formatting for encrypted files does not have the overhead of armored Base64\nencoding and decoding.\n\n## X25519 Key Pair Generation\n\nJagged supports public and private keys produced using the [age-keygen](https://filippo.io/age/age-keygen.1) command and\nalso provides key pair generation using the\n[X25519KeyPairGenerator](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-x25519/latest/com/exceptionfactory/jagged/x25519/X25519KeyPairGenerator.html)\nclass. The class implements\n[KeyPairGenerator](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/security/KeyPairGenerator.html) and\nsupports standard methods for generating\n[KeyPair](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/security/KeyPair.html) instances. Both\n[PublicKey](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/security/PublicKey.html) and\n[PrivateKey](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/security/PrivateKey.html) implementations\nreturn Bech32 encoded representations following the age specification.\n\n```\nfinal KeyPairGenerator keyPairGenerator = new X25519KeyPairGenerator();\nfinal KeyPair keyPair = keyPairGenerator.generateKeyPair();\nfinal PublicKey publicKey = keyPair.getPublic();\nSystem.out.printf(\"Public key: %s\", publicKey);\n```\n\n## Binary File Encryption with X25519\n\nEncryption operations require one or more X25519 public keys. Jagged provides the \n[X25519RecipientStanzaWriterFactory](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-x25519/latest/com/exceptionfactory/jagged/x25519/X25519RecipientStanzaWriterFactory.html)\nclass for creating instances of\n[RecpientStanzaWriter](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-api/latest/com/exceptionfactory/jagged/RecipientStanzaWriter.html)\nto support encryption operations. The factory class accepts a\nstandard Java String containing a Bech32 encoded public key starting with `age1` and also supports other implementations\nof [CharSequence](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/CharSequence.html) to provide\nmore control over encoded keys.\n\nThe\n[java.nio.file.Path](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/nio/file/Path.html) class\nrepresents file locations and enables creation of\n[java.nio.Channel](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/nio/channels/Channel.html) objects\nfor reading input files and writing encrypted output files.\n\n```\nfinal CharSequence publicKey = getPublicKey();\nfinal RecipientStanzaWriter stanzaWriter = X25519RecipientStanzaWriterFactory.newRecipientStanzaWriter(publicKey);\nfinal EncryptingChannelFactory channelFactory = new StandardEncryptingChannelFactory();\n\nfinal Path inputPath = getInputPath();\nfinal Path outputPath = getOutputPath();\ntry (\n    final ReadableByteChannel inputChannel = Files.newByteChannel(inputPath);\n    final WritableByteChannel encryptingChannel = channelFactory.newEncryptingChannel(\n        Files.newByteChannel(outputPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE),\n        Collections.singletonList(stanzaWriter)\n    );\n) {\n    copy(inputChannel, encryptingChannel);\n}\n```\n\n## Binary File Decryption with X25519\n\nDecryption operations require a private key corresponding to a recipient from the age file header. Jagged provides the\n[X25519RecipientStanzaReaderFactory](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-x25519/latest/com/exceptionfactory/jagged/x25519/X25519RecipientStanzaReaderFactory.html)\nclass for creating instances of\n[RecipientStanzaReader](https://javadoc.io/doc/com.exceptionfactory.jagged/jagged-api/latest/com/exceptionfactory/jagged/RecipientStanzaReader.html)\nto support decryption\noperations. The factory class accepts a Bech32 encoded private key starting with `AGE-SECRET-KEY-1` represented as a\nJava String or sequence of characters.\n\n```\nfinal CharSequence privateKey = getPrivateKey();\nfinal RecipientStanzaReader stanzaReader = X25519RecipientStanzaReaderFactory.newRecipientStanzaReader(privateKey);\nfinal DecryptingChannelFactory channelFactory = new StandardDecryptingChannelFactory();\n\nfinal Path inputPath = getInputPath();\nfinal Path outputPath = getOutputPath();\ntry (\n    final WritableByteChannel outputChannel = Files.newByteChannel(\n        outputPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE\n    );\n    final ReadableByteChannel decryptingChannel = channelFactory.newDecryptingChannel(\n        Files.newByteChannel(inputPath),\n        Collections.singletonList(stanzaReader)\n    );\n) {\n    copy(decryptingChannel, outputChannel);\n}\n```\n\n## Channel Processing\n\nThe age specification defines the encrypted [binary payload](https://github.com/C2SP/C2SP/blob/main/age.md#payload) as\nconsisting of chunks containing 64 kilobytes. Allocating a `ByteBuffer` with a capacity of `65536` enables integrating\ncomponents to process chunks with an optimal number of method invocations. Transferring bytes from a\n[ReadableByteChannel](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/nio/channels/ReadableByteChannel.html)\nto a\n[WritableByteChannel](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/nio/channels/WritableByteChannel.html)\nrequires iterative processing to avoid partial reads or writes.\n\n```\nvoid copy(\n    final ReadableByteChannel inputChannel,\n    final WritableByteChannel outputChannel\n) throws IOException {\n    final ByteBuffer buffer = ByteBuffer.allocate(65536);\n    while (inputChannel.read(buffer) != -1) {\n        buffer.flip();\n        while (buffer.hasRemaining()) {\n            outputChannel.write(buffer);\n        }\n        buffer.clear();\n    }\n}\n```\n\n# Licensing\n\nJagged is released under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexceptionfactory%2Fjagged","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fexceptionfactory%2Fjagged","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexceptionfactory%2Fjagged/lists"}