11import Foundation
22import OpenSSL
3+
34enum CertGenError : Error {
45 case keyGenerationFailed( String )
56 case x509CreationFailed( String )
67 case writeFailed( String )
78 case sanCreationFailed( String )
89}
10+
911public final class GenerateCert {
1012
1113 public static func createAndSaveCerts( caCN: String = " ProStore " ,
@@ -17,15 +19,25 @@ public final class GenerateCert {
1719 OPENSSL_init_ssl ( UInt64 ( OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS) , nil )
1820 OPENSSL_init_crypto ( UInt64 ( OPENSSL_INIT_LOAD_CONFIG | OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS) , nil )
1921
20- guard let caPkey = try generateRSAKey ( bits: rsaBits) else { throw CertGenError . keyGenerationFailed ( " CA key generation failed " ) }
22+ guard let caPkey = try generateRSAKey ( bits: rsaBits) else {
23+ throw CertGenError . keyGenerationFailed ( " CA key generation failed " )
24+ }
2125
2226 guard let caX509 = try createSelfSignedCertificate ( pkey: caPkey, commonName: caCN, days: daysValid, isCA: true ) else {
27+ EVP_PKEY_free ( caPkey)
2328 throw CertGenError . x509CreationFailed ( " CA certificate creation failed " )
2429 }
2530
26- guard let serverPkey = try generateRSAKey ( bits: rsaBits) else { throw CertGenError . keyGenerationFailed ( " Server key generation failed " ) }
31+ guard let serverPkey = try generateRSAKey ( bits: rsaBits) else {
32+ EVP_PKEY_free ( caPkey)
33+ X509_free ( caX509)
34+ throw CertGenError . keyGenerationFailed ( " Server key generation failed " )
35+ }
2736
2837 guard let serverX509 = try createCertificateSignedByCA ( serverPKey: serverPkey, caPkey: caPkey, caX509: caX509, commonName: serverCN, days: daysValid) else {
38+ EVP_PKEY_free ( caPkey)
39+ X509_free ( caX509)
40+ EVP_PKEY_free ( serverPkey)
2941 throw CertGenError . x509CreationFailed ( " Server certificate creation failed " )
3042 }
3143
@@ -48,7 +60,8 @@ public final class GenerateCert {
4860 try writePrivateKeyPEM ( pkey: serverPkey, to: serverKeyURL. path)
4961 try writeX509PEM ( x509: serverX509, to: serverCertURL. path)
5062
51- try writePKCS12 ( pkey: serverPkey, cert: serverX509, caCert: caX509, to: localhostP12URL. path, password: nil )
63+ // Try with empty password first, which is what installApp.swift expects
64+ try writePKCS12 ( pkey: serverPkey, cert: serverX509, caCert: caX509, to: localhostP12URL. path, password: " " )
5265
5366 EVP_PKEY_free ( caPkey)
5467 X509_free ( caX509)
@@ -90,11 +103,14 @@ public final class GenerateCert {
90103
91104 return pkey
92105 }
106+
93107 private static func createSelfSignedCertificate( pkey: OpaquePointer ? ,
94108 commonName: String ,
95109 days: Int32 ,
96110 isCA: Bool ) throws -> OpaquePointer ? {
97- guard let x509 = X509_new ( ) else { throw CertGenError . x509CreationFailed ( " X509_new failed " ) }
111+ guard let x509 = X509_new ( ) else {
112+ throw CertGenError . x509CreationFailed ( " X509_new failed " )
113+ }
98114
99115 X509_set_version ( x509, 2 )
100116
@@ -179,12 +195,15 @@ public final class GenerateCert {
179195
180196 return x509
181197 }
198+
182199 private static func createCertificateSignedByCA( serverPKey: OpaquePointer ? ,
183200 caPkey: OpaquePointer ? ,
184201 caX509: OpaquePointer ? ,
185202 commonName: String ,
186203 days: Int32 ) throws -> OpaquePointer ? {
187- guard let cert = X509_new ( ) else { throw CertGenError . x509CreationFailed ( " X509_new failed " ) }
204+ guard let cert = X509_new ( ) else {
205+ throw CertGenError . x509CreationFailed ( " X509_new failed " )
206+ }
188207
189208 X509_set_version ( cert, 2 )
190209
@@ -272,6 +291,15 @@ public final class GenerateCert {
272291 }
273292 }
274293
294+ // Also add extended key usage for server authentication
295+ if let ext_eku = X509V3_EXT_conf_nid ( nil , nil , NID_ext_key_usage, " serverAuth " ) {
296+ defer { X509_EXTENSION_free ( ext_eku) }
297+ if X509_add_ext ( cert, ext_eku, - 1 ) != 1 {
298+ X509_free ( cert)
299+ throw CertGenError . x509CreationFailed ( " X509_add_ext for ext_key_usage failed " )
300+ }
301+ }
302+
275303 guard let caKey = caPkey else {
276304 X509_free ( cert)
277305 throw CertGenError . x509CreationFailed ( " CA private key missing " )
@@ -284,15 +312,20 @@ public final class GenerateCert {
284312
285313 return cert
286314 }
287- @discardableResult private static func addNameEntry( name: OpaquePointer ? , field: String , value: String ) -> Int32 {
315+
316+ @discardableResult
317+ private static func addNameEntry( name: OpaquePointer ? , field: String , value: String ) -> Int32 {
288318 guard let name = name else { return 0 }
289319 return value. withCString { valuePtr in
290320 return X509_NAME_add_entry_by_txt ( name, field, MBSTRING_ASC, valuePtr, - 1 , - 1 , 0 )
291321 }
292322 }
323+
293324 // Simpler version that doesn't use deprecated stack functions
294325 private static func addSubjectAltName_IP_simple( cert: OpaquePointer ? , ipString: String ) throws {
295- guard let cert = cert else { throw CertGenError . sanCreationFailed ( " cert nil " ) }
326+ guard let cert = cert else {
327+ throw CertGenError . sanCreationFailed ( " cert nil " )
328+ }
296329
297330 // Create SAN string in format "IP:127.0.0.1"
298331 let sanString = " IP: \( ipString) "
@@ -307,28 +340,44 @@ public final class GenerateCert {
307340 throw CertGenError . sanCreationFailed ( " X509_add_ext failed for SAN " )
308341 }
309342 }
343+
310344 private static func writePrivateKeyPEM( pkey: OpaquePointer ? , to path: String ) throws {
311- guard let pkey = pkey else { throw CertGenError . writeFailed ( " pkey nil " ) }
312- guard let bio = BIO_new_file ( path, " w " ) else { throw CertGenError . writeFailed ( " BIO_new_file failed for \( path) " ) }
345+ guard let pkey = pkey else {
346+ throw CertGenError . writeFailed ( " pkey nil " )
347+ }
348+ guard let bio = BIO_new_file ( path, " w " ) else {
349+ throw CertGenError . writeFailed ( " BIO_new_file failed for \( path) " )
350+ }
313351 defer { BIO_free_all ( bio) }
314352
315353 if PEM_write_bio_PrivateKey ( bio, pkey, nil , nil , 0 , nil , nil ) != 1 {
316354 throw CertGenError . writeFailed ( " PEM_write_bio_PrivateKey failed for \( path) " )
317355 }
318356 }
357+
319358 private static func writeX509PEM( x509: OpaquePointer ? , to path: String ) throws {
320- guard let x509 = x509 else { throw CertGenError . writeFailed ( " x509 nil " ) }
321- guard let bio = BIO_new_file ( path, " w " ) else { throw CertGenError . writeFailed ( " BIO_new_file failed for \( path) " ) }
359+ guard let x509 = x509 else {
360+ throw CertGenError . writeFailed ( " x509 nil " )
361+ }
362+ guard let bio = BIO_new_file ( path, " w " ) else {
363+ throw CertGenError . writeFailed ( " BIO_new_file failed for \( path) " )
364+ }
322365 defer { BIO_free_all ( bio) }
323366
324367 if PEM_write_bio_X509 ( bio, x509) != 1 {
325368 throw CertGenError . writeFailed ( " PEM_write_bio_X509 failed for \( path) " )
326369 }
327370 }
371+
328372 private static func writePKCS12( pkey: OpaquePointer ? , cert: OpaquePointer ? , caCert: OpaquePointer ? , to path: String , password: String ? ) throws {
329- guard let pkey = pkey, let cert = cert else { throw CertGenError . writeFailed ( " pkey or cert nil " ) }
373+ guard let pkey = pkey, let cert = cert else {
374+ throw CertGenError . writeFailed ( " pkey or cert nil " )
375+ }
376+
377+ // Always use an empty string if password is nil
378+ let passString = password ?? " "
379+ let pass : UnsafePointer < CChar > ? = passString. utf8CString. withUnsafeBufferPointer { $0. baseAddress }
330380
331- let pass : UnsafePointer < CChar > ? = password? . utf8CString. withUnsafeBufferPointer { $0. baseAddress } ?? nil
332381 let friendlyName = " localhost "
333382 let name : UnsafePointer < CChar > ? = friendlyName. utf8CString. withUnsafeBufferPointer { $0. baseAddress }
334383
@@ -351,11 +400,13 @@ public final class GenerateCert {
351400 }
352401 defer { PKCS12_free ( p12) }
353402
354- guard let bio = BIO_new_file ( path, " w " ) else { throw CertGenError . writeFailed ( " BIO_new_file failed for \( path) " ) }
403+ guard let bio = BIO_new_file ( path, " wb " ) else {
404+ throw CertGenError . writeFailed ( " BIO_new_file failed for \( path) " )
405+ }
355406 defer { BIO_free_all ( bio) }
356407
357408 if i2d_PKCS12_bio ( bio, p12) != 1 {
358409 throw CertGenError . writeFailed ( " i2d_PKCS12_bio failed for \( path) " )
359410 }
360411 }
361- }
412+ }
0 commit comments