PKCS 1
PKCS #1, formally known as Public-Key Cryptography Standards #1, is a cryptographic standard that provides specifications for implementing public-key cryptography based on the RSA algorithm, including cryptographic primitives for encryption and signatures, specific encryption and signature schemes with padding, and ASN.1 syntax for RSA keys and related structures.[1]
Originally developed by RSA Laboratories as part of their Public-Key Cryptography Standards (PKCS) series, PKCS #1 was first released in version 1.0 in 1991 to promote interoperability in RSA-based systems, such as digital signatures and data encryption.[2] Subsequent revisions addressed security improvements and compatibility; version 1.5 was published as RFC 2313 in 1998, version 2.0 as RFC 2437 later that year introducing optimal asymmetric encryption padding (OAEP), version 2.1 as RFC 3447 in 2003 adding probabilistic signature scheme (PSS), and the current version 2.2 republished by the IETF as RFC 8017 in November 2016 to transfer maintenance while supporting updated hash functions like SHA-224 and SHA-512 variants per FIPS 180-4.[1][3]
The standard defines essential components such as RSA public keys (consisting of modulus n and exponent e) and private keys (including the private exponent d and optional prime factors), along with raw primitives like RSA encryption primitive (RSAEP) and signature primitive (RSASP1).[1] For encryption, it mandates RSAES-OAEP for new implementations due to its provable security against chosen-ciphertext attacks, while retaining the legacy RSAES-PKCS1-v1_5 for backward compatibility; similarly, RSASSA-PSS is recommended for signatures with its security reduction to the random oracle model, alongside RSASSA-PKCS1-v1_5.[1] Widely used in protocols like X.509 certificates, PKCS #7, and secure communications, PKCS #1 ensures robust RSA deployments but notes that older schemes like PKCS1-v1_5 may be vulnerable to padding oracle attacks if not implemented carefully.[1]
Overview
Purpose and Development
PKCS#1, also known as Public-Key Cryptography Standard #1, is a specification developed to define formats for RSA public and private keys, along with cryptographic primitives for encryption and digital signatures based on the RSA algorithm.[4] It forms part of the broader PKCS series aimed at promoting consistent implementations of public-key cryptography.[3]
The standard originated from efforts by RSA Laboratories, which initiated the PKCS series in 1991 through meetings with early adopters of public-key technology.[4] Initial internal versions (1.0 through 1.3) were distributed to participants in these meetings between 1991 and 1993, while the first public release, version 1.4, occurred on June 3, 1991, as part of preliminary materials for a workshop.[5] Version 1.5 followed in November 1993 and was formalized as RFC 2313 by the IETF in March 1998.[6] Subsequent versions addressed evolving needs: version 2.0 in October 1998 (RFC 2437) introduced optimal asymmetric encryption padding (OAEP), version 2.1 in February 2003 (RFC 3447) added probabilistic signature schemes (PSS) and multi-prime RSA support, and version 2.2 in November 2016 (RFC 8017) incorporated additional hash functions while transferring change control to the IETF, obsoleting prior RFCs.[4][3][7]
The core objectives of PKCS#1 center on standardizing RSA usage to facilitate interoperability across diverse systems and vendors, while bolstering security against known weaknesses in unpadded or "textbook" RSA, such as vulnerability to chosen-ciphertext attacks.[4] By mandating padding schemes and encoding rules, the standard mitigates risks like deterministic encryption flaws and ensures resistance to common cryptanalytic threats, evolving over versions to incorporate proven secure primitives as cryptographic research advanced.[4] This focus has made PKCS#1 a foundational reference for RSA implementations in protocols like TLS and secure messaging systems.[4]
Scope and Relation to RSA
PKCS#1 specifies standardized formats, primitives, and schemes for implementing public-key cryptography exclusively using the RSA algorithm. It defines Abstract Syntax Notation One (ASN.1) structures in Distinguished Encoding Rules (DER) for RSA public and private keys, including the representation of key components such as the modulus and exponents. Additionally, it outlines basic cryptographic primitives for RSA encryption and decryption operations, as well as for signing and verification processes, and provides specific encryption and signature schemes that incorporate these primitives. However, PKCS#1 excludes procedures for RSA key generation, which are addressed in the more general private-key information syntax defined in PKCS#8.[4]
PKCS#1 extends the foundational RSA algorithm by standardizing secure constructions that mitigate vulnerabilities inherent in direct application of RSA's core operation—modular exponentiation for public-key encryption and decryption. While RSA relies on the computational difficulty of integer factorization to ensure security, PKCS#1 mandates the use of probabilistic padding mechanisms in its schemes to prevent deterministic attacks, such as chosen-ciphertext attacks that could otherwise compromise confidentiality or authenticity. This relation positions PKCS#1 as a practical framework for deploying RSA in real-world systems, transforming the theoretical algorithm into robust, implementation-ready protocols without altering its underlying mathematical basis.[4]
Beyond its RSA-centric focus, PKCS#1 deliberately limits its scope to avoid overlap with other aspects of public-key infrastructure. It does not address key exchange protocols, which are covered in PKCS#3 for Diffie-Hellman-based agreements, nor does it include mechanisms for certificate requests or certification processes, as specified in PKCS#10. Furthermore, PKCS#1 applies solely to RSA and excludes support for alternative public-key algorithms, such as elliptic curve cryptography (ECC), ensuring specialized but non-generalized guidance for RSA deployments.[8]
Public Key Encoding
In PKCS#1, the RSA public key is encoded using Abstract Syntax Notation One (ASN.1) to ensure a standardized, interoperable representation suitable for cryptographic operations such as encryption and signature verification. The structure is defined as a SEQUENCE containing two INTEGER components: the modulus n and the public exponent e. This format, known as RSAPublicKey, employs the Distinguished Encoding Rules (DER) for binary serialization, which produces a deterministic output from the ASN.1 description.[9]
The modulus n is the product of at least two distinct odd primes, forming a positive integer that serves as the basis for the RSA domain, with typical lengths ranging from 2048 to 4096 bits to provide adequate security against factorization attacks. The public exponent e is an integer satisfying $3 \leq e < n and coprime to \lambda(n), where \lambda(n) is the Carmichael function of n; the value 65537 ($2^{16} + 1) is commonly used due to its efficiency in modular exponentiation and resistance to certain attacks. Integers are represented in big-endian byte order, with the most significant byte first, and no additional padding is applied within the key encoding itself to maintain compactness.[10]
This encoding facilitates the export of public keys for integration into X.509 certificates or direct use in cryptographic primitives defined elsewhere in PKCS#1. For instance, a 512-bit RSAPublicKey with modulus n = 0x0A66791DC6988168DE7AB77419BB7FB0C001C62710270075142942E19A8D8C51D053B3E3782A1DE5DC5AF4EBE99468170114A1DFE67CDC9A9AF55D655620BBAB and exponent e = 65537 yields the following DER-encoded byte sequence (in hexadecimal):
30 47 02 40 0A 66 79 1D C6 98 81 68 DE 7A B7 74 19 BB 7F B0
C0 01 C6 27 10 27 00 75 14 29 42 E1 9A 8D 8C 51 D0 53 B3 E3
78 2A 1D E5 DC 5A F4 EB E9 94 68 17 01 14 A1 DF E6 7C DC 9A
9A F5 5D 65 56 20 BB AB 02 03 01 00 01
30 47 02 40 0A 66 79 1D C6 98 81 68 DE 7A B7 74 19 BB 7F B0
C0 01 C6 27 10 27 00 75 14 29 42 E1 9A 8D 8C 51 D0 53 B3 E3
78 2A 1D E5 DC 5A F4 EB E9 94 68 17 01 14 A1 DF E6 7C DC 9A
9A F5 5D 65 56 20 BB AB 02 03 01 00 01
This 73-byte structure begins with the SEQUENCE tag (0x30), followed by the length, then the modulus (tagged 0x02, 64 bytes), and the exponent (tagged 0x02, 3 bytes).[11]
Private Key Encoding
The private key encoding in PKCS#1 specifies an ASN.1 structure for representing RSA private keys, enabling secure storage and transmission while supporting efficient cryptographic operations. This format encapsulates both the essential secret components required for RSA computations and optional auxiliary parameters that optimize performance without compromising security.[12]
The ASN.1 type for an RSA private key is defined as:
RSAPrivateKey ::= SEQUENCE {
version Version,
modulus INTEGER, -- n
publicExponent INTEGER, -- e
privateExponent INTEGER, -- d
prime1 INTEGER, -- p
prime2 INTEGER, -- q
exponent1 INTEGER, -- d mod (p-1)
exponent2 INTEGER, -- d mod (q-1)
coefficient INTEGER, -- q^{-1} mod p
otherPrimeInfos OtherPrimeInfos OPTIONAL
}
RSAPrivateKey ::= SEQUENCE {
version Version,
modulus INTEGER, -- n
publicExponent INTEGER, -- e
privateExponent INTEGER, -- d
prime1 INTEGER, -- p
prime2 INTEGER, -- q
exponent1 INTEGER, -- d mod (p-1)
exponent2 INTEGER, -- d mod (q-1)
coefficient INTEGER, -- q^{-1} mod p
otherPrimeInfos OtherPrimeInfos OPTIONAL
}
Here, Version ::= INTEGER { two-prime(0), multi(1) }, where version 0 indicates a two-prime RSA key (the default case) and omits otherPrimeInfos, while version 1 supports multi-prime RSA with additional prime factors specified in the optional field.[12]
The core components include the modulus n, which is the product of two distinct primes p (prime1) and q (prime2) such that n = p \cdot q; the public exponent e; and the private exponent d, a positive integer less than n satisfying e \cdot d \equiv 1 \pmod{\lambda(n)}, where \lambda(n) is the Carmichael totient function of n. These elements form the foundational secret material for RSA decryption and signing operations.[12][13]
To enhance computational efficiency, particularly for decryption, the structure incorporates Chinese Remainder Theorem (CRT) parameters: exponent1 as d \mod (p-1), exponent2 as d \mod (q-1), and coefficient as the modular inverse q^{-1} \mod p, allowing faster reduction modulo n by operating separately modulo p and q. For multi-prime cases (version 1), otherPrimeInfos extends this with sequences of additional primes r_i, their corresponding exponents d_i, and coefficients t_i, maintaining compatibility with two-prime optimizations.[12]
Private keys in this encoding must be rigorously protected against unauthorized disclosure or modification, as exposure of components like d, p, or q could enable factorization of n or direct computation of plaintexts and signatures; the standardized format facilitates secure handling in protocols like PKCS#8 for key wrapping and PKCS#12 for storage.[12][14]
Cryptographic Primitives
Basic RSA Functions
PKCS#1 defines the foundational RSA primitives as low-level building blocks for secure cryptographic schemes, consisting of basic encryption and signature operations performed modulo the RSA modulus. These primitives operate on integer representatives and rely on the RSA trapdoor one-way function, where the public key is the pair (n, e) and the private key includes the exponent d. Here, n is the RSA modulus, the product of two distinct odd primes p and q (with n at least 1024 bits long as specified in the standard, though at least 2048 bits is recommended for adequate security as of 2025)[1][15]; e is the public exponent, an integer satisfying 2 < e < n and gcd(e, λ(n)) = 1, where λ(n) is the Carmichael totient function λ(n) = lcm(p-1, q-1); and d is the private exponent, a positive integer less than n such that e · d ≡ 1 (mod λ(n)).[1]
The encryption primitive, known as RSA Encryption Primitive (RSAEP), computes the ciphertext from a message representative. Specifically, given the public key (n, e) and an integer message representative m where 0 ≤ m < n, the ciphertext c is calculated as:
c = m^e \mod n
The output c satisfies 0 ≤ c < n. The corresponding decryption primitive, RSA Decryption Primitive (RSADP), recovers the message using the private key. Given the private key (which includes n and d) and ciphertext c where 0 ≤ c < n, the message m is:
m = c^d \mod n
with 0 ≤ m < n, assuming correct key usage and no errors in computation. These operations form the core of RSA-based encryption but are deterministic and vulnerable to attacks if used without additional encoding.[1]
For digital signatures, PKCS#1 specifies primitives that operate on a message representative m, typically derived from hashing the original message to ensure fixed length and security properties. The signature primitive, RSA Signature Primitive (RSASP1), generates the signature using the private key: given (n, d) and m where 0 ≤ m < n, the signature s is:
s = m^d \mod n
with 0 ≤ s < n. Verification uses the RSA Verification Primitive (RSAVP1): given the public key (n, e) and s where 0 ≤ s < n, compute:
m' = s^e \mod n
then check if m' equals the expected message representative m (e.g., the hash of the signed data). If they match, the signature is valid. Like the encryption primitives, raw RSA signatures are insecure without proper encoding to prevent malleability and other attacks, as detailed in subsequent sections on encoding mechanisms.[1]
Encoding and Padding Mechanisms
In PKCS#1, encoding mechanisms convert messages or hashes represented as octet strings into integer values suitable for RSA operations. The Octet-String-to-Integer Primitive (OS2IP) performs this conversion by interpreting the input as a big-endian octet string, where the integer x is computed as
x = \sum_{i=0}^{xLen-1} x_i \cdot 256^{xLen-1-i},
with x_i denoting the i-th octet from the left.[16] This ensures that byte sequences are mapped uniquely to nonnegative integers within the RSA modulus range, facilitating the application of RSA primitives like exponentiation.[16]
Padding is essential in PKCS#1 because raw RSA encryption and signature operations are inherently deterministic and malleable, allowing an adversary to modify ciphertexts or signatures without detection and predict outputs from known inputs.[17] Without padding, these vulnerabilities enable attacks such as chosen-ciphertext manipulations, as the lack of randomness and structure in plaintext-to-ciphertext mapping exposes the scheme to forgery or decryption oracle exploitation.[17] Padding schemes address this by incorporating redundancy, randomness, and formatting to achieve semantic security and verifiability.
PKCS#1 defines two primary padding categories: deterministic schemes like PKCS#1 v1.5, which append a fixed structure with redundancy (e.g., a padding string of nonzero octets followed by a delimiter), and probabilistic schemes like OAEP, which employ a mask generation function (MGF) to introduce randomness and obscure the message boundary.[18][19] The v1.5 padding ensures a specific octet sequence (e.g., 0x00 || 0x02 || PS || 0x00 || M) for both encryption and signatures, providing basic redundancy without variability.[18] In contrast, OAEP uses iterative masking to expand and randomize the encoded message, enhancing resistance to cryptanalytic attacks through provable security under the random oracle model.[19]
For decryption, redundancy checks in padding schemes verify the integrity of recovered messages; for instance, in v1.5 encryption, the presence of the leading 0x00 octet and padding delimiter confirms correct unpadding, rejecting malformed outputs to prevent partial decryptions.[18] In signature schemes, Encoding Method for Signature Append (EMSA) integrates hash functions to process messages into padded octet strings, such as EMSA-PKCS1-v1.5, which prefixes a DigestInfo structure (containing the hash) with a deterministic padding string (00 || 01 || PS || 00), enabling verification against the signer's public key.[20] These EMSA methods ensure that signatures bind the hash of the message via redundancy, thwarting existential forgery by requiring exact matches during recovery.[20]
Encryption Schemes
RSAES-PKCS1-v1_5
RSAES-PKCS1-v1_5 is a legacy RSA encryption scheme defined in PKCS#1 version 1.5, which combines the RSA encryption primitive (RSAEP) with the EME-PKCS1-v1_5 encoding method to provide a basic mechanism for encrypting messages using a public key.[21] This scheme supports messages of up to k - 11 octets in length, where k is the length of the RSA modulus n in octets, and it is included in later versions of the standard primarily for backward compatibility rather than for new implementations.[21] The encoding ensures that the padded message fits exactly into k octets before applying the RSA exponentiation.
The encryption process begins with inputs consisting of an RSA public key (n, e) and a message M represented as an octet string of length mLen, where $0 < mLen \leq k - 11.[21] If mLen > k - 11, the process outputs an error indicating the message is too long.[21] Otherwise, a padding string PS of length k - mLen - 3 octets is generated, consisting of pseudo-random bytes each in the range 1 to 255 (non-zero).[21] The encoded message EM is then formed as the concatenation EM = 00 \parallel 02 \parallel PS \parallel 00 \parallel M, where $00 and $02 are single-octet values in hexadecimal notation, PS provides probabilistic padding, and the trailing $00 serves as a separator before the message M.[21] To complete encryption, convert EM to an integer m using the octet string to integer primitive (OS2IP), compute the ciphertext integer c = m^e \mod n via RSAEP, and output the ciphertext C as the k-octet string from integer to octet string primitive (I2OSP) applied to c.[22][21]
The full padding structure follows the format:
EM = 00 \parallel \text{BT} \parallel PS \parallel 00 \parallel M
where BT is the block type octet equal to 02 for encryption, and |PS| \geq 8 to ensure sufficient randomness against certain attacks.[21]
Decryption takes as input an RSA private key K (which includes n and the decryption exponent d, or equivalent via Chinese Remainder Theorem components) and a ciphertext C of exactly k octets, with k \geq 11.[21] If the length conditions are not met, it outputs a decryption error.[21] Convert C to integer c via OS2IP, compute the integer m = c^d \mod n (or equivalent) via the RSA decryption primitive (RSADP), and recover EM as the k-octet I2OSP of m.[23][21] Parse EM: it must begin with the octet 00 followed by BT = 02, then non-zero octets in PS of at least 8 octets, followed by the octet 00; if any check fails (e.g., no valid separator or insufficient padding), output a decryption error.[21] Otherwise, extract M as the remaining octets after the separator.[21]
In this scheme, the block type BT distinguishes the encoding purpose: the leading 00 acts as a padding delimiter (effectively BT = 00 in some contexts), while BT = 02 specifically indicates encryption with data padding, and the minimum |PS| \geq 8 enforces a baseline security property by randomizing at least 8 bytes.[21] For encryption, the probabilistic choice of non-zero bytes in PS aims to prevent deterministic attacks, though the scheme's overall structure has known limitations compared to more modern alternatives.[21]
RSAES-OAEP
RSAES-OAEP is an RSA encryption scheme introduced in PKCS#1 version 2.0 that combines the RSA encryption primitive (RSAEP) with the Optimal Asymmetric Encryption Padding (OAEP) encoding method, known as EME-OAEP, to achieve semantic security against adaptive chosen-ciphertext attacks (IND-CCA2).[24] This scheme supports messages of length up to k - 2hLen - 2 octets, where k is the length of the RSA modulus n in octets and hLen is the length of the output of the underlying hash function in octets.[24] Unlike earlier padding methods, RSAES-OAEP incorporates randomization through a seed value and uses a mask generation function to expand and obfuscate the message, providing provable security in the random oracle model assuming the difficulty of inverting the RSA function.[25]
The key components of RSAES-OAEP include the EME-OAEP encoding scheme, a hash function (such as SHA-256, which produces hLen = 32 octets), and the mask generation function MGF1, which is based on the hash function and generates a pseudorandom string of arbitrary length from a seed.[26] MGF1 operates by iteratively hashing the seed concatenated with a counter to produce the mask, ensuring diffusion across the padded data.[26] An optional label L (defaulting to an empty string) allows for additional context in the encoding, with its hash lHash = \text{Hash}(L) prepended to the padded message; the label length must not exceed $2^{61} - 1 octets to prevent overflow issues.[24] These elements together ensure that the encoded message block EM is exactly k octets long before RSA exponentiation.[24]
The encryption process, RSAES-OAEP-ENCRYPT, takes as input the RSA public key (n, e), a message M of length mLen \leq k - 2hLen - 2, and the optional label L.[24] It proceeds as follows:
-
If L is not provided, set L to the empty string and compute lHash = \text{Hash}(L). Generate a padding string PS consisting of k - mLen - 2hLen - 2 zero octets. Form the data block DB = lHash || PS || 0x01 || M.[24]
-
Generate a random seed of hLen octets. Compute the mask dbMask = \text{MGF1}(seed, k - hLen), where the mask length is the length of DB. Then, maskedDB = DB \oplus dbMask, where \oplus denotes octet-wise XOR. Compute seedMask = MGF1(maskedDB, hLen), and maskedSeed = seed \oplus seedMask. The encoded message is EM = 0x00 || maskedSeed || maskedDB.[24]
-
Convert EM to an integer m = OS2IP(EM), compute the ciphertext integer c = RSAEP(n, e, m) = m^e \mod n, and output the ciphertext C = I2OSP(c, k).[24]
This encoding expands the message while randomizing it, preventing deterministic attacks on the underlying RSA function. The full OAEP expansion can be represented as:
\begin{align*}
&[DB](/page/DB) = lHash || [PS](/page/PS) || 0x01 || [M](/page/M), \\
&masked[DB](/page/DB) = [DB](/page/DB) \oplus \text{MGF1}([seed](/page/Seed), |[DB](/page/DB)|), \\
&maskedSeed = [seed](/page/Seed) \oplus \text{MGF1}(masked[DB](/page/DB), hLen), \\
&[EM](/page/EM) = 0x00 || maskedSeed || masked[DB](/page/DB),
\end{align*}
followed by C = I2OSP( \text{OS2IP}([EM](/page/EM))^e \mod n, k ).[24]
Decryption, RSAES-OAEP-DECRYPT, reverses the process using the RSA private key K (corresponding to modulus length k) and ciphertext C, with the same label L. It requires k \geq 2hLen + 2.[24]
-
Convert C to integer c = OS2IP(C). Compute m = RSADP(K, c); if c \geq n or the operation fails, output "decryption error". Convert back to EM = I2OSP(m, k). If the leading octet of EM is not 0x00, output "decryption error".[24]
-
Compute lHash = \text{Hash}(L). Extract maskedSeed (first hLen octets after 0x00) and maskedDB (remaining k - hLen - 1 octets). Compute seedMask = MGF1(maskedDB, hLen), so seed = maskedSeed \oplus seedMask. Then, dbMask = MGF1(seed, k - hLen - 1), and DB = maskedDB \oplus dbMask.[24]
-
Parse DB for lHash' (first hLen octets), followed by zero octets PS, a 0x01 octet, and message M. If lHash' ≠ lHash, no 0x01 is found, or PS contains non-zero octets, output "decryption error". Otherwise, output M.[24]
These checks ensure integrity and recover the original message, rejecting invalid or tampered ciphertexts without leaking information.[24]
Signature Schemes
RSASSA-PKCS1-v1_5
RSASSA-PKCS1-v1_5 is a signature scheme with appendix that combines the RSA signing primitive RSASP1 with the deterministic encoding method EMSA-PKCS1-v1_5 to produce signatures on messages of arbitrary length, provided a suitable hash function is used.[27] It was originally specified in PKCS#1 version 1.5 and remains included in later versions for backward compatibility with legacy systems.[27] The scheme assumes the infeasibility of computing e-th roots modulo the RSA modulus n and relies on the security of the underlying hash function to prevent collisions.[27]
The signing process begins by applying a hash function to the message M to produce a hash value H, followed by encoding the hash algorithm identifier and H into a DER-encoded DigestInfo structure T, where tLen denotes the length of T in octets.[20] If the intended encoded message length emLen (equal to the octet length k of the RSA modulus n) is less than tLen + 11, the process fails with an error.[20] Otherwise, a padding string PS of length emLen - tLen - 3 octets, consisting entirely of 0xFF bytes, is generated.[20] The encoded message EM is then constructed as the concatenation:
\text{EM} = 0x00 \parallel 0x01 \parallel \text{PS} \parallel 0x00 \parallel \text{T}
where \parallel denotes concatenation.[20] This EM is converted to an integer m via the octet string to integer primitive OS2IP, and the signature representative s is computed using the RSA private key (n, d) as s = m^d \mod n.[28] Finally, the signature S is the integer s converted back to an octet string of length k via I2OSP.[28] The process outputs S or fails with errors such as "message too long" if the hash exceeds allowable limits.[28]
Verification uses the signer's RSA public key (n, e) and proceeds by first confirming that the signature S has exactly k octets.[29] The octet string S is converted to an integer s via OS2IP, and an integer m is recovered as m = s^e \mod n using the RSA verification primitive RSAVP1.[29] This m is then converted to an octet string EM' of length k via I2OSP.[29] Independently, the original message M is re-encoded using EMSA-PKCS1-v1_5 to produce EM, and the scheme outputs "valid signature" if EM equals EM', ensuring the padding structure (starting with 0x00 || 0x01, followed by 0xFF padding, a 0x00 separator, and the matching DigestInfo) and hash value are intact; otherwise, it outputs "invalid signature".[29] Errors like "RSA modulus too short" may occur if parameters are invalid.[29]
The scheme supports a range of digest algorithms, each prefixed with its AlgorithmIdentifier in the DigestInfo T, including MD2 (tLen=18), MD5 (tLen=18), SHA-1 (tLen=35), SHA-224 (tLen=47), SHA-256 (tLen=51), SHA-384 (tLen=67), SHA-512 (tLen=83), SHA-512/224 (tLen=47), and SHA-512/256 (tLen=51).[30] These identifiers are DER-encoded as specified in RFC 8017, Appendix A.2, to ensure interoperability.[30]
RSASSA-PSS
RSASSA-PSS is a probabilistic digital signature scheme with appendix, defined in PKCS#1 version 2.0 and subsequent revisions, that combines the RSA signature primitive (RSASP1) with the EMSA-PSS encoding method to produce signatures with provable security properties.[4] The scheme operates on messages of arbitrary length by first hashing the input and then applying a randomized padding mechanism, ensuring existential unforgeability under chosen-message attacks in the random oracle model, provided the RSA problem is intractable and the underlying hash and mask generation functions are secure.[4][31] This design, originally proposed by Bellare and Rogaway, enhances resistance to forgery compared to deterministic alternatives by incorporating randomness, making it suitable for high-security applications.[31]
The core components of RSASSA-PSS include an RSA private key K (typically (n, d), where n is the modulus and d is the private exponent) for signing and the corresponding public key (n, e) for verification, along with a cryptographic hash function (Hash, such as SHA-256) and a mask generation function (MGF, usually MGF1 based on the same hash).[4] A key element is the salt, a random octet string of length sLen octets, which introduces probabilistic behavior to thwart certain attacks.[4] Relevant parameters are emBits (the intended bit length of the encoded message EM, typically one less than the bit length of n), hLen (the octet length of the hash output), and sLen (the salt length, recommended to be at least 8 octets and often equal to hLen - 2 for optimal security).[4]
The signing process begins by computing mHash = \text{Hash}(M), where M is the message to sign.[4] A random salt of length sLen is generated. The EMSA-PSS encoding is then applied to produce EM of length emLen = \lceil emBits / 8 \rceil octets: first form M' = eight zero octets \| mHash \| salt, then compute H = \text{Hash}(M'); next, construct the data block DB = padding string PS (of emLen - sLen - hLen - 2 zero octets) \| 0x01 \| salt, generate the mask dbMask = \text{MGF}(H, emLen - hLen - 1), compute maskedDB = DB \oplus dbMask, set the leftmost $8 \cdot emLen - emBits bits of maskedDB to zero if emBits is not a multiple of 8, and finally form EM = maskedDB \| H \| 0xBC.[4] This EM is converted to an integer m = OS2IP(EM), and the signature integer s = RSASP1(K, m) = m^d \mod n is output as the octet string S = I2OSP(s, k), where k =\lceil (\text{bit length of } n)/8 \rceil.[4]
Verification recovers the encoded message by converting the signature S to integer s = OS2IP(S), computing m = RSAVP1((n, e), s) = s^e \mod n, and converting back to EM = I2OSP(m, emLen).[4] The EMSA-PSS verification then extracts H' (the hLen octets before the final 0xBC octet) and maskedDB from EM, generates dbMask = MGF(H', emLen - hLen - 1), unmasks DB = maskedDB \oplus dbMask, checks that the rightmost octet of maskedDB is 0xBC, the leftmost bits are zero as required, and DB starts with PS \| 0x01 followed by a salt of length sLen; it then recomputes M'' = eight zero octets \| mHash \| extracted salt, H'' = \text{Hash}(M''), and mHash = \text{Hash}(M), verifying that H' = H''.[4] If all checks pass, the signature is valid; otherwise, it is invalid.[4]
The full PSS encoding integrates the salt for randomization within a masked structure, represented as:
\text{EM} = \underbrace{\text{maskedDB}}_{\text{length: } emLen - hLen - 1} \ \| \ \underbrace{H}_{\text{length: } hLen} \ \| \ 0\text{xBC}
where maskedDB derives from the XOR of the data block (padding \| 0x01 \| salt) with the MGF output from H = Hash(eight zero octets \| Hash(M) \| salt), ensuring the padding is indistinguishable from random without the salt.[4] This construction provides the scheme's security foundation, with proofs showing that breaking RSASSA-PSS implies solving the RSA problem or distinguishing the hash/MGF from random oracles.[31]
Version History
Versions 1.0 to 1.5
The Public-Key Cryptography Standards (PKCS) #1 specification originated with version 1.0 in 1991, developed and privately distributed by RSA Data Security, Inc., to define basic RSA-based cryptographic primitives for encryption and digital signatures.[32] This initial version introduced minimal padding mechanisms for RSA encryption, known as EME-PKCS1-v1_5, which prepends a padding string to the message before exponentiation, and a corresponding signature scheme, EMSA-PKCS1-v1_5, which applies similar padding to a message digest for verification.[32] These schemes provided foundational support for RSA operations in public-key systems but lacked explicit integration with hash functions, relying instead on direct message processing or basic concatenation.[32]
Subsequent minor revisions from version 1.1 to 1.3, in February through March 1991, and version 1.4 in June 1991, were handled internally by RSA Laboratories through working group distributions, with version 1.4 marking the first public release on June 3, 1991, as part of the broader PKCS suite documented in NIST/OSI Implementors' Workshop materials.[32] These updates refined the basic primitives and ASN.1 syntax for RSA public and private keys, improving interoperability with emerging standards like X.509 for certificate encoding.[32] Version 1.4 consolidated these enhancements into a more structured format suitable for implementors.[32]
Version 1.5, first issued in November 1993 and published as IETF informational RFC 2313 in March 1998, represented a significant milestone, signaling a transition from RSA Laboratories' proprietary oversight to broader community influence under IETF auspices.[32][33] It formally standardized the EME-PKCS1-v1_5 encryption scheme, which uses a deterministic padding of 00 02 followed by random non-zero bytes and a message separator, and the EMSA-PKCS1-v1_5 signature scheme, which embeds a DigestInfo structure containing the hash algorithm identifier (supporting MD2, MD4, and MD5) within a padded block.[32] These mechanisms ensured compatibility with existing RSA implementations while specifying exact byte-level formats for encryption blocks and signature values.[32] RFC 2313 was later obsoleted by RFC 2437 in October 1998, which introduced version 2.0 with expanded features.[33] At this stage, PKCS #1 remained focused on these legacy padding methods, without incorporating probabilistic schemes like OAEP for encryption or PSS for signatures.[33]
Versions 2.0 to 2.2
PKCS#1 version 2.0, published in October 1998 as RFC 2437, marked a significant advancement by introducing the RSAES-OAEP encryption scheme, which incorporates optimal asymmetric encryption padding to enhance security against chosen-ciphertext attacks under the random oracle model.[3] This version also removed support for the MD4 hash function due to emerging cryptanalytic weaknesses, while retaining the RSASSA-PKCS1-v1_5 signature scheme and providing initial security analyses linking scheme robustness to the difficulty of inverting the RSA function with sufficiently large keys.[3] Although it included discussions on plaintext-aware properties for OAEP, full provable security arguments were not yet formalized.[3]
Version 2.1, released in February 2003 as RFC 3447, built upon its predecessor by adding the RSASSA-PSS signature scheme, a probabilistic method designed to provide existential unforgeability against adaptive chosen-message attacks in the random oracle model.[7] It also introduced support for multi-prime RSA to improve computational efficiency in key generation and operations, while formalizing security proofs for both RSAES-OAEP and RSASSA-PSS, establishing their equivalence to the RSA problem under standard assumptions.[7] Regarding hash functions, this version restricted MD2 and MD5 to compatibility use with the legacy PKCS1-v1_5 scheme, recommending stronger alternatives like SHA-1, SHA-256, SHA-384, and SHA-512 for new implementations due to vulnerabilities in the older hashes.[7]
The current version 2.2, published in November 2016 as RFC 8017, obsoleted RFC 3447 and incorporated errata fixes, clarified the application of the MGF1 mask generation function—recommending it use the same underlying hash as the primary scheme for consistency—and expanded support for SHA-2 family hashes by adding SHA-224, SHA-512/224, and SHA-512/256 to align with FIPS 180-4.[4] These updates emphasized probabilistic schemes like OAEP and PSS as preferred for their provable security properties, while maintaining backward compatibility for deterministic alternatives in legacy contexts.[4] As of 2025, no major revisions have occurred, though NIST guidelines in the 2020s have discussed deprecating RSA-based mechanisms with key sizes below 2048 bits by 2030 due to post-quantum threats, without altering the PKCS#1 specification itself.[34]
Implementations
Software Libraries
OpenSSL, a widely used open-source cryptographic library, provides comprehensive support for all PKCS#1 schemes through its high-level EVP interface, which abstracts low-level RSA operations and includes legacy PKCS#1 v1.5 padding alongside modern v2.0+ features like OAEP and PSS.[35] The EVP interface enables secure encryption and signing; for instance, OAEP encryption can be performed using EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) followed by EVP_PKEY_encrypt, with options for specifying hash functions like SHA-256 via EVP_PKEY_CTX_set_rsa_oaep_md. This design promotes safe usage by handling padding internally and warning against insecure legacy modes in recent versions.[36]
Bouncy Castle, available for Java and C#, offers full PKCS#1 compliance, implementing RSA encryption (RSAES-PKCS1-v1_5 and RSAES-OAEP) and signature schemes (RSASSA-PKCS1-v1_5 and RSASSA-PSS) with integrated ASN.1 parsing for key and message formatting as defined in RFC 8017.[37] Key classes like PKCS1Encoding handle padding schemes, ensuring interoperability with standards-compliant systems, while the library's modular structure allows customization of underlying hashes and masks for OAEP/PSS operations.
Crypto++, a C++ library focused on performance, supports high-speed RSA implementations of PKCS#1 schemes, including PSS for probabilistic signatures and OAEP for encryption, optimized for large-scale applications with hardware acceleration where available. It provides classes like RSAES<OAEP<SHA256>> for encryption and RSASS<PSS> for signing, adhering to RFC 8017 specifications for padding and encoding.
Other notable libraries include Python's cryptography module, which implements PKCS#1 v1.5 and v2.2 schemes via its hazmat.primitives.asymmetric.[rsa](/page/RSA) package, supporting OAEP encryption with rsa.OAEP and PSS signing with rsa.PSS; Go's standard crypto/[rsa](/page/RSA) package, which directly implements PKCS#1 encryption and signatures per RFC 8017 using functions like EncryptOAEP and SignPSS; and Intel's Integrated Performance Primitives (IPP) Cryptography library, which accelerates PKCS#1 v1.5 operations like ippsRSAEncrypt_PKCSv15 for embedded and high-performance computing.[38][39][40]
As of 2025, these libraries have incorporated warnings about RSA's vulnerability to quantum attacks, urging migration to post-quantum alternatives, though no new PKCS#1 version has been released since RFC 8017 in 2016.
Compliance and Standards
PKCS#1 schemes are integral to several foundational cryptographic standards. In the Transport Layer Security (TLS) protocol version 1.2, as defined in RFC 5246, RSA key exchange employs RSAES-PKCS1-v1_5 for encrypting the premaster secret, while digital signatures in handshakes and certificates use RSASSA-PKCS1-v1_5 with specified hash functions.[41] Similarly, the Cryptographic Message Syntax (CMS) in RFC 5652 utilizes PKCS#1 for RSA-based key transport in enveloped data, where the content-encryption key is encrypted via RSAES-PKCS1-v1_5 or RSAES-OAEP, and for signing operations in signed data via RSASSA-PKCS1-v1_5 or RSASSA-PSS.[42] PKCS#1 also underpins RSA key structures in X.509 certificates, with the rsaEncryption object identifier (1.2.840.113549.1.1.1) and associated ASN.1 encodings enabling public key representation and usage in certificate profiles as outlined in RFC 5280.[43]
Implementation compliance with PKCS#1 is rigorously tested through the NIST Cryptographic Algorithm Validation Program (CAVP), which evaluates RSA primitives such as RSASP1 for signature generation (supporting both PKCS#1 v1.5 and PSS padding) and RSADP for decryption, aligned with FIPS 186-4 requirements.[44] These tests generate automated vectors to verify correctness across key sizes and moduli, ensuring modules meet federal standards before Cryptographic Module Validation Program (CMVP) certification.[45] Additionally, RFC 8017 provides sample inputs and outputs for encryption and signature schemes, including detailed examples for RSAES-OAEP and RSASSA-PSS operations, serving as reference vectors for developer verification.[4]
The specification is maintained by the IETF Crypto Forum Research Group (CFRG), which published RFC 8017 as the authoritative version 2.2 in 2016 and continues to issue guidance through ongoing drafts, including the September 2025 revision of "Implementation Guidance for the PKCS #1 RSA Cryptography Specification" (draft-irtf-cfrg-rsa-guidance).[46] CFRG recommendations explicitly deprecate RSAES-PKCS1-v1_5 and RSASSA-PKCS1-v1_5 in new protocol designs, mandating RSAES-OAEP for encryption and RSASSA-PSS for signatures to mitigate known vulnerabilities, with additional guidance on side-channel protections such as constant-time operations and blinding techniques. This shift, emphasized in CFRG documents since 2023, prioritizes provably secure primitives while preserving backward compatibility for legacy deployments.
Interoperability is facilitated by PKCS#1's ASN.1 module, which standardizes RSAPublicKey and RSAPrivateKey structures for encoding RSA parameters like modulus and exponents, enabling seamless integration across libraries and protocols such as X.509 and CMS.[7] These definitions ensure cross-vendor compatibility for key exchange and operations. However, legacy systems adhering to PKCS#1 v1.5 often face interoperability challenges with modern endpoints, exacerbated by exploitable flaws like the Marvin timing attack, which affects older TLS, S/MIME, and token implementations supporting vulnerable padding.[47]
Security Considerations
Known Attacks
One of the most significant vulnerabilities in early PKCS#1 implementations is Bleichenbacher's chosen-ciphertext attack on RSAES-PKCS1-v1_5 encryption, introduced in 1998. This adaptive attack exploits a padding oracle—where a decryption device reveals whether a ciphertext has valid PKCS#1 v1.5 padding (block type 02)—to gradually narrow down the plaintext space through iterative queries. By crafting modified ciphertexts and observing oracle responses, an attacker can decrypt an arbitrary target ciphertext using approximately 10^5 to 10^6 queries, making it practical against real-world systems like SSL/TLS servers.[48]
For signatures, RSASSA-PKCS1-v1_5 is susceptible to broadcast attacks when the same message is signed under multiple public keys with small exponents (e.g., e=3), as demonstrated by Håstad in 1988; using the Chinese Remainder Theorem on the resulting congruences enables message recovery with high probability if at least e recipients are involved.[49]
In contrast, the RSAES-OAEP and RSASSA-PSS schemes introduced in PKCS#1 v2.0 provide stronger security guarantees. Both are provably secure against chosen-ciphertext attacks in the random oracle model, assuming the underlying hash function and mask generation function behave ideally, as formalized by Bellare and Rogaway for OAEP in 1994 and extended to PSS in 1996. However, real-world implementations remain vulnerable to side-channel attacks, such as timing discrepancies during padding validation or fault injection to bypass checks, which can leak information about intermediate values. A notable example is Manger's attack (2001), which exploits error message leaks in OAEP decoding to perform chosen-ciphertext attacks with around 2^14 to 2^16 oracle interactions.[50][4]
Post-2012 analyses confirm no fundamental breaks on the core RSAES-OAEP or RSASSA-PSS in PKCS#1 v2.2, but legacy v1.5 usage persists in protocols like TLS, enabling variants of Bleichenbacher's attack. The ROBOT attack, disclosed in 2017, exploits such oracles in TLS handshakes to recover session keys or cookies using about 65,000 queries across multiple connections, affecting numerous servers due to incomplete mitigations. More recently, the Marvin attack (2023) demonstrated practical timing-based Bleichenbacher variants on both PKCS#1 v1.5 and OAEP implementations, allowing attackers to perform decryption or signing operations by observing cache-timing differences during padding checks, often requiring only thousands of observations.[51][52]
Best Practices and Mitigations
To ensure secure implementation of PKCS#1 schemes, it is recommended to prefer RSAES-OAEP for encryption and RSASSA-PSS for signatures over the older PKCS#1 v1.5 padding, as the latter is vulnerable to chosen-ciphertext attacks and lacks probabilistic padding.[53] RSA key sizes of at least 3000 bits should be used to provide adequate security against factoring attacks, as recommended by authorities like BSI; 2048-bit keys remain acceptable under NIST guidelines until December 31, 2030, but larger sizes (e.g., 3072 bits or more) are advised for protection beyond 2030.[54][15] Additionally, hash functions such as SHA-256 or stronger must be employed in conjunction with these schemes to maintain collision resistance.[1]
Mitigations against side-channel attacks require constant-time implementations for critical operations like modular exponentiation in RSA, ensuring that execution time and memory access patterns do not leak information about keys or messages.[53] To counter padding oracle vulnerabilities, particularly in legacy v1.5 deployments, error blinding techniques should be applied during decryption, randomizing responses to invalid paddings without revealing structural details.[53]
NIST Special Publication 800-131A Revision 2 deprecates RSA PKCS#1 v1.5 for encryption, key establishment, and key transport, with legacy use acceptable only until December 31, 2030, mandating a transition to more secure alternatives thereafter.[55] In preparation for quantum threats, organizations are advised to migrate toward hybrid schemes combining classical RSA with post-quantum algorithms like ML-KEM, as outlined in NIST's 2024 transition guidance, to enable gradual adoption without disrupting existing infrastructure.[56]
Specific best practices include generating random salts of appropriate length (typically matching the hash output size) during RSASSA-PSS signing to enhance security against forgery attempts, ensuring each signature incorporates fresh randomness.[1] For RSAES-OAEP, a non-empty label should be used when contextual binding is needed, such as specifying the intended recipient or application, to prevent misuse in multi-purpose deployments; otherwise, an empty label is acceptable for simplicity.[1] Regular key rotation, at least annually or upon evidence of compromise, is essential to limit exposure, following guidelines for cryptographic key management.[57]