TIP fast way to find definitions of a word
Example 1 select NormalvsCompact
press [ctrl]+[f]
press [f3]
2 select CompactTLS1.3
press [ctrl]+[f]
press [f3]
3 select HMAC
press [ctrl]+[f]
press [f3]
TLS Transport Layer Security
uses TCP with port 443
TCP Transmission Control Protocol
defined in RFC793
T T'<x..y> a vector of variable size
type T
= vecSize || vecValue
vecSize value = size in bytes of vecValue
size = ceil(log2(y+1)/8) bytes
|| means concatenation
vecValue value = ByteArray of custom data
size = vecSize value
x = vecValue minimum size in bytes
y = vecValue maximum size in bytes
example T T' <x..y>
CipherSuite cipher_suites<2..2^16-2>
CompactTLS1.3
based on RFC8446
SessionNew map Key ^ ClientHello
Exch | + key_share*
| + signature_algorithms*
| + psk_key_exchange_modes*
v + pre_shared_key* -------->
ServerHello ^ Key
+ key_share* | Exch
+ pre_shared_key* v
{EncryptedExtensions} ^ Server
{CertificateRequest*} v Params
{Certificate*} ^
{CertificateVerify*} | Auth
{Finished} v
<-------- [ApplicationData*]
^ {Certificate*}
Auth | {CertificateVerify*}
v {Finished} -------->
[ApplicationData] <-------> [ApplicationData]
symbols + extensions sent in previously noted message
* messages/extensions optional
situation dependent
not always sent
{} messages protected by keys derived from [sender]_handshake_traffic_secret
[] messages protected by keys derived from [sender]_application_traffic_secret_N
SessionRetry uses HelloRetryRequest
map ClientHello1
+ key_share -------->
HelloRetryRequest
<-------- + key_share
ClientHello2
+ key_share -------->
ServerHello
+ key_share
{EncryptedExtensions}
{CertificateRequest*}
{Certificate*}
{CertificateVerify*}
{Finished}
<-------- [ApplicationData*]
{Certificate*}
{CertificateVerify*}
{Finished} -------->
[ApplicationData] <-------> [ApplicationData]
SessionResume Initial map ClientHello
+ key_share -------->
ServerHello
+ key_share
{EncryptedExtensions}
{CertificateRequest*}
{Certificate*}
{CertificateVerify*}
{Finished}
<-------- [ApplicationData*]
{Certificate*}
{CertificateVerify*}
{Finished} -------->
<-------- [NewSessionTicket]
[ApplicationData] <-------> [ApplicationData]
noRRT Initial NewSessionTicket had no early_data
has no Server Certificates because autentication depends on pre_shared_key
key_share may be sent to allow SessionResume change to SessionRetry
map ClientHello
+ key_share*
+ pre_shared_key -------->
ServerHello
+ pre_shared_key
+ key_share*
{EncryptedExtensions}
{Finished}
<-------- [ApplicationData*]
{Finished} -------->
[ApplicationData] <-------> [ApplicationData]
RRT Initial NewSessionTicket had early_data
has no Server Certificates because autentication depends on pre_shared_key
key_share may be sent to allow SessionResume change to SessionRetry
Server does not wait to receive EndOfEarlyData to send ServerHello
map ^ ClientHello
| + early_data
0-RTT | + key_share*
| + psk_key_exchange_modes
| + pre_shared_key
v (ApplicationData*) -------->
ServerHello
+ pre_shared_key
+ key_share*
{EncryptedExtensions}
+ early_data*
{Finished}
<-------- [ApplicationData*]
^ (EndOfEarlyData)
1-RTT | {Finished} -------->
v [ApplicationData] <-------> [ApplicationData]
0-RTT zero round-trip time
data is not forward secret
attacker threats duplicating network flight
using Client retry behavior then Server receives multiple copies of ApplicationData
1-RTT one round-trip time
ClientHello sent at start of Session
after receiving HelloRetryRequest
struct {
ProtocolVersion legacy_version = 0x0303;
Random random;
opaque legacy_session_id<0..32>;
CipherSuite cipher_suites<2..2^16-2>;
opaque legacy_compression_methods<1..2^8-1>;
Extension extensions<8..2^16-1>;
} ClientHello;
ProtocolVersion uint16
0x0303 = TLS 1.2
0x0304 = TLS 1.3
legacy_version used for Middlebox compatability
not used by Server to select TLS version of Session
random size = 32 bytes
cryptographically secure
new generated by Client
legacy_session_id if Client wants no Middlebox compatability then size = 0 bytes
if Client wants Middlebox compatability then = unpredictable value
size = 32byte
Server will use for ChangeCipherSpec
cipher_suites list of uint8 CipherSuite[2];
supported by Client
ordered preference descending
legacy_compression_methods = 0 = null
size = 1byte
if wrong value or size
then Server Alert illegal_parameter
extensions has supported_versions with ProtocolVersion = 0x0304
cookie if received HelloRetryRequest has cookie
may have certificate_authorities
signature_algorithms_cert
post_handshake_auth
server_name
early_data if sent before HelloRetryRequest
has no early_data if sent after HelloRetryRequest
pre_shared_key is last
else Server Alert illegal_parameter
if has no pre_shared_key
then it has signature_algorithms and
supported_groups and
key_share
else Server Abort missing_extension
if has pre_shared_key and
sent after HelloRetryRequest
then update obfuscated_ticket_age
binders
if has no server_name
then Server may Alert missing_extension
if started Sesion with TLS version < 1.3
then received supported_versions = 0x0304
then stay with TLS version < 1.3
Server ignores any unrecognized Extension
if sent with 0-RRT and
receive ServerHello with TLS version < 1.3
then Alert protocol_version
put in Handshake then put in TLSPlaintext then send to peer
ServerHello sent after ClientHello
if Server supports ClientHello parameters
struct {
ProtocolVersion legacy_version = 0x0303;
Random random;
opaque legacy_session_id_echo<0..32>;
CipherSuite cipher_suite;
uint8 legacy_compression_method = 0;
Extension extensions<6..2^16-1>;
} ServerHello;
legacy_version used for Middlebox compatability
not used by Client to select TLS version of Session
random 32 bytes
cryptographically secure
new generated by Server
last 8 bytes = 44 4F 57 4E 47 52 44 01 if Server supports TLS version >= 1.2 and selects 1.2 for Session
= 44 4F 57 4E 47 52 44 00 if Server supports TLS version >= 1.1 and selects 1.1 for Session
if Client only supports TLS version 1.3
then last 8 bytes != 44 4F 57 4E 47 52 44 01
!= 44 4F 57 4E 47 52 44 00
if received and does not follow these rules
then Client Alert illegal_parameter
legacy_session_id_echo = ClientHello.legacy_session_id
if received and
!= ClientHello.legacy_session_id
then Client Alert illegal_parameter
cipher_suite one selected from ClientHello.cipher_suites
else Client Alert illegal_parameter
extensions has supported_versions ProtocolVersion = 0x0304
else Client Alert protocol_version
has no Extension different from ClientHello.extensions
has one or both pre_shared_key
key_share
pre_shared_key may be last
if started Sesion with TLS version < 1.3
then received supported_versions = 0x0304
then stay with TLS version < 1.3
put in Handshake then put in TLSPlaintext then send to peer
HelloRetryRequest sent by Server
if received ClientHello has no sufficient information
Example ClientHello key_share has DHE or ECDHE groups unacceptable
unsupported
if received more than one time in Session
then Alert unexpected_message
if received with not enough data to generate different ClientHello
then Alert illegal_parameter
has HandshakeType = server_hello
struct {
ProtocolVersion legacy_version = 0x0303;
Random random;
opaque legacy_session_id_echo<0..32>;
CipherSuite cipher_suite;
uint8 legacy_compression_method = 0;
Extension extensions<6..2^16-1>;
} HelloRetryRequest;
random = CF 21 AD 74 E5 9A 61 11 BE 1D 8C 02 1E 65 B8 91 C2 A2 11 16 7A BB 8C 5E 07 9E 09 E2 C8 A8 33 9C
= SHA256 of string "HelloRetryRequest"
legacy_session_id_echo = ClientHello.legacy_session_id
else Client Alert illegal_parameter
cipher_suite one selected from ClientHello.cipher_suites
else Client Alert illegal_parameter
extensions has supported_versions ProtocolVersion = 0x0304
else Client Alert illegal_parameter
has no Extension different from extensions of ClientHello exept cookie
may have cookie
key_share
put in Handshake then put in TLSPlaintext then send to peer
EncryptedExtensions sent by Server
after ServerHello
even if extensions = empty
struct { Extension extensions<0..2^16-1>; } EncryptedExtensions;
extensions may have supported_groups
early_data
server_name
put in Handshake then put in TLSPlaintext then convert to TLSCiphertext then send to peer
CertificateRequest sent by Server
may be mutiple times
after EncryptedExtensions in SessionNew
SessionRetry
SessionResume Initial
Server Finished at any time in SessionResume noRRT
SessionResume RRT
received then accepts request by sending Certificate then
CertificateVerify then
Finished
declines request by sending Certificate with empty certificate_list then
Finished
if received after Server Finished and
ClientHello had no post_handshake_auth
then Alert unexpected_message
struct {
opaque certificate_request_context<0..2^8-1>;
Extension extensions<2..2^16-1>;
} CertificateRequest;
certificate_request_context = random unpredictable bytes
size = zero if in SessionNew
SessionRetry
SessionResume Initial
= nonzero if in SessionResume noRRT
SessionResume RRT
prevents replay of Client CertificateVerify
unique for every Session
extension must have signature_algorithms
may have signature_algorithms_cert
certificate_authorities
oid_filters
Client ignores any unrecognized Extension
put in Handshake then put in TLSPlaintext then convert to TLSCiphertext then send to peer
Certificate used to autenticate its sender
struct {
opaque certificate_request_context<0..2^8-1>;
CertificateEntry certificate_list<0..2^24-1>;
} Certificate;
struct {
select (certificate_type) {
case RawPublicKey: opaque ASN1_subjectPublicKeyInfo<1..2^24-1>; //RFC7250
case X509: opaque cert_data<1..2^24-1>;
};
Extension extensions<0..2^16-1>;
} CertificateEntry;
enum {
X509(0), //RFC5280
OpenPGP_RESERVED(1),
RawPublicKey(2), //RFC7250
(255)
} CertificateType;
certificate_type defined in received server_certificate_type
client_certificate_type
= X509(0) = X.509v3 if received no server_certificate_type
client_certificate_type
!= OpenPGP_RESERVED(1)
certificate_request_context = certificate_request_context from CertificateRequest if sent by Client
= empty if sent by Server
certificate_list certificates certify others in the list by signing them creating a signature chain
signature chain may not follow certificate_list order
may have extraneous certificates
first certificate belongs to sender
PrivateKey is possessed by sender
PublicKey is compatible with received signature_algorithms if sent by Server
signature chain ends with TrustAnchor
each certificate signed using any algorithm from received signature_algorithms_cert
signature_algorithms if signature_algorithms_cert not received
has digitalSignature bit set if has KeyUsage and sent by Server
TrustAnchor a certificate already trusted by receiver
may be self signed
signature is not validated by receiver
may not be in certificate_list if sender knowns receiver already has it
may be signed using algorithm not in received signature_algorithms
at least one may be from certificate_authorities in CertificateRequest if sent by Client
includes only one CertificateEntry if certificate_type = RawPublicKey
nonempty if sent by Client accepting CertificateRequest
empty if sent by Client declining CertificateRequest
if empty and received by Client then Client Alert decode_error
if empty and received by Server
then Server continue Session without Client autentication
abort Session Alert certificate_required
if invalid and received by Server
then Server continue Session without Client autentication
abort Session Alert specific to invalidity
extensions has extensions received in ClientHello if sent by Server
has extensions received in CertificateRequest if sent by Client
a extension that applies to entire certificate_list may be in first CertificateEntry in certificate_list
KeyUsage asserts all key usage bits from oid_filters
ExtendedKeyUsage has all key purpose OIDs from oid_filters
if receviced with signature algorithm using MD5 then Alert bad_certificate
SHA1 then must Alert bad_certificate if not TrustAnchor
may Alert bad_certificate if TrustAnchor
keys are compatible with signature_algorithms_cert if received signature_algorithms_cert
signature_algorithms if received no signature_algorithms_cert
if received by Server and
has not enough filters from oid_filters
then Server continue Session without Client authentication
abort Session Alert unsupported_certificate
put in Handshake then put in TLSPlaintext then convert to TLSCiphertext then send to peer
CertificateVerify sender proves possesion of PrivateKey of first certificate in certificate_list
provides Session integrity up to this point
sent after Certificate
struct {
SignatureScheme algorithm;
opaque signature<0..2^16-1>;
} CertificateVerify;
signature = SignatureAlgorithm(AA)
AA = BB || CC || 0x00 || Transcript-Hash(HandshakeContext, Certificate)
|| means concatenaion
BB each byte value = 32
size = 64 bytes
CC = "TLS 1.3, server CertificateVerify" if sent by Server
= "TLS 1.3, client CertificateVerify" if sent by Client
SignatureAlgorithm uses PrivateKey of first certificate in certificate_list
compatible with first certificate in certificate_list
uses one from signature_algorithms in ClientHello if CertificateVerify sent by Server
signature_algorithms in CertificateRequest if CertificateVerify sent by Client
if first CertificateEntry in certificate_list uses RSA
then SignatureAlgorithm = RSASSA-PSS
!= RSASSA-PKCS1-v1_5
SHA1 is not used
if receiver verification fails
then Alert decrypt_error
put in Handshake then put in TLSPlaintext then convert to TLSCiphertext then send to peer
Finished proves authentication of handshake and computed keys
struct{ opaque verify_data[Hash.length]; } Finished;
verify_data = HMAC(finished_key, Transcript-Hash(HandshakeContext, Certificate, CertificateVerify))
finished_key = HKDF-Expand-Label(BaseKey, "finished", "", Hash.length)
BaseKey = [sender]_handshake_traffic_secret
= [sender]_application_traffic_secret_N if sent Post-Handshake
Certificate only included if present
CertificateVerify only included if present
if receiver verification fails
then Alert decrypt_error
put in Handshake then put in TLSPlaintext then convert to TLSCiphertext then send to peer
EndOfEarlyData indicates 0-RTT ApplicationData messages have been transmitted
sent by Client
after Server Finished
if received EncryptedExtensions with early_data
struct {} EndOfEarlyData;
encrypted using client_early_traffic_secret
NewSessionTicket sent by Server
after Finished at any time
one or multiple times after each other or after specific events
used to create new SessionResume noRRT
SessionResume RRT
compatible with psk_key_exchange_modes
struct {
uint32 ticket_lifetime;
uint32 ticket_age_add;
opaque ticket_nonce<0..255>;
opaque ticket<1..2^16-1>;
Extension extensions<0..2^16-2>;
} NewSessionTicket;
ticket_lifetime time in seconds
<= 604800
BigEndian
if = 0 then ticket should be discarded immediately
validity may be shorter than the stated
ticket_age_add securely generated
unique value
obscures the age of ticket
ticket_nonce unique value for this connection
ticket used as PSK identity
an opaque label
may be either database lookup key
self-encrypted and self-authenticated value
extensions Clients ignores unrecognized extensions
may have early_data
if has early_data
then this NewSessionTicket can create new SessionResume RRT
if has no early_data
then this NewSessionTicket can create new SessionResume noRRT
(new Session using NewSessionTicket) cipher suite KDF hash algorithm is equal to
(this Session) cipher suite KDF hash algorithm
creates unique association between this ticket value
secret PSK derived from resumption master secret
different servers using same Certificate may accept each other's NewSessionTicket
KeyUpdate sent by Client
Server
then all subsequent messages from Sender use next generation of keys
if received before sender Finished
then Alert unexpected_message
struct { KeyUpdateRequest request_update; } KeyUpdate;
enum { update_not_requested(0), update_requested(1), (255) } KeyUpdateRequest;
request_update = 0 if received then respond with KeyUpdate request_update = 1
= 1 if received then no response needed
> 1 if received then Alert illegal_parameter
[sender]_application_traffic_secret_N+1 = HKDF-Expand-Label([sender]_application_traffic_secret_N,"traffic upd","",Hash.length)
first compute [sender]_application_traffic_secret_N+1
second update [sender]_Secret
third compute [sender]_write_key
[sender]_write_iv
ApplicationData is opaque to TLS
always encrypted
if theres nothing else to send then do close_notify
ChangeCipherSpec struct { enum { change_cipher_spec(1), (255) } type; } ChangeCipherSpec;
with TLSPlaintext.length = 1
TLSPlaintext.fragment = 0x01
Client if wants Middlebox compatability
then if in SessionResume RRT then sends after first ClientHello
if not in SessionResume RRT then sends before either second ClientHello
Client encrypted handshake
Server if wants Middlebox compatability or
received ClientHello with legacy_session_id = nonempty
then sends after either ServerHello
HelloRetryRequest
if received after first ClientHello in Session and
received before peer Finished and
has single byte value 0x01 and
not encrypted in TLSCiphertext
then ignore it
else Alert unexpected_message
Extension struct {
ExtensionType extension_type;
opaque extension_data<0..2^16-1>;
} Extension;
enum {
server_name(0), //RFC6066
max_fragment_length(1), //RFC6066
status_request(5), //RFC6066
supported_groups(10), //RFC8422 RFC7919
signature_algorithms(13), //RFC8446
use_srtp(14), //RFC5764
heartbeat(15), //RFC6520
application_layer_protocol_negotiation(16), //RFC7301
signed_certificate_timestamp(18), //RFC6962
client_certificate_type(19), //RFC7250
server_certificate_type(20), //RFC7250
padding(21), //RFC7685
pre_shared_key(41), //RFC8446
early_data(42), //RFC8446
supported_versions(43), //RFC8446
cookie(44), //RFC8446
psk_key_exchange_modes(45), //RFC8446
certificate_authorities(47), //RFC8446
oid_filters(48), //RFC8446
post_handshake_auth(49), //RFC8446
signature_algorithms_cert(50), //RFC8446
key_share(51), //RFC8446
(65535)
} ExtensionType;
table server_name CH EE
max_fragment_length CH EE
status_request CH CR CT
supported_groups CH EE
signature_algorithms CH CR
use_srtp CH EE
heartbeat CH EE
application_layer_protocol_negotiation CH EE
signed_certificate_timestamp CH CR CT
client_certificate_type CH EE
server_certificate_type CH EE
padding CH
key_share CH SH HRR
pre_shared_key CH SH
psk_key_exchange_modes CH
early_data CH EE NST
cookie CH HRR
supported_versions CH SH HRR
certificate_authorities CH CR
oid_filters CR
post_handshake_auth CH
signature_algorithms_cert CH CR
CH = ClientHello
SH = ServerHello
EE = EncryptedExtensions
CT = Certificate
CR = CertificateRequest
NST = NewSessionTicket
HRR = HelloRetryRequest
if extension is in a message not listed
then receiver Alert illegal_parameter
server_name Client must send to tell the Server name its contacting
received by Server then used to send apropiate Server certificate
then send server_name in EncryptedExtensions
ignored then send no server_name in EncryptedExtensions
helps Servers hosting multiple 'virtual' Servers at a single network address
must be supported
extension_data = ServerNameList if in ClientHello
extension_data = empty if in EncryptedExtensions
struct { ServerName server_name_list<1..2^16-1> } ServerNameList;
struct {
NameType name_type;
select (name_type) {
case host_name: HostName;
} name;
} ServerName;
enum { host_name(0), (255) } NameType;
opaque HostName<1..2^16-1>;
server_name_list has no repetitions of name_type
if ServerName is not recognized by Server
then abort Session with Alert unrecognized_name
continue Session
if ServerName mismatch between Client and Server
then client may decide to continue Session
host_name refers to DNS hostnames
HostName begins with a 16bit length
represented as byte string
case insensitive
uses ASCII without trailing dot
supports internationalized domain A-labels RFC5890
has no literal IPv4 and IPv6 addresses
algorithm to compare hostnames defined in RFC5890 Section 2.3.2.4
ServerName may be treated as opaque data
passed to the application using TLS
if Client wants to start SessionResume and
SNI provided externally is not valid for Server Certificate in SessionResume Initial
then Client must not start SessionResume
if Client wants to start SessionResume and
SNI provided externally is not equal to SNI in SessionResume Initial
then Client may not start SessionResume
SessionResume noRRT or RRT uses SNI in server_name ClientHello of this Session
not of SessionResume Initial
SNI Server Name Identification
defined internally through server_name
externally
no need for Server to associate SNI with each NewSessionTicket
may be stored with PSK by Clients
supported_versions indicates supported TLS versions by sender
used to select TLS version of Session
extension_data = SupportedVersions
struct {
select (Handshake.msg_type) {
case client_hello: ProtocolVersion versions<2..254>;
case server_hello: //and HelloRetryRequest
ProtocolVersion selected_version;
};
} SupportedVersions;
versions ordered preference descending
cookie allows Server to reach Client at their apparent network
must be supported
use steps 1 Client sends ClientHello with no Cookie
2 Server decides to send HelloRetryRequest
3 Server makes hash of ClientHello
4 Server protects hash with any integrity algorithm
5 Server puts protected hash in Cookie in HelloRetryRequest
6 Client responds with ClientHello with copy of Cookie
extension_data = Cookie
struct { opaque cookie<1..2^16-1>; } Cookie;
signature_algorithms used for CertificateVerify
Certificate if received no signature_algorithms_cert
extension_data = SignatureSchemeList
struct { SignatureScheme supported_signature_algorithms<2..2^16-2>; } SignatureSchemeList;
supported_signature_algorithms ordered preference descending
enum {
//RSASSA-PKCS1-v1_5 algorithms RFC8017
//used only in Certificate
rsa_pkcs1_sha256(0x0401), //must be supported for Certificate
rsa_pkcs1_sha384(0x0501),
rsa_pkcs1_sha512(0x0601),
//ECDSA algorithms
//curve defined in ANSI ANS X9.62-2005, FIPS 186-4
//DER ecoded ECDSA-Sig-Value
ecdsa_secp256r1_sha256(0x0403), //must be supported for CertificateVerify
ecdsa_secp384r1_sha384(0x0503),
ecdsa_secp521r1_sha512(0x0603),
//RSASSA-PSS algorithms RFC8017
//MGF1 mask generation function 1
//Salt length = hash algorithm output length
//PublicKey in X.509 uses rsaEncryption OID RFC5280
rsa_pss_rsae_sha256(0x0804), //must be supported for Certificate and CertificateVerify
rsa_pss_rsae_sha384(0x0805),
rsa_pss_rsae_sha512(0x0806),
//EdDSA algorithms
//PureEdDSA
//no prehash
//RFC8032
ed25519(0x0807),
ed448(0x0808),
//RSASSA-PSS algorithms RFC8017
//mask generation function 1
//Salt length = hash algorithm output length
//PublicKey in X.509 uses RSASSA-PSS OID RFC5756
//Certificate signature parameters are DER encoded
//PublicKey parameters = signature parameters
rsa_pss_pss_sha256(0x0809),
rsa_pss_pss_sha384(0x080a),
rsa_pss_pss_sha512(0x080b),
//Legacy algorithms
//deprecated
//not negotiated
rsa_pkcs1_sha1(0x0201),
ecdsa_sha1(0x0203),
//Reserved Code Points
private_use(0xFE00..0xFFFF),
(0xFFFF)
} SignatureScheme;
signature_algorithms_cert signature algorithms for Certificate
must be supported
extension_data = SignatureSchemeList
certificate_authorities supported by sender
used by receiver to fill certificate_list
extension_data = CertificateAuthoritiesExtension
struct {
DistinguishedName authorities<3..2^16-1>;
} CertificateAuthoritiesExtension;
opaque DistinguishedName<1..2^16-1>;
authorities uses DER
ITU-T X.501
can be TrustAnchor subordinate CA
oid_filters sent by Server for Client Certificate
extension_data = OIDFilterExtension
struct { OIDFilter filters<0..2^16-1>; } OIDFilterExtension;
struct {
opaque certificate_extension_oid<1..2^8-1>;
opaque certificate_extension_values<0..2^16-1>;
} OIDFilter;
filters has no OIDFilter repeated
OIDFilter uses DER
one _oid can have multiple _values
has no special anyExtendedKeyUsage OID
post_handshake_auth sent by Client
indicates Clients could send Certificate after Finished
extension_data = PostHandshakeAuth
size = 0
struct {} PostHandshakeAuth;
supported_groups sent by Client then used by Server in this Session key exchange
sent by Server then used by Client in next Session to fill key_share
indicates all groups supported by sender
extension_data = NamedGroupList
struct { NamedGroup named_group_list<2..2^16-1>; } NamedGroupList;
named_group_list ordered preference descending
enum {
//Elliptic Curve Groups (ECDHE) FIPS 186-4 RFC7748 http://www.secg.org/sec2-v2.pdf
secp256r1(0x0017),//must be supported
secp384r1(0x0018),
secp521r1(0x0019),
x25519(0x001D), x448(0x001E),
//Finite Field Groups (DHE) RFC7919
ffdhe2048(0x0100), ffdhe3072(0x0101), ffdhe4096(0x0102),
ffdhe6144(0x0103), ffdhe8192(0x0104),
//Reserved Code Points
ffdhe_private_use(0x01FC..0x01FF),
ecdhe_private_use(0xFE00..0xFEFF),
(0xFFFF)
} NamedGroup;
key_share defines cryptographic paramenters for Session
extension_data = KeyShareClientHello if in ClientHello
extension_data = KeyShareServerHello if in ServerHello
extension_data = KeyShareHelloRetryRequest if in HelloRetryRequest
struct { KeyShareEntry client_shares<0..2^16-1>; } KeyShareClientHello;
struct { KeyShareEntry server_share; } KeyShareServerHello;
struct { NamedGroup selected_group; } KeyShareHelloRetryRequest;
struct {
NamedGroup group;
opaque key_exchange<1..2^16-1>;
} KeyShareEntry;
client_shares multiple KeyShareEntry if sent before HelloRetryRequest
selected from supported_groups ClientHello
with same order as supported_groups ClientHello
may not have all from supported_groups ClientHello
NamedGroup different from other NamedGroup in client_shares
key_exchange generated independently from other key_exchange in client_shares
one KeyShareEntry if sent after HelloRetryRequest
selected from selected_group HelloRetryRequest
empty if Client wants HelloRetryRequest
if received and breaks any rule
then Server Alert illegal_parameter
selected_group one NamedGroup selected from supported_groups of ClientHello
not sent if using psk_ke PskKeyExchangeMode
if received and breaks any rule
then Client Alert illegal_parameter
server_share has NamedGroup = one in client_shares ClientHello if sent before HelloRetryRequest
= selected_group HelloRetryRequest if sent after HelloRetryRequest
not sent if using psk_ke PskKeyExchangeMode
if received and breaks any rules
then Client Alert illegal_parameter
key_exchange = PublicValue
DH Finite Field Diffie-Hellman
defined in RFC7919 Appendix A
PublicValue = Y for the specified group RFC7919
= g^X mod p
1 < Y < p-1
BigEndian
left padded with zeros
size with pad = p bytes
these rules are verified by receiver
in one DH group the padding results in all public keys having same length
ECDHE Elliptic Curve Diffie-Hellman
if secp256r1 or secp384r1 or secp521r1
then PublicValue = UncompressedPointRepresentation
struct {
uint8 legacy_form = 4;
opaque X[coordinate_length];
opaque Y[coordinate_length];
} UncompressedPointRepresentation;
X Y each BigEndian
left padded with zeros
size with pad = 32 if secp256r1
48 if secp384r1
66 if secp521r1
have correct interval
solution of eliptic curve equation
point not at infinity
these rules are verified by receiver
no need to verify membership in correct subgroup
if X25519 or X448
then PublicValue = byte strings inputs and outputs of functions from RFC7748
size = 32 if X25519
56 if X448
psk_key_exchange_modes supported by Client
sent by Client
with pre_shared_key else Server abort Session
used in current Session PSKs
NewSessionTicket
extension_data = PskKeyExchangeModes
struct { PskKeyExchangeMode ke_modes<1..255>; } PskKeyExchangeModes;
enum { psk_ke(0), psk_dhe_ke(1), (255) } PskKeyExchangeMode;
psk_ke PSK only key establishment mode
Server then sends no key_share
psk_dhe_ke PSK with (EC)DHE key establishment mode
Client and Server then send key_share
early_data extension_data = EarlyDataIndication
struct {
select (Handshake.msg_type) {
case new_session_ticket: uint32 max_early_data_size;
case client_hello: Empty;
case encrypted_extensions: Empty;
};
} EarlyDataIndication;
struct {} Empty;
max_early_data_size = 0-RTT ApplicationData size max limit
counts TLSInnerPlaintext.content
does not count TLSInnerPlaintext.type
TLSInnerPlaintext.zeros
if received 0-RTT ApplicationData size > max_early_data_size
then Server may Alert unexpected_message
if received NewSessionTicket with early_data
then Client can use it for new SessionResume RRT
if received ClientHello with early_data
then Server do SessionRetry sending HelloRetryRequest and
ignoring 0-RRT using max_early_data_size
SessionResume RRT ignoring 0-RRT using max_early_data_size
until bein able to decrypt 1-RRT data
EncryptedExtensions will have no early_data
decrypting 0-RRT
EncryptedExtensions will have early_data
if 0-RRT decryption fails
then Alert bad_record_mac
pre_shared_key extension_data = PreSharedKeyExtension
struct {
select (Handshake.msg_type) {
case client_hello: OfferedPsks;
case server_hello: uint16 selected_identity;
};
} PreSharedKeyExtension;
struct {
PskIdentity identities<7..2^16-1>;
PskBinderEntry binders<33..2^16-1>;
} OfferedPsks;
struct {
opaque identity<1..2^16-1>;
uint32 obfuscated_ticket_age;
} PskIdentity;
opaque PskBinderEntry<32..255>;
selected_identity 0-base index value
selected by Server
from OfferedPsks
then validates binder
if validation fails
then abort Session and do not select another OfferedPsks
received by Client then validates 0-base index value range
binders hash matches CipherSuite ServerHello
key_share is present if required by psk_key_exchange_modes
if validation fails
then Abort illegal_parameter
= 0 indicates Server is not ignoring 0-RRT data
EncryptedExtensions will have early_data
then Server validates binder associated TLS version
CipherSuite
ALPN RFC7301 if any
if validation fails
then send no pre_shared_key and
do SessionRetry sending HelloRetryRequest and
ignoring 0-RRT using max_early_data_size
SessionResume RRT ignoring 0-RRT using max_early_data_size
until bein able to decrypt 1-RRT data
EncryptedExtensions will have no early_data
if != 0 and receives EncryptedExtensions with early_data
then Client Alert illegal_parameter
identities list supported by Client
binders list of HTHT values
one for each value in identities
same order as identities
each uses hash algorithm defined by CipherSuite from previous Session with NewSessionTicket
externally with PSK established externally
if not defined then use SHA-256
HTHT = HMAC(finished_key, Transcript-Hash(Truncate(ClientHello))) if not in SessionRetry
= HMAC(finished_key, Transcript-Hash(ClientHello1, HelloRetryRequest, Truncate(ClientHello2))) if in SessionRetry
finished_key = HKDF-Expand-Label(binder_key, "finished", "", Hash.length)
TruOut = Truncate()
only removes ClientHello.extensions.OfferedPsks.binders
all length fields of ClientHello are set as if binders was not removed
including overall length
extensions length
pre_shared_key length
identity label for PSK established internally using NewSessionTicket
externally
obfuscated_ticket_age = (TAGE + TAGEA) % 2^32
TAGE time since the receipt of NewSessionTicket
in milliseconds
< ticket_lifetime converted to milliseconds
TAGEA ticket_age_add converted to milliseconds
prevents passive observers from correlating connections unless tickets are reused
may be = 0 if established externally
if = 0 then its ignored by Server
Server validates TAGE = (obfuscated_ticket_age % 2^32) - TAGEA
Handshake struct{ HandshakeType msg_type;
uint24 length;
select (Handshake.msg_type) {
case client_hello: ClientHello;
case server_hello: ServerHello;
case end_of_early_data: EndOfEarlyData;
case encrypted_extensions: EncryptedExtensions;
case certificate_request: CertificateRequest;
case certificate: Certificate;
case certificate_verify: CertificateVerify;
case finished: Finished;
case new_session_ticket: NewSessionTicket;
case key_update: KeyUpdate;
};
} Handshake;
length = sizeof(Handshake)-4 bytes
enum { hello_request_RESERVED(0),
client_hello(1),
server_hello(2),
hello_verify_request_RESERVED(3),
new_session_ticket(4),
end_of_early_data(5),
hello_retry_request_RESERVED(6),
encrypted_extensions(8),
certificate(11),
server_key_exchange_RESERVED(12),
certificate_request(13),
server_hello_done_RESERVED(14),
certificate_verify(15),
client_key_exchange_RESERVED(16),
finished(20),
certificate_url_RESERVED(21),
certificate_status_RESERVED(22),
supplemental_data_RESERVED(23),
key_update(24),
message_hash(254),
(255)
} HandshakeType;
_RESERVED not used in TLS version 1.3
if received unknown HandshakeType
then Alert unexpected_message
RecordLayer all sent or received messages are processed through TLSRecords
TLSRecords TLSPlaintext
TLSCiphertext
TLSPlaintext struct {
ContentType type;
ProtocolVersion legacy_record_version;
uint16 length;
opaque fragment[TLSPlaintext.length];
} TLSPlaintext;
enum {
invalid(0),
change_cipher_spec(20),
alert(21),
handshake(22),
application_data(23),
(255)
} ContentType;
legacy_record_version deprecated
must be = 0x0303 if fragment contains no first ClientHello in Session
may be = 0x0301 if fragment contains first ClientHello in Session and
Client wants compatability with previous TLS versions
length in bytes
<= 2^14 bytes else Alert record_overflow
fragment block of data being transmitted
may contain more than one Handshake if type = handshake(22) and
no other type in between
less than one Handshake if type = handshake(22)
if key change occurs not at fragment boundary
then receiver Alert unexpected_message
has only one Alert if type = alert(21)
may be empty if type = application_data(23)
is not empty if type = handshake(22)
TLSCiphertext uses AEAD Authenticated Encryption with Associated Data
algorithm expansion < 256 bytes for TLS 1.3
RFC5116
struct {
ContentType opaque_type = application_data(23);
ProtocolVersion legacy_record_version = 0x0303;
uint16 length;
opaque encrypted_record[TLSCiphertext.length];
} TLSCiphertext;
opaque_type for Middlebox compatibility
actual content type in TLSInnerPlaintext.type
legacy_record_version = 0x0303 = TLS v1.2
length = size of TLSInnerPlaintext + AEAD algorithm expansion
<= 2^14 + 256 bytes else Alert record_overflow
encrypted_record = AEADEncrypted = AEAD-Encrypt(write_key, preRecordNonceW, additional_data, plaintext)
write_key = [sender]_write_key
preRecordNonceW = padedNonceW XOR [sender]_write_iv
padedNonceW = zerosPad || write_nonce
size = iv_length
write_nonce 64bit integer
BigEndian
= write_nonce+1 each time after running AEAD-Encrypt
= 0 if start of Session or
changing [sender]_write_key
never wraps
if about to wrap then close Session or
do KeyUpdate
additional_data = TLSCiphertext.opaque_type ||
TLSCiphertext.legacy_record_version ||
TLSCiphertext.length
plaintext = TLSInnerPlaintext
struct {
opaque content[TLSPlaintext.length];
ContentType type;
uint8 zeros[length_of_padding];
} TLSInnerPlaintext;
TLSInnerPlaintext content = TLSPlaintext.fragment
length <= 2^14
type = TLSPlaintext.type
zeros = 0
length is customizable
used to hide length of content
if type = application_data
then content length may = 0
if type = Handshake or Alert
and content length = 0
then Alert unexpected_message
AEADDecrypted = AEAD-Decrypt(write_key, preRecordNonceR, additional_data, AEADEncrypted)
= plaintext
write_key = [sender]_write_key
preRecordNonceR = padedNonceR XOR [sender]_write_iv
padedNonceR = zerosPad || read_nonce
size = iv_length
read_nonce 64bit integer
BigEndian
= read_nonce+1 each time after running AEAD-Decrypt
= 0 if starting of Session
changing [sender]_write_key
never wraps
if its going to wrap then close Session
do KeyUpdate
additional_data = TLSCiphertext.opaque_type ||
TLSCiphertext.legacy_record_version ||
TLSCiphertext.length
plaintext = TLSInnerPlaintext
read backwards counting each byte until nonZeroByte
this count = length_of_padding
this nonZeroByte = TLSInnerPlaintext.type
if fails then Alert bad_record_mac
CipherSuite from TLS = 1.3 cannot be used for TLS <= 1.2
TLS <= 1.2 cannot be used for TLS = 1.3
all ciphers modeled as AEAD
uint8 CipherSuite[2];
TLS_AES_128_GCM_SHA256 = {0x13,0x01}; //RFC5116
TLS_AES_256_GCM_SHA384 = {0x13,0x02}; //RFC5116
TLS_CHACHA20_POLY1305_SHA256 = {0x13,0x03}; //RFC8439
TLS_AES_128_CCM_SHA256 = {0x13,0x04}; //RFC5116
TLS_AES_128_CCM_8_SHA256 = {0x13,0x05}; //RFC6655
TLS_AES_128_GCM_SHA256 must be supported
AEAD_AES_128_GCM AEAD-Encrypt = GCM-AE K (IV, P, A)
AEAD-Decrypt = GCM-AD K (IV, C, A, T)
K = write_key
size = 16 bytes = key_length
IV = preRecordNonceW if Encrypt
= preRecordNonceR if Decrypt
size = 12 bytes = iv_length
P = plaintext
size <= 2^36 - 31
A = additional_data
C = encrypted_record wihtouth last 16 bytes
T = last 16 bytes in encrypted_record
https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
CIPH = Cipher(byte in[4*Nb], byte out[4*Nb], word w[Nb*(Nr+1)])
used in GCM-AE
GCM-AD
https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197.pdf
SHA256 = Hash
output size = 32 bytes
[sender]_write_key = HKDF-Expand-Label([sender]_Secret, "key", "", key_length)
[sender]_write_iv = HKDF-Expand-Label([sender]_Secret, "iv", "", iv_length)
if ContentType = Handshake then [sender]_Secret = [sender]_handshake_traffic_secret
ContentType = ApplicationData then [sender]_Secret = [sender]_application_traffic_secret_N
ContentType = ApplicationData and 0-RTT then [sender]_Secret = client_early_traffic_secret
HandshakeContext = ClientHello || ... || later of EncryptedExtensions/CertificateRequest if sent by Server
= ClientHello || ... || later of Server Finished/EndOfEarlyData if sent by Client
= ClientHello || ... || Client Finished + CertificateRequest if sent Post-Handshake
EarlySecret = HKDF-Extract(HLZ, PSK)
PSK = pre-shared key established externally if PSK is used in Session
= HKDF-Expand-Label(resumption_master_secret, "resumption", ticket_nonce, Hash.length) if PSK is used in Session
= HLZ if PSK is not used in Session
binder_key = Derive-Secret(EarlySecret, BinderStrg, "")
client_early_traffic_secret = Derive-Secret(EarlySecret, "c e traffic", ClientHello)
early_exporter_master_secret = Derive-Secret(EarlySecret, "e exp master", ClientHello)
BinderStrg = "ext binder" if PSK was provisioned outside of TLS
= "res binder" if PSK was provisioned as resumption master secret of previous handshake
HandshakeSecret = HKDF-Extract(Derive-Secret(EarlySecret, "derived", ""), (EC)DHESharedSecret)
client_handshake_traffic_secret = Derive-Secret(HandshakeSecret, "c hs traffic", ClientHello...ServerHello)
server_handshake_traffic_secret = Derive-Secret(HandshakeSecret, "s hs traffic", ClientHello...ServerHello)
MasterSecret = HKDF-Extract(Derive-Secret(HandshakeSecret, "derived", ""), HLZ)
client_application_traffic_secret_0 = Derive-Secret(MasterSecret, "c ap traffic", ClientHello...Server Finished)
server_application_traffic_secret_0 = Derive-Secret(MasterSecret, "s ap traffic", ClientHello...Server Finished)
exporter_master_secret = Derive-Secret(MasterSecret, "exp master", ClientHello...Server Finished)
resumption_master_secret = Derive-Secret(MasterSecret, "res master", ClientHello...Client Finished)
if a secret is not available
then secret = HLZ
HLZ = 0
size = Hash.length
Derive-Secret(Secret, Label, Messages) = HKDF-Expand-Label(Secret, Label, Transcript-Hash(Messages), Hash.length)
HKDF-Expand-Label(Secret, Label, Context, Length) = HKDF-Expand(Secret, HkdfLabel, Length)
put Context inside HkdfLabel
struct {
uint16 length = Length;
opaque label<7..255> = "tls13 " + Label;
opaque context<0..255> = Context;
} HkdfLabel;
(EC)DHESharedSecret = SharedSecret
Finite Field Diffie-Hellman
SharedSecret = negotiated key(Z)
converted to byte array
BigEndian
LeftPadded with zeros
size of the prime
Elliptic Curve Diffie-Hellman
RFC7748
if secp256r1 or secp384r1 or secp521r1
then SharedSecret = FE2OSP(Z)
Field Element to Octet String Conversion Primitive
Z = x-coordinate of ECDH shared secret curve point
defined in IEEE Std. 1363-2000
= x-coordinate in big endian
size = 32 bytes if secp256r1
size = 48 bytes if secp384r1
size = 66 bytes if secp521r1
if X25519 or X448
then KeyShareEntry.key_exchange public key = ScalMul(SecKe, StPuBsp)
SecKe secret key
has appropriate length
StPuBsp standard public basepoint
SharedSecret = ScalMul(SecKe, PePuKe)
SecKe = secret key
PePuKe = peer's public key
ScalMul = ScalMul( scalar, u-coordinate point )
ECDH scalar multiplication function
HMAC keyed-hash message authentication code
used to verify data integrity of a message
not vulnerable to LengthExtensionAttack
key is a secret cryptographic key
hash is an algorithm like MD5
SHA256
RFC2104
= HMAC(key, message)
= hash( (k xor opad) || hash( (k xor ipad) || message ) )
k = key if keySize = hashBlockSize
= key || zpad if keySize < hashBlockSize
= hash(key) || zpad if keySize > hashBlockSize
zpad = all bytes set to 0x00
size = hashBlockSize - keySize if keySize < hashBlockSize
= hashBlockSize - hashOutputSize if keySize > hashBlockSize
opad = all bytes set to 0x5c
size = hashBlockSize
ipad = all bytes set to 0x36
size = hashBlockSize
if hash = SHA256
then hashBlockSize = 512bits = 64bytes
hashOutputSize = 256bits = 32bytes
HKDF HMAC-based Extract-and-Expand Key Derivation Function
RFC5869
PRK pseudo random key
= HKDF-Extract(salt, IKM)
= HMAC-Hash(salt, IKM)
salt optional
random
non secret
used by HMAC as the key
IKM input keying material
used by HMAC as the message
Hash defined by CipherSuite
output length = HashLen
if salt not provided
then salt = all bytes set to zero
size = HashLen
OKM output keying material
= HKDF-Expand(PRK, info, L)
= first L octets of T
= generate T until its size >= L
T = T(1) || T(2) || T(3) || ... || T(N)
N = ceil(L/HashLen)
T(0) = empty string zero length
T(1) = HMAC-Hash(PRK, T(0) || info || 0x01)
T(2) = HMAC-Hash(PRK, T(1) || info || 0x02)
T(3) = HMAC-Hash(PRK, T(2) || info || 0x03) ...
|| means concatenation
counter 0x01 0x02 0x03 ... size = 8bits
PRK size >= HashLen
info optional context
application specific information
may be zerolength string
L length of OKM in bytes
<= 255*HashLen
TanscriptHash Transcript-Hash(M1, M2, ... Mn) = Hash(M1 || M2 || ... || Mn)
each M# = TLSPlaintext.fragment
may have ClientHello || HelloRetryRequest || ClientHello || ServerHello ||
EncryptedExtensions || Server CertificateRequest || Server Certificate ||
Server CertificateVerify || Server Finished || EndOfEarlyData ||
Client Certificate || Client CertificateVerify || Client Finished
if Session has HelloRetryRequest
then Transcript-Hash(cli1hush, HelloRetryRequest, ... Mn) = Hash( cli1hush || HelloRetryRequest || ... || Mn)
cli1hush = Handshake.msg_type = message_hash = 254
.length = HashLen
.content = Hash(TLSPlaintext.fragment of ClientHello1)
TLS-Exporter(label, context_value, key_length) = HKDF-Expand-Label( Derive-Secret(SecretExp, label, ""),
"exporter",
Hash(context_value),
key_length )
SecretExp = exporter_master_secret
= early_exporter_master_secret
used in 0-RTT
if no context provided
then context_value = zero length
StateMachine [] indicates actions for specific circumstances
"K_{send,recv} = foo" means set send/recv key to the given key
Client
START <----+
Send ClientHello | | Recv HelloRetryRequest
[K_send = early data] | |
v |
/ WAIT_SH ----+
| | Recv ServerHello
| | K_recv = handshake
Can | V
send | WAIT_EE
early | | Recv EncryptedExtensions
data | +--------+--------+
| Using | | Using certificate
| PSK | v
| | WAIT_CERT_CR
| | Recv | | Recv CertificateRequest
| | Certificate | v
| | | WAIT_CERT
| | | | Recv Certificate
| | v v
| | WAIT_CV
| | | Recv CertificateVerify
| +> WAIT_FINISHED <+
| | Recv Finished
\ | [Send EndOfEarlyData]
| K_send = handshake
| [Send Certificate [+ CertificateVerify]]
Can send | Send Finished
app data --> | K_send = K_recv = application
after here v
CONNECTED
clients may send Alerts from post-ServerHello messages in the clear or
with the early data keys
if sending these Alerts
then clients should first rekey to the handshake keys
Server
START <-----+
Recv ClientHello | | Send HelloRetryRequest
v |
RECVD_CH ----+
| Select parameters
v
NEGOTIATED
| Send ServerHello
| K_send = handshake
| Send EncryptedExtensions
| [Send CertificateRequest]
Can send | [Send Certificate + CertificateVerify]
app data | Send Finished
after --> | K_send = application
here +--------+--------+
No 0-RTT | | 0-RTT
| |
K_recv = handshake | | K_recv = early data
[Skip decrypt errors] | +------> WAIT_EOED -+
| | Recv | | Recv EndOfEarlyData
| | early data | | K_recv = handshake
| +------------+ |
| |
+> WAIT_FLIGHT2 <--------+
|
+--------+--------+
No auth | | Client auth
| |
| v
| WAIT_CERT
| Recv | | Recv Certificate
| empty | v
| Certificate | WAIT_CV
| | | Recv
| v | CertificateVerify
+-> WAIT_FINISHED <---+
| Recv Finished
| K_recv = application
v
CONNECTED
Alert struct {
AlertLevel level;
AlertDescription description;
} Alert;
enum { warning(1), fatal(2), (255) } AlertLevel;
enum {
close_notify(0),
unexpected_message(10),
bad_record_mac(20),
record_overflow(22),
handshake_failure(40),
bad_certificate(42),
unsupported_certificate(43),
certificate_revoked(44),
certificate_expired(45),
certificate_unknown(46),
illegal_parameter(47),
unknown_ca(48),
access_denied(49),
decode_error(50),
decrypt_error(51),
protocol_version(70),
insufficient_security(71),
internal_error(80),
inappropriate_fallback(86),
user_canceled(90),
missing_extension(109),
unsupported_extension(110),
unrecognized_name(112),
bad_certificate_status_response(113),
unknown_psk_identity(115),
certificate_required(116),
no_application_protocol(120),
(255)
} AlertDescription;
level ignored by receiver
replaced by Class in TLS v1.3
Class defined in each AlertDescription
Closure notifies connection is ending
used to avoid truncation attack
level = warning(1)
Error level = fatal(2)
if sent or received
then immediately close connection
Unknown = Error
close_notify send if sender will not send any more messages on this Session
user cancels operation after Handshake complete
if received then subsecuent received messages are ignored
if sent then subsecuent received messages are not ignored
Class Closure
user_canceled send if canceling Handshake not because of protocol failure
may be followed by close_notify
Class Closure
unexpected_message send if inappropriate message was received
Example wrong order of handshake messages
received prematurely ApplicationData
Class Error
bad_record_mac send if received record cannot be deprotected
Class Error
record_overflow send if received TLSCiphertext size > 2^14 + 256 bytes
TLSPlaintext size > 2^14 bytes
Class Error
handshake_failure send if unacceptable security parameters from available options
Class Error
bad_certificate send if received corrupt certificate
Example certificate signatures did not verify correctly
Class Error
unsupported_certificate send if received certificate has unsupported type
Class Error
certificate_revoked send if received certificate was revoked by its signer.
Class Error
certificate_expired send if received certificate expired or is invalid
Class Error
certificate_unknown send if received certificate has unspecified issue
Class Error
illegal_parameter send if received Handshake with incorrect or inconsistent fields
message is syntactically correct but semantically invalid
Class Error
unknown_ca send if received valid certificate chain or partial chain and
CA certificate could not be located or matched with known TrustAnchor
Class Error
access_denied send if received valid certificate or PSK and
access control applied and
decide not to proceed with negotiation
Class Error
decode_error send if received message has incorrect protocol syntax
some field out of range
incorrect length
Class Error
decrypt_error send if handshake cryptographic operation failed
Example unable to verify Signature
validate Finished
PskBinder
invalid PSK identity
Class Error
protocol_version send if received TLS version is recognized and
not supported
Class Error
insufficient_security send if negotiated handshake parameters are acceptable and
more secure needed
Class Error
internal_error send if internal error unrelated to peer or protocol correctness
Class Error
inappropriate_fallback Server sends if ClientHello.cipher_suites has TLS_FALLBACK_SCSV and
highest protocol version supported by Server is higher than ClientHello.client_version
RFC7507
Class Error
missing_extension send if received Handshake wihtout mandatory extension for TLS version
other negotiated parameters
Class Error
unsupported_extension send if received Handshake with prohibited extension
extensions in ServerHello not offered in ClientHello
Certificate not offered in CertificateRequest
Class Error
unrecognized_name Server sends if no Server name can be found from server_name ClientHello
RFC6066
Class Error
bad_certificate_status_response Client sends if received status_request has invalid OCSP
RFC6066
Class Error
unknown_psk_identity Server may send if PSK key establishment is desired and
no acceptable PSK identity is provided by Client
may not sent if invalid PSK identity
Class Error
certificate_required Server sends if Client certificate is desired and
none was provided
Class Error
no_application_protocol Server sends if all Client application_layer_protocol_negotiation protocols are not supported
RFC7301
Class Error
Middlebox if terminating a TLS connection
then behave as compliant TLS Server to original Client
Client to original Server
have certificate which the client is willing to accept
verify original Server Certificate
generate own ClientHello with parameters it understands
random in ServerHello
if forwarding ClientHello parameters it does not understand
then process no messages beyond ClientHello
forward all subsequent traffic unmodified
FullConnection Test Vectors https://tools.ietf.org/html/rfc8448
RFC8017
MGF1 mask generation function
mask = MGF(mgfSeed, maskLen)
mgfSeed seed from which mask is generated
maskLen octet length of mask
<= 2^32 hLen else mask = "mask too long"
T starts as empty octet string
for counter from 0 to ceil(maskLen / hLen) - 1
do C = I2OSP (counter, 4)
T = T || Hash(mgfSeed || C)
mask = leading maskLen octets of T
I2OSP Integer-to-Octet-String primitive
X = I2OSP(x, xLen)
= X_1 X_2 ... X_xLen
X_i = x_(xLen-i) for 1 <= i <= xLen
octet length = xLen
x nonnegative integer to be converted
< 256^xLen else X = "integer too large"
= x_(xLen-1) 256^(xLen-1) +
x_(xLen-2) 256^(xLen-2) + ... +
x_1 256^1 +
x_0 256^0
x_i = a digit from x represented in base 256
>= 0
< 256
if x < 256^(xLen-1)
then one or more leading x_i will be zero
OS2IP Octet-String-to-Integer primitive
x = OS2IP(X)
nonnegative integer
X_1 X_2 ... X_xLen = the octets of X from first to last
x_(xLen-i) = integer value of octet X_i for 1 <= i <= xLen
x = x_(xLen-1) 256^(xLen-1) +
x_(xLen-2) 256^(xLen-2) + ... +
x_1 256^1 +
x_0 256^0
RSAEP c = RSAEP((n,e), m)
= m^e mod n
(n,e) valid RSA public key
m message representative
>= 0 else c = "message representative out of range"
< n else c = "message representative out of range"
RSADP m = RSADP(K, c)
< n
K valid RSA public key
c ciphertext representative
>= 0 else m = "message representative out of range"
< n else m = "ciphertext representative out of range"
if K = (n, d)
then m = c^d mod n
if K = (p, q, dP, dQ, qInv) and a possibly empty sequence of triplets (r_i, d_i, t_i), i = 3, ..., u
then m_1 = c^dP mod p
m_2 = c^dQ mod q
if u > 2
then m_i = c^(d_i) mod r_i, i = 3, ..., u
h = (m_1 - m_2) * qInv mod p
m = m_2 + q * h
if u > 2
then R = r_1 and for i = 3 to u
do R = R * r_(i-1)
h = (m_i - m) * t_i mod r_i
m = m + R * h
RSASP1 s = RSASP1(K, m)
< n
m message representative
>= 0 else s = "message representative out of range"
< n else s = "message representative out of range"
K valid RSA public key
if K = (n, d)
then s = m^d mod n
if K = (p, q, dP, dQ, qInv) and a possibly empty sequence of triplets (r_i, d_i, t_i), i = 3, ..., u
then s_1 = m^dP mod p
s_2 = m^dQ mod q
If u > 2
then s_i = m^(d_i) mod r_i, i = 3, ..., u
h = (s_1 - s_2) * qInv mod p
s = s_2 + q * h
if u > 2
then R = r_1 and for i = 3 to u
do R = R * r_(i-1)
h = (s_i - s) * t_i mod r_i
s = s + R * h
RSAVP1 m = RSAVP1((n,e), s)
= s^e mod n
(n,e) valid RSA public key
s signature representative
>= 0 else m = "signature representative out of range"
< n else m = "signature representative out of range"
RSAES-OAEP-ENCRYPT C = RSAES-OAEP-ENCRYPT((n,e), M, L)
(n,e) recipient's RSA public key
k octet length of n
M message to be encrypted
octet length = mLen
<= k - 2*hLen - 2 else C = "message too long"
L optional label to be associated with the message
= emptyString if L is not provided
octet length < limit of Hash input else C = "label too long"
lHash = Hash(L)
octet length = hLen
if L = emptyString
then lHash = (0x)da39a3ee 5e6b4b0d 3255bfef 95601890 afd80709 ---> look ---> if Hash = SHA-1
(0x)e3b0c442 98fc1c14 9afbf4c8 996fb924 27ae41e4 649b934c a495991b 7852b855 if Hash = SHA-256
(0x)38b060a7 51ac9638 4cd9327e b1b1e36a 21fdb711 14be0743 4c0cc7bf 63f6e1da 274edebf e76f65fb d51ad2f1 4898b95b if Hash = SHA-384
(0x)cf83e135 7eefb8bd f1542850 d66d8007 d620e405 0b5715dc 83f4a921 d36ce9ce 47d0d13c 5d85f2b0 ff8318d2 877eec2f 63b931bd 47417a81 a538327a f927da3e if Hash = SHA-512
PS padding string
value = 0
octet length = k - mLen - 2*hLen - 2
= may be 0
DB = lHash || PS || 0x01 || M
octet length = k - hLen - 1
seed = random bits
octet length = hLen
dbMask = MGF(seed, k - hLen - 1)
maskedDB = DB xor dbMask
seedMask = MGF(maskedDB, hLen)
maskedSeed = seed xor seedMask
EM = 0x00 || maskedSeed || maskedDB
octet length = k
m = OS2IP(EM)
c = RSAEP ((n,e), m)
C = I2OSP (c, k)
_________________________________________________________
| +----------+------+--+-------+ |
| DB = | lHash | PS |01| M | |
| +----------+------+--+-------+ |
| | |
| +----------+ | |
| | seed | | |
| +----------+ | |
| | | |
| |-------> MGF ---> xor |
| | | |
| +--+ V | |
| |00| xor <----- MGF <-----| |
| +--+ | | |
| | | | |
| V V V |
| +--+----------+----------------------------+ |
| EM = |00|maskedSeed| maskedDB | |
| +--+----------+----------------------------+ |
|_______________________________________________________|
RSAES-OAEP-DECRYPT M = RSAES-OAEP-DECRYPT(K, C, L)
message string
octet length = mLen
<= k - 2hLen - 2
K recipient's RSA private key
k = octet length of n
>= 2*hLen + 2 else M = "decryption error"
C ciphertext to be decrypted
octet length = k else M = "decryption error"
L optional label
= emptyString if L is not provided
octet length < limit of Hash input else M = "decryption error"
c = OS2IP(C)
m = RSADP(K,c)
if m = "ciphertext representative out of range"
then M = "decryption error"
EM = I2OSP(m,k)
octet length = k
lHash = Hash(L)
octet length = hLen
Y || maskedSeed || maskedDB = EM
Y octet length = 1
maskedSeed octet length = hLen
maskedDB octet length = k - hLen - 1
seedMask = MGF(maskedDB, hLen)
seed = maskedSeed xor seedMask
dbMask = MGF(seed, k - hLen - 1)
DB = maskedDB xor dbMask
lHash' || PS || 0x01 || M = DB
lHash' octet length = hLen
PS = 0
octet length = variable
= may be 0
if octet with value = 0x01 does not separate PS from M or
lHash does not equal lHash' or
Y = nonzero
then M = "decryption error"
vulnerable to timing attack, make sure oponent cannot distinguish different error conditions
RSAES-PKCS1-V1_5-ENCRYPT C = RSAES-PKCS1-V1_5-ENCRYPT((n,e), M)
ciphertext
octet length = k
(n,e) recipient's RSA public key
k = octet length of n
M message to be encrypted
octet length = mLen
<= k - 11 else C = "message too long"
PS padding string
value = random bits
length = k - mLen - 3
>= 8
EM = 0x00 || 0x02 || PS || 0x00 || M
octet length = k
m = OS2IP(EM)
c = RSAEP((n,e), m)
C = I2OSP (c, k)
octet length = k
RSAES-PKCS1-V1_5-DECRYPT M = RSAES-PKCS1-V1_5-DECRYPT(K, C)
octet length <= k - 11
K recipient's RSA private key
C ciphertext to be decrypted
octet length = k else M = "decryption error"
k = octet length of n
>= 11 else M = "decryption error"
c = OS2IP (C)
m = RSADP ((n, d), c)
if m = "ciphertext representative out of range"
then M = "decryption error"
EM = I2OSP (m, k)
octet length = k
0x00 || 0x02 || PS || 0x00 || M = EM
if EM first octet != 0x00 or
second octet != 0x02 or
has no 0x00 octet separating PS from M or
PS octet length < 8
then M = "decryption error"
vulnerable to timing attack, make sure oponent cannot distinguish different error conditions
RSASSA-PSS-SIGN S = RSASSA-PSS-SIGN(K, M)
octet length = k
k = octet length of n
modBits = bit length of n
K signer's RSA private key
M message to be signed
EM = EMSA-PSS-ENCODE (M, modBits - 1)
octet length = ceil ((modBits - 1)/8)
= k-1 if modBits-1 is divisible by 8
= k if modBits-1 is not divisible by 8
if EM = "message too long"
then S = "message too long"
if EM = "encoding error"
then S = "encoding error"
m = OS2IP (EM)
octet length < modBits
s = RSASP1 (K, m)
S = I2OSP (s, k)
RSASSA-PSS-VERIFY voi = RSASSA-PSS-VERIFY ((n,e), M, S)
(n,e) signer's RSA public key
M message whose signature is to be verified
S signature to be verified
octet length = k else voi = "invalid signature"
k = octet length of n
modBits = bit length of n
s = OS2IP (S)
m = RSAVP1 ((n, e), s)
if m = "signature representative out of range"
then voi = "invalid signature"
EM = I2OSP (m, emLen)
octet length = emLen = ceil ((modBits - 1)/8)
= k-1 if modBits-1 is divisible by 8
= k if modBits-1 is not divisible by 8
if EM = "integer too large"
then voi = "invalid signature"
Result = EMSA-PSS-VERIFY (M, EM, modBits - 1)
if Result = "consistent" then voi = "valid signature"
else voi = "invalid signature"
RSASSA-PKCS1-V1_5-SIGN S = RSASSA-PKCS1-V1_5-SIGN (K, M)
octet length = k
k = octet length of n
K signer's RSA private key
M message to be signed
EM = EMSA-PKCS1-V1_5-ENCODE (M, k)
octet length = k
if EM = "message too long"
then S = "message too long" and stop
if EM = "intended encoded message length too short"
then S = "RSA modulus too short"
m = OS2IP (EM)
s = RSASP1 (K, m)
S = I2OSP (s, k)
RSASSA-PKCS1-V1_5-VERIFY S = RSASSA-PKCS1-V1_5-VERIFY ((n,e), M, S)
signature to be verified
octet length = k else S = "invalid signature"
(n,e) signer's RSA public key
M message whose signature is to be verified
k = octet length of n
s = OS2IP (S)
m = RSAVP1 ((n, e), s)
if m = "signature representative out of range"
then S = "invalid signature"
EM = I2OSP (m, k)
octet length = k
if EM = "integer too large"
then S = "invalid signature"
EM' = EMSA-PKCS1-V1_5-ENCODE (M, k)
octet length = k
if EM' = "message too long"
then S = "message too long" and stop
if EM' = "intended encoded message length too short"
then S = "RSA modulus too short"
if EM = EM' then S = "valid signature"
else S = "invalid signature"
EMSA-PSS-ENCODE EM = EMSA-PSS-ENCODE (M, emBits)
octet length = emLen = ceil (emBits/8)
>= hLen + sLen + 2 else EM = "encoding error"
M message to be encoded
octet length < limit of Hash input else EM = "message too long"
emBits max bit length of the integer OS2IP(EM)
>= 8hLen + 8sLen + 9
mHash = Hash(M)
octet length = hLen
salt random octet string
octet length = sLen
= emptyString if sLen = 0
M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt
octet length = 8 + hLen + sLen
H = Hash(M')
octet length = hLen
PS = 0
octet length = emLen - sLen - hLen - 2
= may be 0
DB = PS || 0x01 || salt
octet length = emLen - hLen - 1
dbMask = MGF(H, emLen - hLen - 1)
maskedDB = DB xor dbMask
set leftmost 8*emLen - emBits bits of
leftmost octet = 0
EM = maskedDB || H || 0xbc
EMSA-PSS-VERIFY coi = EMSA-PSS-VERIFY (M, EM, emBits)
M message to be verified
octet length < limit of Hash input else coi = "inconsistent"
EM encoded message
octet length = emLen = ceil(emBits/8)
>= hLen + sLen + 2 else coi = "inconsistent"
rightmost octet = 0xbc else coi = "inconsistent"
emBits max bit length of the integer OS2IP(EM)
>= 8hLen + 8sLen + 9
mHash = Hash(M)
octet length = hLen
maskedDB = leftmost emLen - hLen - 1 octets of EM
H = next hLen octets of EM
if leftmost 8*emLen - emBits bits of
leftmost octet in maskedDB != 0
then coi = "inconsistent"
dbMask = MGF(H, emLen - hLen - 1)
DB = maskedDB xor dbMask
set leftmost 8*emLen - emBits bits of
leftmost octet = 0
if emLen - hLen - sLen - 2 leftmost octets != 0 or
emLen - hLen - sLen - 1 position octet != 1 leftmost position is "position 1"
then coi = "inconsistent"
salt = last sLen octets of DB
octet length = sLen
M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt
octet length = 8 + hLen + sLen
H' = Hash(M')
octet length = hLen
if H = H' then coi = "consistent"
else coi = "inconsistent"
EMSA-PKCS1-v1_5-ENCODE EM encoded message
= EMSA-PKCS1-v1_5-ENCODE (M, emLen)
= 0x00 || 0x01 || PS || 0x00 || T
= "message too long" if M length >= Hash input length limit
= "intended encoded message length too short" if emLen < tLen + 11
M message to be encoded
emLen octet length of EM
>= tLen + 11
PS = 0xff in each octet
octet length = emLen - tLen - 3
>= 8
T = (0x)30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 02 05 00 04 10 || H if Hash = MD2
(0x)30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 05 05 00 04 10 || H if Hash = MD5
(0x)30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14 || H if Hash = SHA-1
(0x)30 2d 30 0d 06 09 60 86 48 01 65 03 04 02 04 05 00 04 1c || H if Hash = SHA-224
(0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || H if Hash = SHA-256
(0x)30 41 30 0d 06 09 60 86 48 01 65 03 04 02 02 05 00 04 30 || H if Hash = SHA-384
(0x)30 51 30 0d 06 09 60 86 48 01 65 03 04 02 03 05 00 04 40 || H if Hash = SHA-512
(0x)30 2d 30 0d 06 09 60 86 48 01 65 03 04 02 05 05 00 04 1c || H if Hash = SHA-512/224
(0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 06 05 00 04 20 || H if Hash = SHA-512/256
octets length = tLen
H = Hash(M)
RSAPublicKey ::= SEQUENCE
{
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
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, -- (inverse of q) mod p CRT coefficient q^(-1) mod p
otherPrimeInfos OtherPrimeInfos OPTIONAL
}
Version ::= INTEGER { two-prime(0), multi(1) } (CONSTRAINED BY {-- version must be multi if otherPrimeInfos present --})
OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
OtherPrimeInfo ::= SEQUENCE
{
prime INTEGER, -- ri
exponent INTEGER, -- di
coefficient INTEGER -- ti
}
RSAPrivateKey binary file uses DER
Example using hexadecimal representation
data starts with --------> 30 82 04 A3 02 01 ...
RSAPrivateKey TagType SEQUENCE = 30
Length in next 2 bytes = 82
Length = 04 A3
version TagType INTEGER = 02
Length = 01
Value = 00
modulus TagType INTEGER = 02
Length in next 2 bytes = 82
Length = 01 01
Value = 00 AC 28 1A 29 41 88 7E FB E6 4D 35 B0 6D B9 99 59 C4 51 8D 13 DC C0 60 F7 BF EC 25 2C 07 C8 CC DF 76 9A 0F 86 47 61 CF 63 E8 7A D3 01 91 1C 97 21 F7 79 36 50 06 BB 8D 36 2E D4 B9 34 36 66 A6 FA 4D F2 07 AF 2D 28 7F 43 3F E8 06 D9 11 61 F0 BD 8A DE 5D DA B0 D4 4D DE 83 51 14 F2 6A 3F 5E F6 E5 7F C5 5D EB E7 05 FA 45 EE 91 98 3D 95 EE 8D 06 DF 4E 68 BF 29 8C 35 48 85 95 37 17 DC 1F D6 81 35 9D ED 8C 2C EA A3 91 17 FE 27 C0 F5 68 74 88 2C 1E FB 75 63 00 FA FA 28 09 2A E9 EC 73 92 3C 36 68 E6 79 FB A4 25 ED 17 46 EA E9 F5 14 89 F2 57 C6 BC E6 41 9A 64 20 71 B6 B9 B7 0B 41 63 19 84 20 52 86 6E 14 1A BE 9B C9 81 01 82 5A DD 53 A5 31 C7 C7 7C DA 0B B5 08 C5 5D 9F DF A2 A9 08 D1 3C D5 DA CA 43 DA 47 E3 8B 8E B3 FC 6A 34 87 B1 30 7E 2F FC 61 92 EC EC CE 16 4E 12 73 01
publicExponent TagType INTEGER = 02
Length = 03
Value = 01 00 01
privateExponent TagType INTEGER = 02
Length in next 2 bytes = 82
Length = 01 00
Value = 0E 97 9B 98 11 A0 F3 3A 80 CB DC 98 26 C4 CB 95 CB 49 2A 4F 1B B0 06 C8 19 31 77 F9 22 73 C7 C1 CD 38 C3 C6 1A F8 D4 6E 60 7F 40 B1 6A 4E 65 15 10 F7 EA 3D A0 44 77 74 B7 45 7A A9 4D 4F 28 2A 21 3F 39 9D 84 93 61 2E 1F 15 98 B6 FF 7D 30 BD B0 2E A6 FF E9 6B 25 6A 8D F0 EA 78 9D BE DF 5A C8 1B 2B 6C 89 99 5C 6D 59 4E 1E 73 2C 0D 30 5A 2D D1 F1 C0 79 2B 99 61 CE D2 DB D1 FE A8 BE DD F9 1D B5 42 67 35 78 B0 08 78 E6 72 D6 7D 1B 50 04 A5 D0 B3 AF 7D 86 F2 21 B1 C9 DC D7 54 26 0F 85 D9 0D 3E 1A 67 95 96 8D 11 62 2C A6 31 E8 38 06 F3 42 E7 7B 54 E8 FF CC 5F 23 6F BF 2C 7E F6 BA 07 BB 62 CC D3 30 68 44 70 D2 85 1B C0 AA 0C 3D 15 FE 28 C2 B8 86 A6 BD 33 6C F9 27 68 98 24 9D CD 77 1F D3 4B 4A 9F DF 03 23 5B 83 C2 C7 25 A6 3C D5 4A A0 F5 CD DF EC B0 3D F2 99 BD 2F 91
prime1 TagType INTEGER = 02
Length in next 1 bytes = 81
Length = 81
Value = 00 D7 A3 38 D0 3F CD 5A E4 68 4A 44 A9 69 5F 93 F2 09 CB AB 00 4C FA 1F 66 BC 7F 14 9B 27 38 03 13 7C 29 AA 3F 24 9F 6F 3B CF 1B 33 48 80 78 0D B0 AA 1A 39 50 22 8A 7F F7 9F 94 17 29 09 93 2F B2 A3 77 39 D1 43 92 FF 72 90 9B 30 71 42 9A B0 02 3B 05 E1 4A BF 78 76 6C 59 04 56 41 31 04 EB 25 C4 81 37 32 EF 94 61 F0 7C B2 DC DB 50 74 4A 58 BC 1E 29 12 9E EF 29 3A A5 AC 44 85 8C 17 24 6D
prime2 TagType INTEGER = 02
Length in next 1 bytes = 81
Length = 81
Value = 00 CC 61 63 C5 DA B0 36 DF 51 AF 09 2F 96 1F E8 A4 99 1D 0F AD 8A E1 F8 AE E4 F3 67 01 B8 0F 4C E3 08 D6 44 C0 B2 3C 2D A0 7D 62 01 32 F6 63 B5 0D 36 35 43 90 25 88 E9 43 D6 F4 65 DF 5C AB E8 6E ED D9 67 9C 06 CE DB 89 7E 8D D9 5A 4C 4D 91 D6 1C E5 C4 37 DD 25 2F 2A 7E E5 51 A2 E3 EB BE 23 A4 CF 5B 6E BB 52 BC 35 63 A3 3B 52 25 12 D7 E6 9B 7C 60 86 CA 7F CB 46 6A E9 F6 0E 4E 80 E4 65
exponent1 TagType INTEGER = 02
Length in next 1 bytes = 81
Length = 80
Value = 5A 1A 21 8B 22 9D B3 F4 EC 7E DB E1 CD DD D9 FB B0 8C 21 8F A6 9A 7C B4 78 DE C0 C5 73 C5 BB C3 50 86 38 54 DA 00 A4 81 E1 30 04 65 AF 08 7A EF EE A3 B6 7E FD 6F D1 B8 AE 3D 3A D0 32 E8 05 6E 27 4F 92 21 16 93 3D 99 A9 42 AF 23 24 29 6E 92 00 07 9C F3 96 BD C6 FD CE D4 39 16 54 5D 31 C4 3E 2F 9F D8 F0 B2 97 99 DD 00 FF B7 C4 0E 53 62 70 78 49 C1 36 17 C8 AA 70 BF 62 82 4A DF 00 A1
exponent2 TagType INTEGER = 02
Length in next 1 bytes = 81
Length = 80
Value = 0B 11 57 12 D6 D5 E6 12 CF DC 97 B5 C0 FE 77 5D BA 80 ED 61 6B 7C F3 37 9D 64 64 F6 9D DE 0E 3E D9 32 A9 44 7A 22 72 22 17 C8 1F E2 7F 9F 44 A5 B3 82 CC E6 D8 71 82 98 4E E2 AB 45 9E 42 1C F3 2C E4 32 0D 2B FD 35 BC 4B 63 29 1B 0F B8 BF A6 05 A8 97 A8 A2 CC 29 B2 B6 6E 7A AE 44 83 76 A7 7D 7C 51 2A 3C F0 AD 0A 83 1B CA D1 96 FA 5F 40 B8 B8 D2 8D 5A 17 F3 8D 1B AB 11 CD 12 89 3E 55
coefficient TagType INTEGER = 02
Length in next 1 bytes = 81
Length = 81
Value = 00 BE CE 82 86 E7 AB DC 7F 45 B8 CB D8 A9 57 B1 FA DA E6 70 37 9F F7 E4 77 1A 5D 88 37 EF 3A C1 12 2C 72 C2 09 5B 3B 10 A2 D1 FD 22 32 06 6A 42 5E A4 9B 5B D7 7D E8 19 B9 D0 22 1F DE D2 50 18 A8 55 44 68 5E 99 9F 55 BE 65 41 A2 F4 88 58 1E 24 66 30 3C 79 1D 82 0F 18 18 69 24 F4 9C B9 EB 53 AA C1 FE C3 78 4A F7 A4 47 9F C9 20 CE 01 4A C4 31 E9 F1 02 06 62 74 E6 51 CF 7D 9A 45 FF DB 94
BER Basic Encoding Rules
DER Distinguished Encoding Rules
subset of BER
uses TLVformat
CER Canonical Encoding Rules
OER Octet Encoding Rules
PER Packed Encoding Rules
XER XML Encoding Rules
E-XER Extended XER
JER JSON Encoding Rules
TLVformat = Tag || Length || Value
|| means concatenation
Tag = Class || Form || Value
size = 8 bit
Class = 0 UNIVERSAL
= 1 APPLICATION
= 2 contex-defined
= 3 PRIVATE
size = 2 bit
Form = 0 primitive
= 1 constructed
size = 1 bit
Value size = 5 bit
---------------------------------------------------------------------------
| TagType Class Form Class || Form || Value |
| BIT STRING UNIVERSAL Primitive 00000011 = 03 |
| BOOLEAN UNIVERSAL Primitive 00000001 = 01 |
| INTEGER UNIVERSAL Primitive 00000010 = 02 |
| NULL UNIVERSAL Primitive 00000101 = 05 |
| OBJECT IDENTIFIER UNIVERSAL Primitive 00000110 = 06 |
| OCTET STRING UNIVERSAL Primitive 00000100 = 04 |
| BMPSTRING UNIVERSAL Primitive 00011110 = 1E |
| IA5STRING UNIVERSAL Primitive 00010110 = 16 |
| PrintableString UNIVERSAL Primitive 00010011 = 13 |
| TELETEXSTRING UNIVERSAL Primitive 00010100 = 14 |
| UTF8String UNIVERSAL Primitive 00001100 = 0C |
| UTCTime UNIVERSAL Primitive 00010111 = 17 |
| GeneralizedTime UNIVERSAL Primitive 00011000 = 18 |
| SEQUENCE UNIVERSAL Constructed 00110000 = 30 |
| SEQUENCE OF UNIVERSAL Constructed 00110000 = 30 |
| SET UNIVERSAL Constructed 00110001 = 31 |
| SET OF UNIVERSAL Constructed 00110001 = 31 |
---------------------------------------------------------------------------
Length if size of TLVformat.Value < 128 bytes
then ==========================
|size of TLVformat.Value |
--------------------------
| 0 x x x x x x x |
==========================
7 least significant bits define size in bytes of TLVformat.Value
if size of TLVformat.Value >= 128 bytes
then =======================================
| number of bytes next | size of TLVformat.Value
----------------------------------------------
| 1 x x x x x x x | y y y y y ....
=====================================
7 least significant bits define size in bytes of size in bytes of TLVformat.Value
Value depends on assigned ASN.1
may be = Value
may be = Tag || Length || Value
SHA256 = SHA256(msg)
msg message to hash
bit size = l
>= 0
< 2^64
l bit size = 64
M = msg || pad
pad bit value = 1 || 0...0 || l
bit size = 1 + o + 64
!= 0
bit size = multiple of 512
(l + 1 + o + 64) mod 512 = 0
H starting value = 6a09e667 bb67ae85 3c6ef372 a54ff53a 510e527f 9b05688c 1f83d9ab 5be0cd19
in hexadecimal
represents the first thirty-two bits of
the fractional parts of
the square roots of
the first eight prime numbers
changes value while M is hashed
bit size = 8*32
K represents the first 32bits of
the fractional parts of
the cube roots of
the first 64 prime numbers
= 428a2f98 71374491 b5c0fbcf e9b5dba5 3956c25b 59f111f1 923f82a4 ab1c5ed5
d807aa98 12835b01 243185be 550c7dc3 72be5d74 80deb1fe 9bdc06a7 c19bf174
e49b69c1 efbe4786 0fc19dc6 240ca1cc 2de92c6f 4a7484aa 5cb0a9dc 76f988da
983e5152 a831c66d b00327c8 bf597fc7 c6e00bf3 d5a79147 06ca6351 14292967
27b70a85 2e1b2138 4d2c6dfc 53380d13 650a7354 766a0abb 81c2c92e 92722c85
a2bfe8a1 a81a664b c24b8b70 c76c51a3 d192e819 d6990624 f40e3585 106aa070
19a4c116 1e376c08 2748774c 34b0bcb5 391c0cb3 4ed8aa4a 5b9cca4f 682e6ff3
748f82ee 78a5636f 84c87814 8cc70208 90befffa a4506ceb bef9a3f7 c67178f2
in hexadecimal
bit size = 64*32
W bit size = 64*32
each 512bit of M is refered as M2[j]
each 512bit of W is refered as W2[j]
each 32bit of M is refered as M[i]
each 32bit of W is refered as W[i]
each 32bit of H is refered as H[i]
each 32bit of K is refered as K[i]
M is divided in N 512bit blocks
for j = 0 to N-1 do
W2[0] = M2[j]
for i = 16 to 63 do
W[i] = o1SH(W[i- 2]) + W[i- 7] +
o0SH(W[i-15]) + W[i-16]
initialize variables a,b,c,d,e,f,g,h each 32bit in size
a = H[0]
b = H[1]
c = H[2]
d = H[3]
e = H[4]
f = H[5]
g = H[6]
h = H[7]
for i = 0 to 63 do
initialize variables t1,t2 each 32bit in size
t1 = h + s1SH(e) + chSH(e,f,g) + K[i] + W[i]
t2 = s0SH(a) + maSH(a,b,c)
h = g
g = f
f = e
e = d + t1
d = c
c = b
b = a
a = t1 + t2
H[0] = H[0] + a
H[1] = H[1] + b
H[2] = H[2] + c
H[3] = H[3] + d
H[4] = H[4] + e
H[5] = H[5] + f
H[6] = H[6] + g
H[7] = H[7] + h
output H
internal functions chSH(x,y,z) = (x&y) ^ ((~x)&z)
maSH(x,y,z) = (x&y) ^ ( x &z) ^ (y&z)
s0SH(x) = rotR(x, 2) ^ rotR(x,13) ^ rotR(x,22)
s1SH(x) = rotR(x, 6) ^ rotR(x,11) ^ rotR(x,25)
o0SH(x) = rotR(x, 7) ^ rotR(x,18) ^ (x >> 3)
o1SH(x) = rotR(x,17) ^ rotR(x,19) ^ (x >> 10)
rotR(x,m) = (x >> m) | (x << 32-m)
& = bitwise AND operation
| = bitwise OR operation
^ = bitwise complement operation
<< = left shift operation
>> = right shift operation
operate on 32bit variables
addition operator is performed modulo 2^32
ACME
RFC8555
Automatic Certificate Management Environment
protocol for Web PKI Certificates
automates process of Certificate verification
issuance
revocation
used by Applicant will be the ACME client will be the Client
CA will be the ACME server will be the Server
communications between Client and Server use HTTPS
JWS
via HTTP are UTF-8 encoded
steps Client establishes new account with Server
proves control of identifier
issues Certificate
fetches updated certificate some time after issuance
+----------------------+---------------------------------------+---------------+
| Action | Request | Response |
+----------------------+---------------------------------------+---------------+
| get directory | GET directory | 200 |
| get nonce | HEAD newNonce | 200 |
| create account | POST newAccount | 201 -> account|
| submit order | POST newOrder | 201 -> order |
| fetch challenges | POST-as-GET order's authorization urls| 200 |
| respond to challenges| POST authorization challenge urls | 200 |
| poll for status | POST-as-GET order | 200 |
| finalize order | POST order's finalize url | 200 |
| poll for status | POST-as-GET order | 200 |
| download certificate | POST-as-GET order's certificate url | 200 |
+----------------------+---------------------------------------+---------------+
"->" = mnemonic for location header field pointing to created resource
CertificateManagement AccountCreation
OrderingCertificate
IdentifierAuthorization
CertificateIssuance
CertificateRevocation
Resources types AccountReso information about an account
OrderReso account's requests to issue certificates
AuthorizationReso account's authorization to act for an identifier
ChallengeReso challenge to prove control of an identifier
CertificateReso issued certificates
"directory" must be provided by Server
"newNonce" must be provided by Server
"newAccount"
"newOrder"
"revokeCert"
"keyChange"
created by Server
are accesed using URLs
URLs are connected by linkRelations
linkRelations RFC8288
"up" used with ChallengeReso
indicates AuthorizationReso to which a challenge belongs
used with media types from CertificateReso
"index" present on all resources other than Directory
indicates the URL of Directory
requests for Resources different to "newAccount" and "revokeCert" have "kid" in "protected"
if Server receives GET request for resources different to "newNonce" or "directory"
then Server return status code 405 (Method Not Allowed)
error type "malformed"
if Client request resource from Server
then Client sends POST request with JWS "payload" = zero-length octet string
Server authenticates Client and verifies any access control rules
if authentication fails
then Server returns status code 405 (Method Not Allowed)
error type "malformed"
Server rate limits resource creation returning error type "urn:ietf:params:acme:error:rateLimited"
may return HTTP Retry-After indicating when current request may succeed again
"detail" human-readable cause of error
Link header field pointing to documentation about the specific rate limit that was hit
with "help" type
RFC8288
ResourcesRelationships map directory
|
+--> newNonce
|
+----------+----------+-----+-----+------------+
| | | | |
| | | | |
V V V V V
newAccount newAuthz newOrder revokeCert keyChange
| | |
| | |
V | V
account | order --+--> finalize
| | |
| | +--> cert
| V
+---> authorization
| ^
| | "up"
V |
challenge
Directory provided by Server
Server allows GET and POST requests for Directory resources
fields "newNonce" has URL that returns new nonce
"newAccount" has URL that returns new account
"newOrder" has URL that returns new order
"newAuthz" has URL that returns new authorization
ommited if not supported
"revokeCert" has URL that returns Revoke certificate
"keyChange" has URL that returns Key change
"meta" JSON object
items "termsOfService" optional
string URL
URL returns the terms of service
if Server rejects newAccount with "termsOfServiceAgreed" = false or
without "termsOfServiceAgreed"
then "termsOfService" is included
"website" optional
string URL HTTP or HTTPS
URL returns more information about ACME Server
"caaIdentities" optional
array of string
each string = ASCII sequence as "Issuer Domain Name" in CAA issue or
issuewild property tag
hostnames that Server recognizes as referring to itself
for CAA record validation RFC6844
configuration
"externalAccountRequired" optional
boolean
if = true then all newAccount requests need "externalAccountBinding"
else Server may return error type "externalAccountRequired"
Server of both ACME and Web may have Web path "example.com/frontpage"
ACME path "example.com/acme" for Directory
of only ACME may have ACME path "example.com/" for Directory
example HTTP/1.1 200 OK
Content-Type: application/json
{
"newNonce": "https://example.com/acme/new-nonce",
"newAccount": "https://example.com/acme/new-account",
"newOrder": "https://example.com/acme/new-order",
"newAuthz": "https://example.com/acme/new-authz",
"revokeCert": "https://example.com/acme/revoke-cert",
"keyChange": "https://example.com/acme/key-change",
"meta":
{
"termsOfService": "https://example.com/acme/terms/2017-5-30",
"website": "https://www.example.com/",
"caaIdentities": ["example.com"],
"externalAccountRequired": false
}
}
Nonce if Client needs new nonce
then case1 Client sends HEAD request to URL of "newNonce"
Server returns Replay-Nonce header with new nonce
may have status code 200 (OK)
Cache-Control header with "no-store"
case2 Client sends GET request to URL of "newNonce"
Server returns Replay-Nonce header with new nonce
status code 204 (No Content)
Cache-Control header with "no-store"
example HEAD /acme/new-nonce HTTP/1.1
Host: example.com
HTTP/1.1 200 OK
Replay-Nonce: oFvnlFP1wIhRlYS2jTaXbA
Cache-Control: no-store
Link: <https://example.com/acme/directory>;rel="index"
Server allows GET and POST requests to URL of "newNonce"
provides nonce in HTTP Replay-Nonce header
with unique value
may provide nonce in error responses
verifies received "nonce" was created by Server
Client "protected" always has "nonce" from HTTP Replay-Nonce sent by Server
ignores invalid HTTP Replay-Nonce sent by Server
if "nonce" has appeared in previous request or
not present in "protected"
then Server returns status code 400(Bad Request)
error type "urn:ietf:params:acme:error:badNonce"
Replay-Nonce with new valid nonce
Client may retry previous query using only that new nonce
try different query using that new nonce
Replay-Nonce header
uses base64url encoding
using URL- and filename-safe character set defined in Section 5 of RFC4648
all trailing '=' characters omitted (as permitted by Section 3.2)
has no line breaks
whitespace
other additional characters
encoding of empty octet sequence is the empty string
should not be in HTTP request messages
ABNF base64url = ALPHA / DIGIT / "-" / "_"
Replay-Nonce = 1*base64url
AccountReso "status" of this account
= "valid"
"deactivated" client-initiated deactivation
"revoked" server-initiated deactivation
"orders" string URL
cannot be updated by Client
URL returns OrdersList submitted by this account
accesed with POST-as-GET request
"contact" optional
array of string URL
used by Server to contact Client for issues related to this account
example Server notifes Client about server-initiated revocation
certificate expiration
"termsOfServiceAgreed" optional
boolean
cannot be updated by Client
"externalAccountBinding" optional
object
cannot be updated by Client
used to associate AccountReso with non-ACME account such as CA customer database
if inside newAccount request
then account holder approves to bind non-ACME account to
ACME account
example {
"status": "valid",
"contact": [ "mailto:cert-admin@example.org",
"mailto:admin@example.org" ],
"termsOfServiceAgreed": true,
"orders": "https://example.com/acme/orders/rzGoeA"
}
states valid
|
|
+-----------+-----------+
Client | Server |
deactiv | revoke |
V V
deactivated revoked
OrdersList JSON object with "orders"
has array of string URL
each URL returns OrderReso made by account
may not include invalid OrdersReso
may include pending OrdersReso
may be incomplete
may be with Link header field with "next" link relation indicating where further entries can be acquired
example HTTP/1.1 200 OK
Content-Type: application/json
Link: <https://example.com/acme/directory>;rel="index"
Link: <https://example.com/acme/orders/rzGoeA?cursor=2>;rel="next"
{
"orders": [ "https://example.com/acme/order/TOlocE8rfgo",
"https://example.com/acme/order/4E16bbL5iSw",
"https://example.com/acme/order/neBHYLfw0mg" ]
}
OrderReso represents request for certificate
tracks progress of order
"status" = "pending"
"ready"
"processing"
"valid"
"invalid"
"expires" string
timestamp for when this order becomes invalid
format RFC3339
optional except if "status" = "pending"
"valid"
"identifiers" array of object
immutable once set
identifier that the order pertains to
"type" = "dns" see registry defined in Section 9.7.7 for any others
"value" identifier itself
"notBefore" optional
string
requested value of notBefore field in certificate
format RFC3339
"notAfter" optional
string
requested value of notAfter field in certificate
format RFC3339
"error" optional
object
error that occurred while processing order
structured as problem document RFC7807
"authorizations" array of string URL
immutable once set
if "pending" OrdersReso
then this has authorizations to complete before requested certificate can be issued
unexpired authorizations that were completed for "identifiers"
if "valid" or "invalid" OrdersReso
then this has Authorization completed
there may not be a 1:1 relationship between "identifiers" and
"authorizations"
each URL returns AuthorizationReso
requested using POST-as-GET
authorizations required are dictated by Server policy
if CA allows multiple OrderReso to be fulfilled based on one authorization
then it may reflect that authorization in all OrderReso
some may already be valid because Client completed authorization in another OrderReso
Client previously pre-authorized the identifier
Server authorized Client using external account
Client may read "status"of OrderReso to determine if more "authorizations" need completion
"finalize" string URL
Client POST a CSR to this URL after all "authorizations" are satisfied
"certificate" optional
string URL
populated by Server after all "authorizations" are satisfied
example {
"status": "valid",
"identifiers": [ { "type": "dns", "value": "www.example.org" },
{ "type": "dns", "value": "example.org" } ],
"expires": "2016-01-20T14:09:07.99Z",
"notBefore": "2016-01-01T00:00:00Z",
"notAfter": "2016-01-08T00:00:00Z",
"authorizations": [ "https://example.com/acme/authz/PAniVnsZcis",
"https://example.com/acme/authz/r4HqLzrSrpI" ],
"finalize": "https://example.com/acme/order/TOlocE8rfgo/finalize",
"certificate": "https://example.com/acme/cert/mAt3xBGaobw"
}
states pending --------------+
| |
| All authz |
| "valid" |
V |
ready ---------------+
| |
| Receive |
| finalize |
| request |
V |
processing ------------+
| |
| Certificate | Error or OrderReso expired or
| issued | AuthorizationReso "expired" or "revoked" or "deactivated"
V V
valid invalid
Server that issues orders synchronously will never show "processing"
AuthorizationReso represents Server's authorization for account to represent this "identifier"
"identifier" object
that AccountReso is authorized to represent
"type" = "dns"
"value" identifier itself
as it would appear in Certificate
format RFC5280 Section 7
if begins with "xn--"
then Server verifies format RFC5890
"status" of this AuthorizationReso
= "pending"
"valid"
"invalid"
"deactivated"
"expired"
"revoked"
Server that immediately deletes "expired" AuthorizationReso will never show "expired"
"expires" optional except if this AuthorizationReso "status" = "valid"
string
timestamp when this AuthorizationReso becomes "invalid"
format RFC3339
"challenges" array of objects
Client fulfills to prove possession of this "identifier"
Server may consider one challenge sufficient
has ChallengeReso that was validated if ChallengeReso "valid"
failed if ChallengeReso "invalid"
"wildcard" = true if created for newOrder with wildcardDomainName
else "wildcard" is absent
example {
"status": "valid",
"expires": "2015-03-01T14:09:07.99Z",
"identifier": { "type": "dns",
"value": "www.example.org" },
"challenges": [
{
"url": "https://example.com/acme/chall/prV_B7yEyA4",
"type": "http-01",
"status": "valid",
"token": "DGyRejmCefe7v4NfDGDKfA",
"validated": "2014-12-01T12:05:58.16Z"
}
],
"wildcard": false
}
states pending --------------------+
| |
Challenge failure | |
or | |
Error | Challenge valid |
+---------+---------+ |
| | |
V V |
invalid valid |
| |
| |
| |
+--------------+--------------+
| | |
| | |
Server | Client | Time after |
revoke | deactivate | "expires" |
V V V
revoked deactivated expired
ChallengeReso represents Server's offer to validate Client's possession of identifier in a specific way
contents depend on validation method
has no standard structure
"type" string
of challenge
set by Server policy
"url" string
to which response can be posted
"status" string
of challenge
= "pending"
"processing"
"valid"
"invalid"
if = "invalid" then Server may include "error" to help diagnose failure
"validated" optional
string
set if "status" = "valid"
time at which Server validated Challenge
format RFC3339
"error" optional
object
that occurred while Server was validating Challenge, if any
structured as problem document RFC7807
multiple errors can be indicated using "subproblems"
if incuded then "status" = "invalid"
states pending
|
| Client responds to challenge
| Server receives response
V
processing <-+
| | | Server retries validation or
| | | client retries request
| +----+
|
|
Successful | Failed
validation | validation
+---------+---------+
| |
V V
valid invalid
AccountCreation
Client wants to create new AccountReso
generates AKP
POST to URL of "newAccount"
has JWS
"protected" has "jwk" and other fields shown in JWS
"payload" "contact" optional
array of string
same meaning as "contact" of AccountReso
has no "mailto" if "hfields" RFC6068 is present or
"to" has more than one "addr-spec"
"mailto" is supported by Server if Server validates URLs
if URL has unsupported scheme
then Server returns error "unsupportedContact"
error description
types of URLs Server accepts
if URL has supported scheme and invalid value
then Server returns error "invalidContact"
"termsOfServiceAgreed" optional
boolean
same meaning as "termsOfServiceAgreed" of AccountReso
Clients may not automatically agree
require some user interaction for agreement
if = false or not included and
Server requires agreement
then Server rejects newAccount requests
"onlyReturnExisting" optional
boolean
allows Client to find AccountReso URL based on account key
if = true and AccountReso already exist
then Server creates no new AccountReso
if = true and AccountReso does not exist
then Server returns status code 400 (Bad Request)
error type "urn:ietf:params:acme:error:accountDoesNotExist"
"externalAccountBinding" optional
object
same meaning as "externalAccountBinding" of AccountReso
if not verified by Server
then Server does not reflect "externalAccountBinding" data in new AccountReso
"signature" uses privateKey of AKP
example POST /acme/new-account HTTP/1.1
Host: example.com
Content-Type: application/jose+json
{
"protected": base64url({
"alg": "ES256",
"jwk": {...},
"nonce": "6S8IqOGY7eL2lsGoTZYifg",
"url": "https://example.com/acme/new-account"
}),
"payload": base64url({
"termsOfServiceAgreed": true,
"contact": [
"mailto:cert-admin@example.org",
"mailto:admin@example.org"
]
}),
"signature": "RZPOnYoPs1PhjszF...-nh6X1qtOFPB519I"
}
Server ignores unrecognized fields of received POST
example if POST has "orders"
then Server ignores it
checks if "jwk" already exist
then returns status code 200 (OK)
Location header with URL of AccountReso with that "jwk"
else creates AccountReso
stores "jwk" to authenticate future requests
returns status code 201 (Created)
Location header with URL of AccountReso
AccountReso
example HTTP/1.1 201 Created
Content-Type: application/json
Replay-Nonce: D8s4D2mLs8Vn-goWuPQeKA
Link: <https://example.com/acme/directory>;rel="index"
Location: https://example.com/acme/acct/evOfKhNU60wg
{
"status": "valid",
"contact": [ "mailto:cert-admin@example.org",
"mailto:admin@example.org"],
"orders": "https://example.com/acme/acct/evOfKhNU60wg/orders"
}
then Client puts URL of AccountReso in "kid" of subsequent requests
AccountUpdate
Client wants to update AccountReso
POST to URL of AccountReso
example POST /acme/acct/evOfKhNU60wg HTTP/1.1
Host: example.com
Content-Type: application/jose+json
{
"protected": base64url({
"alg": "ES256",
"kid": "https://example.com/acme/acct/evOfKhNU60wg",
"nonce": "ax5RnthDqp_Yf4_HZnFLmA",
"url": "https://example.com/acme/acct/evOfKhNU60wg"
}),
"payload": base64url({
"contact": [
"mailto:certificates@example.org",
"mailto:admin@example.org"
]
}),
"signature": "hDXzvcj8T6fbFbmn...rDzXzzvzpRy64N0o"
}
if Server accepts account update
then returns status code 200 (OK)
AccountReso
ExternalAccountBinding
Server binds new AccountReso to nonACMEaccount of nonACMEsystem
steps nonACMEsystem sends to Client
MACkey may be base64url encoded
keyIdentifier as ASCII string
using some mechanism outside of ACME
Client does newAccount request with "externalAccountBinding"
example POST /acme/new-account HTTP/1.1
Host: example.com
Content-Type: application/jose+json
{
"protected": base64url({ "alg": "ES256",
"jwk": /* account key */,
"nonce": "K60BWPrMQG9SDxBDS_xtSw",
"url": "https://example.com/acme/new-account" }),
"payload": base64url({
"contact": [ "mailto:cert-admin@example.org",
"mailto:admin@example.org" ],
"termsOfServiceAgreed": true,
"externalAccountBinding": {
"protected": base64url({ "alg": "HS256",
"kid": /* keyIdentifier from nonACMEsystem */,
"url": "https://example.com/acme/new-account" }),
"payload": base64url(/* same as in "jwk" above */),
"signature": /* MAC using MACkey from nonACMEsystem */ }
}),
"signature": "5TWiqIYQfIDfALQv...x9C2mg8JGPxl5bI4"
}
Server verifies "externalAccountBinding" "protected" "alg" has supported MAC-based algorithm
"kid" has keyIdentifier provided by nonACMEsystem
"nonce" is not present
"url" = outer JWS
retrieves MACkey corresponding to "kid" of "externalAccountBinding"
verifies "signature" of "externalAccountBinding" using retrieved MACkey
"payload" of "externalAccountBinding" represents the same key as was used to verify outer "jwk"
AccountKeyRollover
Client wants to change PublicKey of AccountReso
to recover from key compromise
mitigate impact of unnoticed key compromise
request has signatures by oldKey
newKey
steps Client creates Inner represents request by holder of newKey to take over the holder of oldKey
"protected" "jwk" = PublicKey of newKey pair
"url" = "url" of Outer
"nonce" omitted
"payload" "account" string URL
= URL of AccountReso being modified
"oldKey" JWK
signed by newKey
Outer represents current account holder's assent to this request
"payload" has Inner
signed by oldKey
example POST /acme/key-change HTTP/1.1
Host: example.com
Content-Type: application/jose+json
{
"protected": base64url({ "alg": "ES256",
"kid": "https://example.com/acme/acct/evOfKhNU60wg",
"nonce": "S9XaOcxP5McpnTcWPIhYuB",
"url": "https://example.com/acme/key-change" }),
"payload": base64url({
"protected": base64url({ "alg": "ES256",
"jwk": /* new key */,
"url": "https://example.com/acme/key-change" }),
"payload": base64url({ "account": "https://example.com/acme/acct/evOfKhNU60wg",
"oldKey": /* old key */ }),
"signature": "Xe8B94RD30Azj2ea...8BmZIRtcSKPSd8gU"
}),
"signature": "5TWiqIYQfIDfALQv...x9C2mg8JGPxl5bI4"
}
Server validates JWS
validates POST request belongs to AccountReso
checks Inner is well-formed JWS
has "jwk" not "kid"
verifies using "jwk"
has well-formed "payload"
"url" = Outer "url"
"account" = "kid" in Outer
"oldKey" is the same account key for account in question
no Account exists with "jwk" of Inner
if AccountReso already exist with newKey
then Server may return status code 409 (Conflict)
Location header with URL of that AccountReso
if all is right
then Server updates AccountReso oldKey with newKey
returns status code 200 (OK)
else Server returns error status code
ProblemDocument describing error
updating account key may not change other data of AccountReso
must not invalidate OrdersReso
AuthorizationReso
AccountDeactivation
Client may want when account key is compromised or decommissioned
example POST /acme/acct/evOfKhNU60wg HTTP/1.1
Host: example.com
Content-Type: application/jose+json
{
"protected": base64url({ "alg": "ES256",
"kid": "https://example.com/acme/acct/evOfKhNU60wg",
"nonce": "ntuJWWSic4WVNSqeUmshgg",
"url": "https://example.com/acme/acct/evOfKhNU60wg" }),
"payload": base64url({ "status": "deactivated" }),
"signature": "earzVLd3m5M4xJzR...bVTqn7R08AKOVf3Y"
}
Server verifies request signed by account key
if Server accepts request
then Server updates AccountReso to "deactivated"
returns status code 200 (OK)
AccountReso
cancels AccountReso pending operations such as OrdersReso
may delete data related to AccountReso
send mail to "contact" of AccountReso
not revoke issued Certificates of AccountReso
if receives POST or POST-as-GET from "deactivated" AccountReso
then return status code 401 (Unauthorized)
error type "urn:ietf:params:acme:error:unauthorized"
"deactivated" AccountReso can not request Certificate
access resources
OrdersReso or AuthorizationReso
ACME does not provide a way to reactivate "deactivated" AccountReso
CertificateIssuance
steps Client submits order for Certificate to be issued
Server responds with list of requirements for Client to satisfy before the Certificate is issued
Client responds by telling Server which ChallengeReso are completed
Server validates the completed ChallengeReso
Client submits CSR
Server issues requested Certificate
makes Certificate available to Client
map Client Server
[Order]
Signature ------->
<------- Required Authorizations
[Responses]
Signature ------->
<~~~~~~~~Validation~~~~~~~~>
[CSR]
Signature ------->
<------- Acknowledgement
<~~~~~~Await issuance~~~~~~>
[POST-as-GET request]
Signature ------->
<------- Certificate
[] Information covered by request signatures
ApplyingforCertificateIssuance
Client POST to URL of "newOrder"
has subset of OrderReso describing Certificate to be issued
"identifiers" array of object
that Client wishes to submit order for
"type" string
of identifier
"value" string
identifier itself
may = wildcardDomainName if doing newOrder and
"type" = "dns"
if = wildcardDomainName and
Server accepts newOrder
then Server creates AuthorizationReso with "identifier" value chunk "*." removed
"wildcard" = true
wildcardDomainName = "*." followed by a domain name
example "*.example.com"
described in Subject Alternate Name Extension RFC5280
"notBefore" optional
string
format RFC3339
the notBefore field in Certificate
date on which Certificate becomes valid
"notAfter" optional
string
format RFC3339
the notAfter field in Certificate
date on which Certificate becomes invalid
example POST /acme/new-order HTTP/1.1
Host: example.com
Content-Type: application/jose+json
{
"protected": base64url({ "alg": "ES256",
"kid": "https://example.com/acme/acct/evOfKhNU60wg",
"nonce": "5XJ1L3lEkMG7tR6pA00clA",
"url": "https://example.com/acme/new-order" }),
"payload": base64url({ "identifiers": [
{ "type": "dns", "value": "www.example.org" },
{ "type": "dns", "value": "example.org" } ],
"notBefore": "2016-01-01T00:04:00+04:00",
"notAfter": "2016-01-08T00:04:00+04:00" }),
"signature": "H6ZXtGjTZyUnPeKn...wEA4TklBdh3e454g"
}
Server returns error if cannot fulfill request
does not issue Certificate with contents other than those requested
if Server wants request to be modified
then Server returns appropiate error type and description
Server returns status code 201 (Created)
OrderReso reflecting Client's request
with "authorizations" Client must complete before Certificate is issued
example HTTP/1.1 201 Created
Replay-Nonce: MYAuvOpaoIiywTezizk5vw
Link: <https://example.com/acme/directory>;rel="index"
Location: https://example.com/acme/order/TOlocE8rfgo
{
"status": "pending",
"expires": "2016-01-05T14:09:07.99Z",
"notBefore": "2016-01-01T00:00:00Z",
"notAfter": "2016-01-08T00:00:00Z",
"identifiers": [ { "type": "dns", "value": "www.example.org" },
{ "type": "dns", "value": "example.org" } ],
"authorizations": [ "https://example.com/acme/authz/PAniVnsZcis",
"https://example.com/acme/authz/r4HqLzrSrpI" ],
"finalize": "https://example.com/acme/order/TOlocE8rfgo/finalize"
}
for each OrderReso if Client fulfills Server's requirements before "expires"
then Server will "finalize" upon request of Client
issue Certificate
any "pending" AuthorizationReso is a transaction that Client must complete before Server issues Certificate
if Client fails before "expires"
then Server may make OrderReso "invalid"
delete OrderReso
assume nothing about the sort order of "identifiers" or "authorizations" of OrderReso
if Client believes it has fulfilled "authorizations"
then Client POST to URL of "finalize" of OrderReso
"csr" string
CSR RFC2986
base64url encoded version of the DER format
does not include headers
is different from PEM
example POST /acme/order/TOlocE8rfgo/finalize HTTP/1.1
Host: example.com
Content-Type: application/jose+json
{
"protected": base64url({ "alg": "ES256",
"kid": "https://example.com/acme/acct/evOfKhNU60wg",
"nonce": "MSF2j2nawWHPxxkE3ZJtKQ",
"url": "https://example.com/acme/order/TOlocE8rfgo/finalize" }),
"payload": base64url({ "csr": "MIIBPTCBxAIBADBFMQ...FS6aKdZeGsysoCo4H9P", }),
"signature": "uOrUfIIk5RyQ...nw62Ay1cl6AB"
}
CSR identifiers = newOrder identifiers
"dns" Identifiers appear in commonName of requested subject name or
extensionRequest RFC2985 that request subjectAltName extension
any sort order
Specifications defining new identifier types define their locations in signing request of Certificate
if "finalize" request received and
if CSR identifiers != OrderReso identifiers or
account is not authorized for identifiers in CSR or
CA rejects CSR extensions
then Server returns error
may return ProblemDocument with "badCSR"
"detail" with reasons the CSR was rejected
leaves OrderReso "ready" to allow new "finalize" request
amended CSR
if "finalize" request received and
OrderReso is not "ready"
then Server returns status code 403 (Forbidden)
ProblemDocument type "orderNotReady"
Client may POST to URL of OrderReso to read its "status"
if "finalize" request received and successful
then Server returns status code 200 (OK)
updated OrderReso
if OrderReso is "invalid" then Certificate will not be issued
OrderReso is abandoned
"pending" then Server thinks Client has not fulfilled the requirements
Client checks "authorizations" for entries still pending
"ready" then Server thinks requirements are fulfilled
awaits "finalize" request
"processing" then Certificate is being issued
Client POST-as-GET to URL of OrderReso after Retry-After of response
"valid" then issued Certificate is located at "certificate" of response
example HTTP/1.1 200 OK
Replay-Nonce: CGf81JWBsq8QyIgPCi9Q9X
Link: <https://example.com/acme/directory>;rel="index"
Location: https://example.com/acme/order/TOlocE8rfgo
{
"status": "valid",
"expires": "2016-01-20T14:09:07.99Z",
"notBefore": "2016-01-01T00:00:00Z",
"notAfter": "2016-01-08T00:00:00Z",
"identifiers": [ { "type": "dns", "value": "www.example.org" },
{ "type": "dns", "value": "example.org" } ],
"authorizations": [ "https://example.com/acme/authz/PAniVnsZcis",
"https://example.com/acme/authz/r4HqLzrSrpI" ],
"finalize": "https://example.com/acme/order/TOlocE8rfgo/finalize",
"certificate": "https://example.com/acme/cert/mAt3xBGaobw"
}
PreAuthorization
some Servers may enable Clients to get authorization for identifier proactively outside ACME
example Client hosting virtual servers for collection of names
wants authorization before creating virtual servers
only creates Certificate when virtual server starts up
if CA has Server and
CA has nonACMEsystem authorizing Clients to issue certificates for an identifier
then CA may provision Server with valid authorizations for OrdersReso of those Clients
"newAuthz" is not used to issue Certificates with wildcardDomainName
steps Server shows URL of "newAuthz" in Directory
Client POST to URL of "newAuthz"
"identifier" object
to appear in resulting AuthorizationReso
"type" string
of identifier
"value" string
identifier itself
example POST /acme/new-authz HTTP/1.1
Host: example.com
Content-Type: application/jose+json
{
"protected": base64url({ "alg": "ES256",
"kid": "https://example.com/acme/acct/evOfKhNU60wg",
"nonce": "uQpSjlRb4vQVCjVYAyyUWg",
"url": "https://example.com/acme/new-authz" }),
"payload": base64url({ "identifier": { "type": "dns",
"value": "example.org" }}),
"signature": "nuSDISbWG8mMgE7H...QyVUL68yzf3Zawps"
}
Server chosses if Certificates for identifier is issued
checks identifier is supported
names in blacklist of high-value identifiers
if Server will not issue for identifier
then Server may return status code 403 (Forbidden)
ProblemDocument with reason for rejection
if Server will issue for identifier
then Server builds AuthorizationReso "identifier" submitted by Client
"status" = "pending" unless Server has out-of-band information about Client's authorization status
"challenges" selected by Server's policy
returns status code 201 (Created)
Location header with URL of new AuthorizationReso
AuthorizationReso
Client follows authorization process using returned AuthorizationReso
DownloadingCertificate
steps Client POST to ULR of "certificate" of OrderReso
example POST /acme/cert/mAt3xBGaobw HTTP/1.1
Host: example.com
Content-Type: application/jose+json
Accept: application/pem-certificate-chain
{
"protected": base64url({
"alg": "ES256",
"kid": "https://example.com/acme/acct/evOfKhNU60wg",
"nonce": "uQpSjlRb4vQVCjVYAyyUWg",
"url": "https://example.com/acme/cert/mAt3xBGaobw"
}),
"payload": "",
"signature": "nuSDISbWG8mMgE7H...QyVUL68yzf3Zawps"
}
may request other Certificate formats by including Accept header RFC7231
end-entity in DER by including "application/pkix-cert" RFC2585
"application/pkcs7-mime" RFC5751
Server receives request of Client
returns Certificate which is single
immutable
default format is application/pem-certificate-chain
example HTTP/1.1 200 OK
Content-Type: application/pem-certificate-chain
Link: <https://example.com/acme/directory>;rel="index"
-----BEGIN CERTIFICATE-----
[End-entity certificate contents]
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
[Issuer certificate contents]
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
[Other certificate contents]
-----END CERTIFICATE-----
may support other Certificate formats
add multiple "Link: rel="up"" header pointing to issuers
for formats that only express one Certificate
so Clients can build TLS1.3 Certificate chain
links relation header with "alternate" RFC8288
to alternative Certificate chain starting with end-entity Certificate
to express paths to various trust anchors
then Client decides which "alternate" is optimal
headers Cache-Control
Expires not related to validity period of Certificate
if Client wants renewed Certificate
then Client does new newOrder process
application/pem-certificate-chain
contains one or more Certificates
Certificates use PEM textual encoding RFC7468
strict encoding
have no explanatory text
ABNF "stricttextualmsg" and "eol" are as defined in Section 3
certchain = stricttextualmsg *(eol stricttextualmsg)
first Certificate is end-entity
nonFirst Certificates may directly certify the one preceding it
Certificate validation requires trust anchors be distributed independently
if peer already possess trust anchor Certificate
then chain may not have trust anchor Certificate
"Media Types" registry Type name: application
Subtype name: pem-certificate-chain
Required parameters: None
Optional parameters: None
Encoding considerations: 7bit
Security considerations: Carries a cryptographic certificate and its associated certificate chain. This media type carries no active content.
Interoperability considerations: None
Published specification: RFC 8555
Applications that use this media type: ACME clients and servers, HTTP servers, other applications that need to be configured with a certificate chain
Additional information:
Deprecated alias names for this type: n/a
Magic number(s): n/a
File extension(s): .pem
Macintosh file type code(s): n/a
Person & email address to contact for further information: See Authors' Addresses section.
Intended usage: COMMON
Restrictions on usage: n/a
Author: See Authors' Addresses section.
Change controller: IETF <iesg@ietf.org>
CertificateRevocation
Client POST to URL of "revokeCert"
"certificate" string
Certificate to be revoked
base64url encoded of DER format
different from PEM
"reason" optional
int
reasonCodes Section 5.3.1 RFC5280
used when generating OCSP responses and CRLs
if not set
then Server may omit reasonCode CRL entry extension
when generating OCSP responses and CRLs
disallow subset of reasonCodes from being used by user
if has disallowed reasonCode
then Server rejects request
returns error type "urn:ietf:params:acme:error:badRevocationReason"
ProblemDocument detail may indicate allowed reasonCodes
"jwk" in "protected"
signed with either AKP
key pair in Certificate
example using AKP POST /acme/revoke-cert HTTP/1.1
Host: example.com
Content-Type: application/jose+json
{
"protected": base64url({
"alg": "ES256",
"kid": "https://example.com/acme/acct/evOfKhNU60wg",
"nonce": "JHb54aT_KTXBWQOzGYkt9A",
"url": "https://example.com/acme/revoke-cert" }),
"payload": base64url({
"certificate": "MIIEDTCCAvegAwIBAgIRAP8...",
"reason": 4 }),
"signature": "Q1bURgJoEslbD1c5...3pYdSMLio57mQNN4"
}
using key pair in Certificate POST /acme/revoke-cert HTTP/1.1
Host: example.com
Content-Type: application/jose+json
{
"protected": base64url({
"alg": "RS256",
"jwk": /* certificate's public key */,
"nonce": "JHb54aT_KTXBWQOzGYkt9A",
"url": "https://example.com/acme/revoke-cert" }),
"payload": base64url({
"certificate": "MIIEDTCCAvegAwIBAgIRAP8...",
"reason": 1 }),
"signature": "Q1bURgJoEslbD1c5...3pYdSMLio57mQNN4"
}
Server verifies key used to sign request is authorized
signature with privateKey corresponds to publicKey in Certificate
if account issued Certificate or
account holds authorizations for all identifiers in Certificate
then account is authorized
if revocation succeeds
then returns status code 200 (OK)
example HTTP/1.1 200 OK
Replay-Nonce: IXVHDyxIRGcTE0VSblhPzw
Content-Length: 0
Link: <https://example.com/acme/directory>;rel="index"
if revocation fails
then returns error
example if Certificate is already revoked
then Server returns status code 400 (Bad Request)
error type "urn:ietf:params:acme:error:alreadyRevoked".
HTTP/1.1 403 Forbidden
Replay-Nonce: lXfyFzi6238tfPQRwgfmPU
Content-Type: application/problem+json
Content-Language: en
Link: <https://example.com/acme/directory>;rel="index"
{
"type": "urn:ietf:params:acme:error:unauthorized",
"detail": "No authorization provided for name example.org"
}
IdentifierAuthorization
process to authorize account to manage Certificates for identifier
check that Client controls privateKey of account
identifier
one account can be associated with different identifiers
different accounts can be associated with one identifier
AuthorizationReso is tied to account key
steps Client POST to one URL in "authorizations" of OrderReso
example POST /acme/authz/PAniVnsZcis HTTP/1.1
Host: example.com
Content-Type: application/jose+json
{
"protected": base64url({ "alg": "ES256",
"kid": "https://example.com/acme/acct/evOfKhNU60wg",
"nonce": "uQpSjlRb4vQVCjVYAyyUWg",
"url": "https://example.com/acme/authz/PAniVnsZcis" }),
"payload": "",
"signature": "nuSDISbWG8mMgE7H...QyVUL68yzf3Zawps"
}
Server returns example HTTP/1.1 200 OK
Content-Type: application/json
Link: <https://example.com/acme/directory>;rel="index"
{
"status": "pending",
"expires": "2016-01-02T14:09:30Z",
"identifier": { "type": "dns",
"value": "www.example.org" },
"challenges": [
{
"type": "http-01",
"url": "https://example.com/acme/chall/prV_B7yEyA4",
"token": "DGyRejmCefe7v4NfDGDKfA"
},
{
"type": "dns-01",
"url": "https://example.com/acme/chall/Rg5dV14Gh1Q",
"token": "DGyRejmCefe7v4NfDGDKfA"
}
]
}
DeactivatingAuthorization
Client wants to relinquish its authorization to issue Certificates for identifier
POST to URL of each AuthorizationReso associated with identifier
with {"status": "deactivated"}
example POST /acme/authz/PAniVnsZcis HTTP/1.1
Host: example.com
Content-Type: application/jose+json
{
"protected": base64url({
"alg": "ES256",
"kid": "https://example.com/acme/acct/evOfKhNU60wg",
"nonce": "xWCM9lGbIyCgue8di6ueWQ",
"url": "https://example.com/acme/authz/PAniVnsZcis"
}),
"payload": base64url({ "status": "deactivated" }),
"signature": "srX9Ji7Le9bjszhu...WTFdtujObzMtZcx4"
}
Server verifies request is signed by account key that owns AuthorizationReso
if deactivation is accepted
then returns status code 200 (OK)
updated AuthorizationReso
does not treat deactivated AuthorizationReso as sufficient for issuing certificates
ChallengeResponse
prove control of identifier
receive authorization
Client wants to respond to "http-01" ChallengeReso
POST with empty JSON in "payload"
to URL of ChallengeReso
example POST /acme/chall/prV_B7yEyA4 HTTP/1.1
Host: example.com
Content-Type: application/jose+json
{
"protected": base64url({
"alg": "ES256",
"kid": "https://example.com/acme/acct/evOfKhNU60wg",
"nonce": "Q_s3MWoqT05TrdkM2MTDcw",
"url": "https://example.com/acme/chall/prV_B7yEyA4" }),
"payload": base64url({}),
"signature": "9cbg5JO1Gf5YLjjz...SpkUfcdPai9uVYYQ"
}
Server updates ChallengeReso of AuthorizationReso using received data
ignores fields not specified for this type of ChallengeReso
returns status code 200 (OK)
updated ChallengeReso
if received data is invalid or
can not validate ChallengeReso
then Server returns HTTP error
Client may undo actions to fulfill ChallengeReso
remove files provisioned to a web server
updates AuthorizationReso to "valid" or "invalid"
when one ChallengeReso validation is completed
if to "valid" then Server includes "expires"
may remove completed ChallengeReso
modify "expires"
not remove "invalid" ChallengeReso
validation may take time
Client POST to URL of AuthorizationReso to see when it is finalized
example POST /acme/authz/PAniVnsZcis HTTP/1.1
Host: example.com
Content-Type: application/jose+json
{
"protected": base64url({ "alg": "ES256",
"kid": "https://example.com/acme/acct/evOfKhNU60wg",
"nonce": "uQpSjlRb4vQVCjVYAyyUWg",
"url": "https://example.com/acme/authz/PAniVnsZcis"}),
"payload": "",
"signature": "nuSDISbWG8mMgE7H...QyVUL68yzf3Zawps"
}
if Client knows when Server validates ChallengeReso
then Client may not poll until seeing validation request from Server
if validation is still in progress
then Server returns status code 200 (OK)
may have Retry-After header to give polling interval to Client
example HTTP/1.1 200 OK
Content-Type: application/json
Link: <https://example.com/acme/directory>;rel="index"
{
"status": "valid",
"expires": "2018-09-09T14:09:01.13Z",
"identifier": { "type": "dns",
"value": "www.example.org" },
"challenges": [ { "type": "http-01",
"url": "https://example.com/acme/chall/prV_B7yEyA4",
"status": "valid",
"validated": "2014-12-01T12:05:13.72Z",
"token": "IlirfxKKXAsHtmzK29Pj8A" } ]
}
Challenges
to complete ChallengeReso Client has to control identifier
have privateKey of account
steps Server gives "challenges" in AuthorizationReso
Client POST to URL in "challenges"
Server may retry validation if it fails
sets own retry schedule
may not retry more than every 5 or 10 seconds
because of propagation delays in HTTP or DNS provisioning
queries may fail while resource is being set up
due to information propagating across cluster
firewall rules not being in place
ChallengeReso remains "processing" while retrying
is marked "invalid" if Server gives up
"error" has information about every retry state
Retry-After header inclued in response
has time after Server's next retry
Client can request retry by sending POST to URL of ChallengeReso with new nonce
then Servers may retry immediately
rate-limit retries to avoid denial-of-service attacks
HTTPChallenge
completed over HTTP not HTTPS
Client will prove it can provision HTTP resources on webServer accessed using DomainName
"type" = "http-01"
"token" string
random value
uniquely identifies this challenge
bits of entropy >= 128
has no characters outside base64url alphabet
base64 padding characters "="
RFC4086 has additional information on randomness requirements
example {
"type": "http-01",
"url": "https://example.com/acme/chall/prV_B7yEyA4",
"status": "pending",
"token": "LoqXcYV8q5ONbJQxbmR7SCTNo3tiAXDfowyjxAjEuX0"
}
steps Client construcs KeyAuthorization from "token" of ChallengeReso
account key of Client
puts KeyAuthorization on webServer Path
Path has fixed prefix "/.well-known/acme-challenge/{token}"
{token} = "token" of ChallengeReso
resource is ASCII representation of KeyAuthorization
POST to Server
with empty JSON in "payload"
signals the ChallengeReso can be validated
example POST /acme/chall/prV_B7yEyA4
Host: example.com
Content-Type: application/jose+json
{
"protected": base64url({
"alg": "ES256",
"kid": "https://example.com/acme/acct/evOfKhNU60wg",
"nonce": "UQI1PoRi5OuXzxuX7V7wL0",
"url": "https://example.com/acme/chall/prV_B7yEyA4"
}),
"payload": base64url({}),
"signature": "Q1bURgJoEslbD1c5...3pYdSMLio57mQNN4"
}
Server receives request of Client
constructs KeyAuthorization from "token" of ChallengeReso
account key of Client
stores KeyAuthorization
populates URL template "http://{DomainName}/.well-known/acme-challenge/{token}" RFC6570
dereferences URL using HTTP GET request
port 80
and may follow redirects
to at least one hosts in DNS A and AAAA records
DomainName may resolve to multiple IPv4 and IPv6
example GET /.well-known/acme-challenge/LoqXcYV8...jxAjEuX0
Host: example.org
webServer receives request of Server
may avoid to copy "token" from request to response
to avoid cross-site scripting vulnerabilities
if copies "token" from request to response
then webServer first validates "token" syntax of request
second copies "token" from request to response
returns to Server
example HTTP/1.1 200 OK
Content-Type: application/octet-stream
LoqXcYV8...jxAjEuX0.9jg46WB3...fm21mqTI
"..." indicates the string is truncated
Server receives response of webServer
may ignore whitespace characters at the end
verifies it has well-formed KeyAuthorization
verifies KeyAuthorization of Server equals
KeyAuthorization of response
updates ChallengeReso to "valid" or "invalid"
webServer may delete data created for ChallengeReso
after knowing ChallengeReso is "valid" or "invalid"
"Well-Known URIs" registry uses template from RFC5785
URI suffix: acme-challenge
Change controller: IETF
Specification document(s): RFC 8555, Section 8.3
Related information: N/A
DNSChallenge
Client proves control of DomainName
"type" = "dns-01"
"token" string
random value RFC4086 has additional information on randomness requirements
uniquely identifies this challenge
bits of entropy >= 128
has no characters outside base64url alphabet
padding characters "="
example {
"type": "dns-01",
"url": "https://example.com/acme/chall/Rg5dV14Gh1Q",
"status": "pending",
"token": "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
}
steps Client constructs KeyAuthorization from "token"
account key of Client
creates TXT record name "_acme-challenge" prepended to DomainName
with Digest = base64url(SHA256(KeyAuthorization))
provisions DNS with TXT record
example if DomainName ="www.example.org"
then Client provision DNS with TXT record _acme-challenge.www.example.org. 300 IN TXT "gfj9Xq...Rg85nM"
responds to Server
to acknowledge ChallengeReso can be validated
with empty JSON in "payload"
example POST /acme/chall/Rg5dV14Gh1Q
Host: example.com
Content-Type: application/jose+json
{
"protected": base64url({
"alg": "ES256",
"kid": "https://example.com/acme/acct/evOfKhNU60wg",
"nonce": "SS2sSl1PtspvFZ08kNtzKd",
"url": "https://example.com/acme/chall/Rg5dV14Gh1Q"
}),
"payload": base64url({}),
"signature": "Q1bURgJoEslbD1c5...3pYdSMLio57mQNN4"
}
Server receives response of Client
constructs KeyAuthorization from "token"
account key of Client
stores KeyAuthorization
computes Digest = base64url(SHA256(KeyAuthorization))
query TXT records
verifies contents of any TXT record match Digest
if verification succeed
then Server updates ChallengeReso to "valid"
if no DNS record is found or
DNS record payload do not pass checks
then Server updates ChallengeReso to "invalid"
Client may delete TXT record created for this DNSChallenge
after knowing ChallengeReso is "valid" or "invalid"
ChangesofTOS
if Server has new terms of service and
wants agreement before accepting new requests
then Server returns status code 403 (Forbidden)
Link header with URL of latest terms-of-service
relation "terms-of-service"
error type "urn:ietf:params:acme:error:userActionRequired"
"instance" with URL the Client should direct a human to visit
for instructions on how to agree to the terms
example HTTP/1.1 403 Forbidden
Replay-Nonce: T81bdZroZ2ITWSondpTmAw
Link: <https://example.com/acme/directory>;rel="index"
Link: <https://example.com/acme/terms/2017-6-02>;rel="terms-of-service"
Content-Type: application/problem+json
Content-Language: en
{
"type": "urn:ietf:params:acme:error:userActionRequired",
"detail": "Terms of service have changed",
"instance": "https://example.com/acme/agreement/?token=W8Ih3PswD-8"
}
Server that support TLS 1.3 may allow Clients to send 0-RTT
have no restrictions on what data can be carried in 0-RTT
may use CORS to be accessible from browser Clients W3C.REC-cors-20140116
and may set HTTP Access-Control-Allow-Origin = "*"
Client may send HTTP Accept-Language RFC7231
to enable localization of error messages
must send HTTP User-Agent RFC7231
may include ACME software name and version
HTTP software name and version
JWS JSON Web Signature RFC7515
provides authentication
verified by Server before processing
uses Flattened JSON Serialization RFC7515
has no Unencoded Payload Option RFC7797
Unprotected Header RFC7515
multiple signatures
members "protected" "nonce"
"url" string with URL to which Client directs request
Server checks if equal to URL in HTTP header
else reject as unauthorized
either "jwk" uses JWK
has publicKey of privateKey used for "signature"
"kid" Key ID
string with URL of AccountReso used for "signature"
else Server rejects requests
"alg" = "ES256" must be supported by Server
"EdDSA" may be supported by Server
using "Ed25519" indicated by "crv" RFC8037
!= "none"
MAC Message Authentication Code algorithm
if unsupported algorithm to Server
then Server returns status code 400 (Bad Request)
error type "urn:ietf:params:acme:error:badSignatureAlgorithm"
ProblemDocument with "algorithms" with array of supported "alg"
"payload" string
"signature" string with base64url(alg(ASCII(b1 || '.' || b2)))
b1 = base64url(UTF8(content of "protected"))
b2 = base64url(UTF8(content of "payload" ))
alg() algorithm defined in "alg"
ASCII() octets of ASCII representation
|| means concatenation
if Server supports "alg" and
does not support or rejects "jwk"
then Server returns status code 400 (Bad Request)
error type "urn:ietf:params:acme:error:badPublicKey"
may return ProblemDocument with reason for rejecting the public keys
examples "alg" is "RS256" but the modulus "n" is too small
"alg" is "ES256" but "jwk" does not contain a valid P-256 public key
"alg" is "EdDSA" and "crv" is "Ed448" but Server only supports "EdDSA" with "Ed25519"
the corresponding private key is known to have been compromised
with HTTP Content-Type = "application/jose+json"
else Server returns status code 415 (Unsupported Media Type)
ES256 RFC7518
uses P-256
P-256 has same constants as secp256r1
P-256 y^2=x^3+a*x+b (mod p) a = -3
secp256r1 y^2=x^3+a*x+b (mod p) a = FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC
a = -3 (mod p) = FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC
G elliptic curve base point
d private key
Q public key
= d*G
n order of G
O identity element
= n*G
SignatureGeneration m message to sign
e = integer(SHA256(m))
k cryptographically secure random integer
>= 1
<= n-1
(x,y) = k*G
r = integer(x)
size = 32byte
s = (e+r*d)/k (mod n)
size = 32byte
if r = 0 or s = 0 then generate k again
calcualte r and s again
output = r || s
size = 64byte
|| means concatenation
SignatureVerification check Q != O
Q*n = O
Q lies on the curve
r > 0
r < n
s > 0
s < n
e = integer(SHA256(m))
u = e/s (mod n)
v = r/s (mod n)
(x,y) = u*G + v*Q
check (x,y) != O
r = x (mod n)
KeyAuthorization = token || '.' || base64url(Thumbprint(accountKey))
|| means concatenation
token string
uses base64url alphabet
Thumbprint = SHA256(UTF-8 representation of accountKey)
RFC7638
accountKey JWK of Client
only has required members RFC7518
members ordered lexicographically
for EC required members are "crv" "kty" "x" "y"
order of members is "crv" "kty" "x" "y"
for RSA required members are "e" "kty" "n"
order of members is "e" "kty" "n"
has no whitespace or line breaks before or after any syntactic elements
JWK JSON Web Key RFC7517
content of "x" is base64url encoded
content of "y" is base64url encoded
example {
"crv":"P-256",
"kty":"EC",
"x":"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU",
"y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0"
}
AKP account key pair
has privateKey used by Client to sign all messages sent
publicKey used by Server to verify all messages received
PKIX Public Key Infrastructure using X.509 Certificates
used for authentication of domain names
CA certification authorities
verifies that the Client represents the domain name in Certificate
CSR Certificate Signing Request
PKCS#10
CORS Cross-Origin Resource Sharing
JSON JavaScript Object Notation
binary fields in ACME are base64url encoded Section 5 of RFC4648
use the profile specified in JWS in Section 2 of RFC7515
trailing '=' characters are stripped else rejected as improperly encoded
Certificate used to validate domain names
types DV "Domain validation" may not verify Client's real-world identity
OV "Organization validation" must verify Client's real-world identity
EV "Extended validation" must verify Client's real-world identity
Errors reported in HTTP layer with codes 4XX or 5XX
ChallengeReso
"type" within URN namespace "urn:ietf:params:acme:error:"
URI accountDoesNotExist request specified account that does not exist
alreadyRevoked request specified certificate to be revoked that has already been revoked
badCSR CSR is unacceptable
badNonce Client sent unacceptable anti-replay nonce
badPublicKey JWS was signed by unsupported publicKey
badRevocationReason revocation reason provided is not allowed
badSignatureAlgorithm JWS was signed with unsupported algorithm
caa Certification Authority Authorization (CAA) records forbid the CA from issuing a certificate
compound Specific error conditions are indicated in the "subproblems" array
connection Server could not connect to validation target
dns there was a problem with DNS query during identifier validation
externalAccountRequired request must include value for the "externalAccountBinding" field
incorrectResponse Response received didn't match the challenge's requirements
invalidContact contact URL for account was invalid
malformed request message was malformed
orderNotReady request attempted to finalize order that is not ready to be finalized
rateLimited request exceeds rate limit
rejectedIdentifier Server will not issue certificates for the identifier
serverInternal Server experienced an internal error
tls Server received TLS error during validation
unauthorized Client lacks sufficient authorization
unsupportedContact contact URL for account used an unsupported protocol scheme
unsupportedIdentifier identifier is of unsupported type
userActionRequired Visit "instance" URL and take actions specified there
example error type "badCSR" refers to "type":"urn:ietf:params:acme:error:badCSR"
Servers may use other URI
do not use ACME URN namespace for errors not listed in the appropriate IANA registry
Clients may display "detail" field of all errors
may come with ProblemDocument
ProblemDocument RFC7807
provides additional information
"subproblems" may be included
need not all have same type
do not need to match top level type
has multiple errors in response to request
JSON array of ProblemDocument
each ProblemDocument with "identifier"
"identifier" can only be inside ProblemDocument of "subproblems"
used by Clients
hints that an operation would succeed if that identifier were omitted
example if newOrder contains ten DNS "identifier" and
Server returns ProblemDocument with two "subproblems" referencing two "identifier"
then Client may do newOrder containing only eight "identifier"
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
Link: <https://example.com/acme/directory>;rel="index"
{
"type": "urn:ietf:params:acme:error:malformed",
"detail": "Some of the identifiers requested were rejected",
"subproblems": [
{
"type": "urn:ietf:params:acme:error:malformed",
"detail": "Invalid underscore in DNS name \"_example.org\"",
"identifier": {
"type": "dns",
"value": "_example.org"
}
},
{
"type": "urn:ietf:params:acme:error:rejectedIdentifier",
"detail": "This CA will not issue for \"example.net\"",
"identifier": {
"type": "dns",
"value": "example.net"
}
}
]
}
base64url Base64 Encoding with URL and Filename Safe Alphabet
________________________________________________________________
| Value Encoding Value Encoding Value Encoding Value Encoding |
| 0 A 17 R 34 i 51 z |
| 1 B 18 S 35 j 52 0 |
| 2 C 19 T 36 k 53 1 |
| 3 D 20 U 37 l 54 2 |
| 4 E 21 V 38 m 55 3 |
| 5 F 22 W 39 n 56 4 |
| 6 G 23 X 40 o 57 5 |
| 7 H 24 Y 41 p 58 6 |
| 8 I 25 Z 42 q 59 7 |
| 9 J 26 a 43 r 60 8 |
| 10 K 27 b 44 s 61 9 |
| 11 L 28 c 45 t 62 - |
| 12 M 29 d 46 u 63 _ |
| 13 N 30 e 47 v |
| 14 O 31 f 48 w pad = |
| 15 P 32 g 49 x |
| 16 Q 33 h 50 y |
|________________________________________________________________|
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_
padding is ommited for ACME
CSR Entity defines signature algorithm
generates pair publicKey
privateKey
that depends on signature algorithm
constructs CertificationRequest with publicKey
requests certificate to CA using CertificationRequest
ASN.1 from RFC5912
CertificationRequest ::= SEQUENCE {
certificationRequestInfo SEQUENCE {
version INTEGER,
subject Name,
subjectPublicKeyInfo SEQUENCE {
algorithm AlgorithmIdentifier{PUBLIC-KEY, {PublicKeyAlgorithms}},
subjectPublicKey BIT STRING
},
attributes [0] IMPLICIT SET OF AttributeSet{{AttributeList}}
},
signatureAlgorithm AlgorithmIdentifier {SIGNATURE-ALGORITHM, {SignatureAlgorithms}},
signature BIT STRING
}
version INTEGER Value = 0
for compatibility with future revisions
subject Name ::= CHOICE { -- only one possibility for now -- rdnSequence RDNSequence }
RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
DistinguishedName ::= RDNSequence
RelativeDistinguishedName ::= SET SIZE (1 .. MAX) OF SingleAttribute { {SupportedAttributes} }
SingleAttribute{ATTRIBUTE:AttrSet} ::= SEQUENCE {
type ATTRIBUTE.&id({AttrSet}),
value ATTRIBUTE.&Type({AttrSet}{@type})
}
SupportedAttributes ATTRIBUTE ::= {
at-name | at-surname | at-givenName | at-initials | at-generationQualifier | at-x520CommonName |
at-x520LocalityName | at-x520StateOrProvinceName | at-x520OrganizationName | at-x520OrganizationalUnitName |
at-x520Title | at-x520dnQualifier | at-x520countryName | at-x520SerialNumber | at-x520Pseudonym |
at-domainComponent | at-emailAddress, ... }
ATTRIBUTE ::= CLASS {
&id OBJECT IDENTIFIER UNIQUE,
&Type OPTIONAL,
&equality-match MATCHING-RULE OPTIONAL,
&minCount INTEGER DEFAULT 1,
&maxCount INTEGER OPTIONAL
} WITH SYNTAX {
[TYPE &Type]
[EQUALITY MATCHING RULE &equality-match]
[COUNTS [MIN &minCount] [MAX &maxCount]]
IDENTIFIED BY &id
}
distinguished name of Entity
defined in X.501
attributes defined in RFC2985
X.520
commonName domain name to certify
example *.example.com
organizationName business name
example Exomple Fondoton, Inc.
organizationalUnitName Department Name
example Finance
localityName town city
example Edinburgh
stateOrProvinceName region county
example Normandy
countryName two-letter code of country
ISO 3166-1 alpha-2
example US
emailAddress example admin@example.com
algorithm has information about publicKey of Entity
this only shows data for secp256r1 use case
AlgorithmIdentifier{PUBLIC-KEY, {PublicKeyAlgorithms}}
AlgorithmIdentifier{ALGORITHM-TYPE, ALGORITHM-TYPE:AlgorithmSet} ::= SEQUENCE {
algorithm ALGORITHM-TYPE.&id({AlgorithmSet}),
parameters ALGORITHM-TYPE.&Params({AlgorithmSet}{@algorithm}) OPTIONAL
}
PublicKeyAlgorithms PUBLIC-KEY ::= { PKIXAlgs-2009.PublicKeys, ..., PKIX1-PSS-OAEP-Algorithms-2009.PublicKeys}
PublicKeys PUBLIC-KEY ::= { pk-rsa | pk-dsa | pk-dh | pk-kea, ..., pk-ec | pk-ecDH | pk-ecMQV }
pk-ec PUBLIC-KEY ::= {
IDENTIFIER id-ecPublicKey
KEY ECPoint
PARAMS TYPE ECParameters ARE required
-- Private key format not in this module --
CERT-KEY-USAGE { digitalSignature, nonRepudiation, keyAgreement, keyCertSign, cRLSign }
}
PUBLIC-KEY ::= CLASS {
&id OBJECT IDENTIFIER UNIQUE,
&KeyValue OPTIONAL,
&Params OPTIONAL,
¶mPresence ParamOptions DEFAULT absent,
&keyUsage KeyUsage OPTIONAL,
&PrivateKey OPTIONAL
} WITH SYNTAX {
IDENTIFIER &id
[KEY &KeyValue]
[PARAMS [TYPE &Params] ARE ¶mPresence]
[CERT-KEY-USAGE &keyUsage]
[PRIVATE-KEY &PrivateKey]
}
id-ecPublicKey OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 }
ECParameters ::= CHOICE { namedCurve CURVE.&id({NamedCurve}) }
CURVE ::= CLASS { &id OBJECT IDENTIFIER UNIQUE } WITH SYNTAX { ID &id }
NamedCurve CURVE ::= {
{ ID secp192r1 } | { ID sect163k1 } | { ID sect163r2 } | { ID secp224r1 } | { ID sect233k1 } | { ID sect233r1 } |
{ ID secp256r1 } | { ID sect283k1 } | { ID sect283r1 } | { ID secp384r1 } | { ID sect409k1 } | { ID sect409r1 } |
{ ID secp521r1 } | { ID sect571k1 } | { ID sect571r1 }, ... -- Extensible
}
secp256r1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) prime(1) 7 }
subjectPublicKey this only shows data for secp256r1 use case
implementations must support uncompressed form of ECC publicKey
may support compressed form of ECC publicKey
must not support hybrid form of ECC publicKey
pk-ec has ECPoint ::= OCTET STRING
if publicKey is compressed then ECPoint Value = follow Sections 2.3.3 of SEC1
if publicKey is uncompressed then ECPoint Value = 0x04 || x || y
size = 1+32+32 bytes
(x,y) of publicKey
BIT STRING Value = 0x00 || bitS
first byte indicates how many bits in last byte of bitS are ignored
bitS = converted ECPoint Value
most significant bit of ECPoint Value is the
most significant bit of bitS and so on
least significant bit of ECPoint Value is the
least significant bit of bitS and so on
section 8.6 of X.690
attributes [0] IMPLICIT SET OF AttributeSet{{AttributeList}} go to 8.14.4 of X.690 to understand this ASN.1
AttributeSet{ATTRIBUTE:AttrSet} ::= SEQUENCE {
type ATTRIBUTE.&id({AttrSet}),
values SET SIZE (1..MAX) OF ATTRIBUTE. &Type({AttrSet}{@type})
}
AttributeList ATTRIBUTE ::= {at-extension-req, ...}
at-extension-req ATTRIBUTE ::= { TYPE ExtensionReq IDENTIFIED BY id-ExtensionReq }
id-ExtensionReq OBJECT IDENTIFIER ::= {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9) 14}
ExtensionReq ::= SEQUENCE SIZE (1..MAX) OF Extension{{CertExtensions}}
Extension{EXTENSION:ExtensionSet} ::= SEQUENCE {
extnID EXTENSION.&id({ExtensionSet}),
critical BOOLEAN -- (EXTENSION.&Critical({ExtensionSet}{@extnID}))
DEFAULT FALSE,
extnValue OCTET STRING (CONTAINING EXTENSION.&ExtnType({ExtensionSet}{@extnID}))
-- contains the DER encoding of the ASN.1 value corresponding to the extension type identified by extnID
}
CertExtensions EXTENSION ::= {
ext-AuthorityKeyIdentifier | ext-SubjectKeyIdentifier | ext-KeyUsage | ext-PrivateKeyUsagePeriod |
ext-CertificatePolicies | ext-PolicyMappings | ext-SubjectAltName | ext-IssuerAltName |
ext-SubjectDirectoryAttributes | ext-BasicConstraints | ext-NameConstraints | ext-PolicyConstraints |
ext-ExtKeyUsage | ext-CRLDistributionPoints | ext-InhibitAnyPolicy | ext-FreshestCRL |
ext-AuthorityInfoAccess | ext-SubjectInfoAccessSyntax, ... }
may have challengePassword RFC2985
extensionRequest RFC2985
signatureAlgorithm this only shows data for secp256r1 use case
AlgorithmIdentifier {SIGNATURE-ALGORITHM, {SignatureAlgorithms}}
SIGNATURE-ALGORITHM ::= CLASS {
&id OBJECT IDENTIFIER UNIQUE,
&Value OPTIONAL,
&Params OPTIONAL,
¶mPresence ParamOptions DEFAULT absent,
&HashSet DIGEST-ALGORITHM OPTIONAL,
&PublicKeySet PUBLIC-KEY OPTIONAL,
&smimeCaps SMIME-CAPS OPTIONAL
} WITH SYNTAX {
IDENTIFIER &id
[VALUE &Value]
[PARAMS [TYPE &Params] ARE ¶mPresence ]
[HASHES &HashSet]
[PUBLIC-KEYS &PublicKeySet]
[SMIME-CAPS &smimeCaps]
}
SignatureAlgorithms SIGNATURE-ALGORITHM ::= {
PKIXAlgs-2009.SignatureAlgs, ...,
PKIX1-PSS-OAEP-Algorithms-2009.SignatureAlgs }
SignatureAlgs SIGNATURE-ALGORITHM ::= {
sa-rsaWithMD2 | sa-rsaWithMD5 |
sa-rsaWithSHA1 | sa-dsaWithSHA1 |
sa-ecdsaWithSHA1, ..., -- Extensible
sa-dsaWithSHA224 | sa-dsaWithSHA256 |
sa-ecdsaWithSHA224 | sa-ecdsaWithSHA256 |
sa-ecdsaWithSHA384 | sa-ecdsaWithSHA512
}
sa-ecdsaWithSHA256 SIGNATURE-ALGORITHM ::= {
IDENTIFIER ecdsa-with-SHA256
VALUE ECDSA-Sig-Value
PARAMS TYPE NULL ARE absent
HASHES { mda-sha256 }
PUBLIC-KEYS { pk-ec }
SMIME-CAPS { IDENTIFIED BY ecdsa-with-SHA256 }
}
ecdsa-with-SHA256 OBJECT IDENTIFIER ::= {
iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 }
signature this only shows data for secp256r1 use case
steps generate r and s of SignatureGeneration of ES256
with key pair of Entity
with m = DER encoded certificationRequestInfo
if r left most significant byte = 0 then remove it and repeat this step until not true
if s left most significant byte = 0 then remove it and repeat this step until not true
if r left most significant bit = 1 then append one byte to the left with value 0
if s left most significant bit = 1 then append one byte to the left with value 0
put r in Value of INTEGER r of ECDSA-Sig-Value
put s in Value of INTEGER s of ECDSA-Sig-Value
ECDSA-Sig-Value ::= SEQUENCE { r INTEGER, s INTEGER }
BIT STRING Value = 0x00 || ECDSA-Sig-Value
first byte indicates how many bits in last byte of ECDSA-Sig-Value are ignored
example using hexadecimal representation
secp256r1
data starts with ----------> 30 82 01 80 30...
CertificationRequest TagType SEQUENCE = 30
Length in next 2 bytes = 82
Length = 01 80
CertificationRequestInfo TagType SEQUENCE = 30
Length in next 2 bytes = 82
Length = 01 25
version TagType INTEGER = 02
Length = 01
Value = 00
subject TagType SEQUENCE = 30
Length in next 1 bytes = 81
Length = 87
____ TagType SET = 31
Length = 0B
TagType SEQUENCE = 30
Length = 09
TagType OBJECT = 06
Length = 03
Value Country = 55 04 06
TagType PRINTABLESTRING = 13
Length = 02
Value US = 55 53
____ TagType SET = 31
Length = 13
TagType SEQUENCE = 30
Length = 11
TagType OBJECT = 06
Length = 03
Value State = 55 04 08
TagType UTF8STRING = 0C
Length = 0A
Value California = 43 61 6C 69 66 6F 72 6E 69 61
____ TagType SET = 31
Length = 11
TagType SEQUENCE = 30
Length = 0F
TagType OBJECT = 06
Length = 03
Value Locality = 55 04 07
TagType UTF8STRING = 0C
Length = 08
Value = 4E 65 77 20 59 6F 72 6B
____ TagType SET = 31
Length = 16
TagType SEQUENCE = 30
Length = 14
TagType OBJECT = 06
Length = 03
Value Organization = 55 04 0A
TagType UTF8STRING = 0C
Length = 0D
Value = 4D 75 6E 73 74 65 72 73 20 2E 69 6E 63
____ TagType SET = 31
Length = 16
TagType SEQUENCE = 30
Length = 14
TagType OBJECT = 06
Length = 03
Value Common Name = 55 04 03
TagType UTF8STRING = 0C
Length = 0D
Value = 2A 2E 65 78 61 6D 70 6C 65 2E 63 6F 6D
____ TagType SET = 31
Length = 20
TagType SEQUENCE = 30
Length = 1E
TagType OBJECT = 06
Length = 09
Value email address = 2A 86 48 86 F7 0D 01 09 01
TagType IA5STRING = 16
Length = 11
Value = 61 64 6D 69 6E 40 65 78 61 6D 70 6C 65 2E 63 6F 6D
SubjectPublicKeyInfo TagType SEQUENCE = 30
Length = 59
AlgorithmIdentifier TagType SEQUENCE = 30
Length = 13
ALGORITHM.&id TagType OBJECT = 06
Length = 07
Value id-ecPublicKey = 2A 86 48 CE 3D 02 01
ALGORITHM.&Type TagType OBJECT = 06
Length = 08
Value secp256r1 = 2A 86 48 CE 3D 03 01 07
subjectPublicKey TagType BIT STRING = 03
Length = 42
Value = 00 04 CE FE 2F 9A D8 78 A3 3B F4 65 C2 D0 DF 01 DB 6D 1C A4 41 F9 10 F3 51 46 82 79 CA 8D 52 35 50 B1 90 CE 06 07 38 35 F9 FA 91 52 31 83 2B 17 C1 D7 AC DE 1B BE 01 35 55 10 0F 65 86 97 45 4B FB 23
attributes TagType cont [ 0 ] = A0
Length = 3B
Attribute TagType SEQUENCE = 30
Length = 1A
TagType OBJECT = 06
Length = 09
Value unstructuredName = 2A 86 48 86 F7 0D 01 09 02
TagType SET = 31
Length = 0D
TagType UTF8STRING = 0C
Length = 0B
Value = 47 75 75 67 6C 65 20 2E 69 6E 63
Attribute TagType SEQUENCE = 30
Length = 1D
TagType OBJECT = 06
Length = 09
Value challengePassword = 2A 86 48 86 F7 0D 01 09 07
TagType SET = 31
Length = 10
TagType UTF8STRING = 0C
Length = 0E
Value = 63 68 6F 68 6F 68 6F 68 6F 68 6F 66 6F 69
signatureAlgorithm TagType SEQUENCE = 30
Length = 0A
TagType OBJECT = 06
Length = 08
Value ecdsa-with-SHA256 = 2A 86 48 CE 3D 04 03 02
signature TagType BIT STRING = 03
Length = 49
Value = 00
= 30 46
= 02 21
= 00 D4 7D 86 A0 05 67 27 52 6E 36 86 A7 BC 68 1B 6C 01 CE CD B7 F6 E7 7F A4 89 44 67 27 1F 6B 24 29
= 02 21
= 00 F4 FA C4 DE F7 32 14 2C 56 0A E5 EF A9 E5 BD 69 87 FC 95 03 85 33 A7 E1 74 D7 69 05 17 1E 41 15
X.509 RFC5280
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING
}
TBSCertificate ::= SEQUENCE {
version [0] EXPLICIT Version DEFAULT v1,
serialNumber CertificateSerialNumber,
signature AlgorithmIdentifier,
issuer Name,
validity Validity,
subject Name,
subjectPublicKeyInfo SubjectPublicKeyInfo,
issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, -- If present, version MUST be v2 or v3
subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, -- If present, version MUST be v2 or v3
extensions [3] EXPLICIT Extensions OPTIONAL -- If present, version MUST be v3
}
Version ::= INTEGER { v1(0), v2(1), v3(2) }
CertificateSerialNumber ::= INTEGER
Validity ::= SEQUENCE {
notBefore Time,
notAfter Time
}
Time ::= CHOICE {
utcTime UTCTime,
generalTime GeneralizedTime
}
UniqueIdentifier ::= BIT STRING
SubjectPublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
subjectPublicKey BIT STRING
}
Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
Extension ::= SEQUENCE {
extnID OBJECT IDENTIFIER,
critical BOOLEAN DEFAULT FALSE,
extnValue OCTET STRING -- contains the DER encoding of an ASN.1 value corresponding to the extension type identified by extnID
}
GeneralizedTime YYYYMMDDHHMMSSZ
seconds never ommited
used if year >= 2050
UTCTime YYMMDDHHMMSSZ
seconds never ommited
used if year < 2050
example 2018-10-05 01:38:17 GMT = 181005013817Z = 31 38 31 30 30 35 30 31 33 38 31 37 5a = Value of UTCTime
2019-10-05 01:38:17 GMT = 191005013817Z = 31 39 31 30 30 35 30 31 33 38 31 37 5a = Value of UTCTime
NormalvsCompact Normal The Netscape/Microsoft browser wars in the mid-90's were really vicious and competitive. They really
had it out for each other. Netscape had developed the SSL protocol. The initial version had cryptographic
flaws and was broken pretty quickly, and never released. The first production version was SSL 2, which was
in use for a few years. (I don't know the exact versions of Navigator it shipped in.)
SSL 2 had some flaws, both cryptographic and practical; not dramatic enough to make replacing it a crisis,
but it clearly needed some work from early on.
As a part of the cutthroat competition, Microsoft decided to revise the SSL 2 protocol with some additions
of their own, and specified a protocol called "PCT" that was derived from SSL 2. It was only supported in
IE and IIS.
Netscape also wanted to address SSL 2 issues, but wasn't going to let Microsoft take leadership/ownership
in the standard, so they developed SSL 3.0, which was a more significant departure.
Various people in the industry & community didn't want a fork, so we (Consensus Development, where I worked
with Christopher Allen at the time, and where I had written the SSL 3.0 reference implementation under
contract to Netscape) hosted a meeting between representatives from Netscape and Microsoft; I forget
everyone who was there, but I recall that Bruce Schneier was there (before he was famous), and probably
Paul Kocher, who had designed the SSL 3 protocol; Barbara Fox represented Microsoft. And we negotiated a
deal where Microsoft and Netscape would both support the IETF taking over the protocol and standardizing it
in an open process, which led to me editing the RFC.
As a part of the horsetrading, we had to make some changes to SSL 3.0 (so it wouldn't look the IETF was
just rubberstamping Netscape's protocol), and we had to rename the protocol (for the same reason). And thus
was born TLS 1.0 (which was really SSL 3.1). And of course, now, in retrospect, the whole thing looks silly.
http://tim.dierks.org/2014/05/security-standards-and-name-changes-in.html
Compact protocols SSL developed by Netscape
version 1 initial version
never released
had cryptographic flaws
2 first production version
few years of use
needed work early
flaws cryptographic
practical
addresed by Netscape
Microsoft
replacing was not a crisis
used by unknow version of Navigator
revised and has additions by Microsoft
3.0 created to fix SSL 2 issues
not let Microsoft take leadership/ownership in the standard
more significant departure from standard
fork not wanted by people in industry
community
designed by Paul Kocher
reference implementation written by Tim Dierks
3.1 renamed to TLS 1.0
born because of TheMeeting
PCT specified by microsoft
derived from SSL 2
made to compete with SSL 2
only supported by IE
IIS
War who takes leadership/ownership in the standard
opponents Netscape
Microsoft
mid-90's
really vicious
competitive cutthroat
had each other out
TheMeeting hosted by ConsensusDevelopment where SSL version 3.0 reference implementation was written
relevant workers Tim Dierks
Christopher Allen
attendees Netscape Tim Dierks
Bruce Schneier
probably Paul Kocher
Microsoft Barbara Fox
more forgotten people
NegotiatedDeal IETF takes over SSL
standardizes SSL
make changes to SSL version 3.0
rename SSL version 3.0 to TLS 1.0
hide IETF rubberstamping on SSL
Tim Dierks edits RFC
next update changes character font into Gabor Patch font
left to right reading into one on top of the other font zooming
adds program that transforms text from Normal to Compact
Compact to Normal