Module: Ksef::Crypto
- Defined in:
- lib/ksef/crypto.rb,
lib/ksef/crypto/digest.rb,
lib/ksef/crypto/encryptor.rb,
lib/ksef/crypto/certificate.rb,
lib/ksef/crypto/public_keys.rb
Overview
Encryption of the payloads KSeF requires to be encrypted (docs/REFERENCE.md §10).
Every parameter here is ledgered, not chosen. They come from
sesja-interaktywna.md and uwierzytelnianie.md §2.2 — first-tier documentation —
corroborated against both reference clients' cryptography services. Do not adjust one
without re-reading §10 first; a mismatch here does not fail loudly, it produces a
payload KSeF silently cannot decrypt.
What lives where
This module holds the primitives: the algorithm identifiers, base64, SHA-256 and the one RSA operation. Certificate and PublicKeys deal with the keys the Ministry publishes, Encryptor with a session's symmetric key, and Digest with the integrity metadata that travels beside every payload.
Nothing here knows an API field name. Mapping to EncryptionInfo and
SendInvoiceRequest is Encryptor's single concession to the wire format,
and building the requests themselves belongs to the session layer.
Defined Under Namespace
Classes: Certificate, Digest, Encryptor, PublicKeys
Constant Summary collapse
- CIPHER =
AES-256-CBC with PKCS#7 padding: 256-bit key, 128-bit IV, 128-bit block (§10.1). The Java client names the same thing
AES/CBC/PKCS5Padding; for a 16-byte block PKCS#5 and PKCS#7 are identical, so that is not a divergence. "aes-256-cbc"- KEY_BYTES =
32- IV_BYTES =
16- BLOCK_BYTES =
16- OAEP =
RSAES-OAEP with SHA-256 and MGF1-SHA-256 (§10.1), used for both the symmetric key wrap and the KSeF-token payload of §4.5.
All three options have to be stated.
OpenSSL::PKey::RSA#public_encryptcannot express an MGF1 digest at all, and OpenSSL's default MGF1 digest is SHA-1 — so settingrsa_oaep_mdalone yields OAEP-SHA256-with-MGF1-SHA1, which is a different scheme that KSeF cannot unwrap. It fails at the far end, not here. { rsa_padding_mode: "oaep", rsa_oaep_md: "sha256", rsa_mgf1_md: "sha256" }.freeze
- MAX_OAEP_PLAINTEXT_BYTES =
Longest plaintext RSAES-OAEP can carry for a 2048-bit key:
k - 2*hLen - 2, so256 - 64 - 2. Stated because it pins the digest — with SHA-1 the figure would be 214 — and because it is the reason both encrypted payloads here are small ones. 190
Class Method Summary collapse
-
.decode(text) ⇒ Object
Lenient base64 for everything it receives, which is the asymmetry that matters:
"m0"raises on a line break, and how a server wraps a long value is its business. -
.encode(bytes) ⇒ Object
Strict base64, no line breaks, for everything this gem sends.
-
.rsa_encrypt(plaintext, public_key) ⇒ String
Raw ciphertext, one RSA block wide.
-
.sha256(bytes) ⇒ String
The raw 32-byte digest; Digest is the base64-and-size form.
Class Method Details
.decode(text) ⇒ Object
Lenient base64 for everything it receives, which is the asymmetry that matters:
"m0" raises on a line break, and how a server wraps a long value is its business.
Nothing is silently accepted as a result — a certificate that decodes to garbage
fails in OpenSSL::X509::Certificate.new, with a better message than we would give.
56 |
# File 'lib/ksef/crypto.rb', line 56 def decode(text) = text.to_s.unpack1("m") |
.encode(bytes) ⇒ Object
Strict base64, no line breaks, for everything this gem sends.
50 |
# File 'lib/ksef/crypto.rb', line 50 def encode(bytes) = [bytes].pack("m0") |
.rsa_encrypt(plaintext, public_key) ⇒ String
Returns raw ciphertext, one RSA block wide.
63 |
# File 'lib/ksef/crypto.rb', line 63 def rsa_encrypt(plaintext, public_key) = public_key.encrypt(plaintext, OAEP) |
.sha256(bytes) ⇒ String
Returns the raw 32-byte digest; Digest is the base64-and-size form.
59 |
# File 'lib/ksef/crypto.rb', line 59 def sha256(bytes) = OpenSSL::Digest::SHA256.digest(bytes) |