@@ -47,6 +47,8 @@ class HTTP::Proxy::Server
4747 serial_path : String ) : Bool
4848 return false unless generate_private_key(key_path)
4949
50+ serial = next_serial(serial_path)
51+
5052 host_key = load_private_key(key_path)
5153 return false if host_key.null?
5254
@@ -59,11 +61,10 @@ class HTTP::Proxy::Server
5961 csr = create_csr(host, host_key)
6062 return false if csr.null?
6163
62- cert, serial = sign_csr(host, csr, ca_cert, ca_key)
64+ cert = sign_csr(host, serial , csr, ca_cert, ca_key)
6365 return false if cert.null?
6466
6567 return false unless write_certificate(cert_path, cert)
66- File .write(serial_path, serial.to_s)
6768 true
6869 ensure
6970 LibCrypto .x509_req_free(csr) if csr && ! csr.null?
@@ -126,50 +127,58 @@ class HTTP::Proxy::Server
126127 # Native equivalent of:
127128 # openssl x509 -req -in <csr> -CA <ca_cert> -CAkey <ca_key> \
128129 # -days 825 -sha256 -extfile <ext>
129- private def sign_csr (host : String , req : LibCrypto ::X509_REQ , ca_cert : LibCrypto ::X509 , ca_key : LibCrypto ::EVP_PKEY ) : { LibCrypto ::X509 , Int64 }
130+ private def sign_csr (host : String , serial : Int64 , req : LibCrypto ::X509_REQ , ca_cert : LibCrypto ::X509 , ca_key : LibCrypto ::EVP_PKEY ) : LibCrypto ::X509
130131 cert = LibCrypto .x509_new
131- return { Pointer (Void ).null.as(LibCrypto ::X509 ), 0 _ i64 } if cert.null?
132+ return Pointer (Void ).null.as(LibCrypto ::X509 ) if cert.null?
132133
133134 req_pubkey = LibCrypto .x509_req_get_pubkey(req)
134- return { Pointer (Void ).null.as(LibCrypto ::X509 ), 0 _ i64 } if req_pubkey.null?
135+ return Pointer (Void ).null.as(LibCrypto ::X509 ) if req_pubkey.null?
135136
136137 begin
137- serial = Time .utc.to_unix
138-
139- return {Pointer (Void ).null.as(LibCrypto ::X509 ), 0 _i64 } if LibCrypto .x509_set_version(cert, 2 ) != 1
138+ return Pointer (Void ).null.as(LibCrypto ::X509 ) if LibCrypto .x509_set_version(cert, 2 ) != 1
140139 serial_number = LibCrypto .x509_get_serial_number(cert)
141- return { Pointer (Void ).null.as(LibCrypto ::X509 ), 0 _ i64 } if serial_number.null?
142- return { Pointer (Void ).null.as(LibCrypto ::X509 ), 0 _ i64 } if LibCrypto .asn1_integer_set(serial_number, serial) != 1
140+ return Pointer (Void ).null.as(LibCrypto ::X509 ) if serial_number.null?
141+ return Pointer (Void ).null.as(LibCrypto ::X509 ) if LibCrypto .asn1_integer_set(serial_number, serial) != 1
143142
144143 not_before = LibCrypto .x509_getm_not_before(cert)
145144 not_after = LibCrypto .x509_getm_not_after(cert)
146- return { Pointer (Void ).null.as(LibCrypto ::X509 ), 0 _ i64 } if not_before.null? || not_after.null?
147- return { Pointer (Void ).null.as(LibCrypto ::X509 ), 0 _ i64 } if LibCrypto .x509_gmtime_adj(not_before, 0 ).null?
148- return { Pointer (Void ).null.as(LibCrypto ::X509 ), 0 _ i64 } if LibCrypto .x509_gmtime_adj(not_after, 825 _i64 * 24 * 60 * 60 ).null?
145+ return Pointer (Void ).null.as(LibCrypto ::X509 ) if not_before.null? || not_after.null?
146+ return Pointer (Void ).null.as(LibCrypto ::X509 ) if LibCrypto .x509_gmtime_adj(not_before, 0 ).null?
147+ return Pointer (Void ).null.as(LibCrypto ::X509 ) if LibCrypto .x509_gmtime_adj(not_after, 825 _i64 * 24 * 60 * 60 ).null?
149148
150149 req_subject = LibCrypto .x509_req_get_subject_name(req)
151150 issuer_subject = LibCrypto .x509_get_subject_name(ca_cert)
152- return { Pointer (Void ).null.as(LibCrypto ::X509 ), 0 _ i64 } if req_subject.null? || issuer_subject.null?
151+ return Pointer (Void ).null.as(LibCrypto ::X509 ) if req_subject.null? || issuer_subject.null?
153152
154- return { Pointer (Void ).null.as(LibCrypto ::X509 ), 0 _ i64 } if LibCrypto .x509_set_subject_name(cert, req_subject) != 1
155- return { Pointer (Void ).null.as(LibCrypto ::X509 ), 0 _ i64 } if LibCrypto .x509_set_issuer_name(cert, issuer_subject) != 1
156- return { Pointer (Void ).null.as(LibCrypto ::X509 ), 0 _ i64 } if LibCrypto .x509_set_pubkey(cert, req_pubkey) != 1
153+ return Pointer (Void ).null.as(LibCrypto ::X509 ) if LibCrypto .x509_set_subject_name(cert, req_subject) != 1
154+ return Pointer (Void ).null.as(LibCrypto ::X509 ) if LibCrypto .x509_set_issuer_name(cert, issuer_subject) != 1
155+ return Pointer (Void ).null.as(LibCrypto ::X509 ) if LibCrypto .x509_set_pubkey(cert, req_pubkey) != 1
157156
158- return { Pointer (Void ).null.as(LibCrypto ::X509 ), 0 _ i64 } unless add_extension(cert, " basicConstraints" , " critical,CA:FALSE" )
159- return { Pointer (Void ).null.as(LibCrypto ::X509 ), 0 _ i64 } unless add_extension(cert, " keyUsage" , " critical,digitalSignature,keyEncipherment" )
160- return { Pointer (Void ).null.as(LibCrypto ::X509 ), 0 _ i64 } unless add_extension(cert, " extendedKeyUsage" , " serverAuth" )
157+ return Pointer (Void ).null.as(LibCrypto ::X509 ) unless add_extension(cert, " basicConstraints" , " critical,CA:FALSE" )
158+ return Pointer (Void ).null.as(LibCrypto ::X509 ) unless add_extension(cert, " keyUsage" , " critical,digitalSignature,keyEncipherment" )
159+ return Pointer (Void ).null.as(LibCrypto ::X509 ) unless add_extension(cert, " extendedKeyUsage" , " serverAuth" )
161160
162161 san_value = Socket ::IPAddress .valid?(host) ? " IP:#{ host } " : " DNS:#{ host } "
163- return { Pointer (Void ).null.as(LibCrypto ::X509 ), 0 _ i64 } unless add_extension(cert, " subjectAltName" , san_value)
162+ return Pointer (Void ).null.as(LibCrypto ::X509 ) unless add_extension(cert, " subjectAltName" , san_value)
164163
165- return { Pointer (Void ).null.as(LibCrypto ::X509 ), 0 _ i64 } if LibCrypto .x509_sign(cert, ca_key, LibCrypto .evp_sha256) <= 0
164+ return Pointer (Void ).null.as(LibCrypto ::X509 ) if LibCrypto .x509_sign(cert, ca_key, LibCrypto .evp_sha256) <= 0
166165
167- { cert, serial}
166+ cert
168167 ensure
169168 LibCrypto .evp_pkey_free(req_pubkey)
170169 end
171170 end
172171
172+ private def next_serial (serial_path : String ) : Int64
173+ current = if File .exists?(serial_path)
174+ File .read(serial_path).strip.to_i64?
175+ end
176+ base = current || (Time .utc.to_unix * 1000 )
177+ serial = base + 1
178+ File .write(serial_path, serial.to_s)
179+ serial
180+ end
181+
173182 # Native equivalent of adding extension entries via extfile in `openssl x509 -extfile ...`.
174183 private def add_extension (cert : LibCrypto ::X509 , name : String , value : String ) : Bool
175184 nid = LibCrypto .obj_sn2nid(name.to_unsafe)
0 commit comments