Windows: official and custom Certification Authorities in Git, PHP, ...
Introduction
Using certificates in POSIX systems is usually handled by the system.
For instance, in Debian/Ubuntu you save your certificate with a .cer
extension in the /usr/local/share/ca-certificates
directory, and run
sudo dpkg-reconfigure ca-certificates
or
sudo update-ca-certificates
On Windows, you can use certmgr.msc
(for user-level certificates) or certlm.msc
(for system-level certificates), but many tools (like GIT, PHP, …) won’t read these certificates.
A solution could be to use the Mozilla CA certificate store, append to it your custom CA certificates, and configure your tools to use that file.
You can do it by hand, but it’s better to keep the official list of CA up to date on a regular basis, so let’s automate this process.
The build script
Save the following script to a location of your choice (for instance, C:\Dev\ssl\cacert.vbs
):
Option Explicit
On Error Goto 0
Dim showHelp, argIndex
If WScript.Arguments.Count < 1 Then
showHelp = True
Else
showHelp = False
For argIndex = 0 To WScript.Arguments.Count - 1
If StrComp(WScript.Arguments(argIndex), "-h", 1) = 0 Or StrComp(WScript.Arguments(argIndex), "--help", 1) = 0 Or StrComp(WScript.Arguments(argIndex), "/?", 0) = 0 Then
showHelp = True
Exit For
End If
Next
End If
If showHelp Then
WScript.StdOut.WriteLine "Syntax: cscript //NoLogo " & WScript.ScriptName & " <output-file> [<additional-cert>...]"
WScript.StdOut.WriteLine "Where:"
WScript.StdOut.WriteLine " <output-file>: where to save the certificate store"
WScript.StdOut.WriteLine " <additional-cert>: one or more additional custom certificates to be appended."
WScript.Quit 0
End If
WScript.StdOut.Write "Initializing... "
Randomize Time
Dim fso
Set fso = WScript.CreateObject("Scripting.FileSystemObject")
Dim tempFileName
tempFileName = GetTempFileName()
Dim finalFileName
finalFileName = WScript.Arguments(0)
Dim customList
Set customList = New AdditionalList
WScript.StdOut.WriteLine "done."
If WScript.Arguments.Count > 1 Then
WScript.StdOut.Write "Reading custom certificates... "
For argIndex = 1 To WScript.Arguments.Count - 1
customList.Add WScript.Arguments(argIndex)
Next
WScript.StdOut.WriteLine "done."
End If
WScript.StdOut.Write "Downloading cacert.pem... "
DownloadCACert tempFileName
WScript.StdOut.WriteLine "done."
If customList.Count > 0 Then
WScript.StdOut.Write "Appending custom certificates... "
customList.AppendTo tempFileName
WScript.StdOut.WriteLine "done."
End If
WScript.StdOut.Write "Saving final file... "
If fso.FileExists(finalFileName) Then
fso.DeleteFile finalFileName, True
End If
fso.MoveFile tempFileName, finalFileName
WScript.StdOut.WriteLine "done."
Function GetTempFileName()
Dim tempFolder
Set tempFolder = fso.GetSpecialFolder(2) '2: TemporaryFolder
GetTempFileName = tempFolder.Path & "\" & "cacert-" & Replace(CStr(Rnd()), ",", ".") & ".tmp"
End Function
Sub DownloadCACert(ByVal saveAs)
Dim http
Set http = WScript.CreateObject("WinHttp.WinHttpRequest.5.1")
http.Open "GET", "https://curl.haxx.se/ca/cacert.pem", False
http.Send
If http.Status <> 200 Then
WScript.StdErr.WriteLine http.Status & " (" & http.StatusText & ")"
WScript.Quit 1
End If
Dim outStream
Set outStream = WScript.CreateObject("ADODB.Stream")
outStream.Type = 1 ' 1: adTypeBinary
outStream.Open
outStream.Write http.ResponseBody
outStream.SaveToFile tempFileName, 2 ' 2: adSaveCreateOverWrite
outStream.Close
Set outStream = Nothing
Set http = Nothing
End Sub
Class AdditionalList
Private myStreams()
Private myNames()
Private myCount
Private Sub Class_Initialize()
myCount = 0
End Sub
Private Sub Class_Terminate()
Dim i
For i = 0 To MyCount - 1
myStreams(i).Close
Set myStreams(i) = Nothing
Next
myCount = 0
End Sub
Public Sub Add(ByVal item)
If fso.FileExists(item) Then
Me.AddFile item
ElseIf fso.FolderExists(item) Then
Me.AddFolder item
Else
WScript.StdErr.WriteLine "Unable to find the file/folder " & what
WScript.Quit 1
End If
End Sub
Public Sub AddFile(ByVal item)
Dim stream
Set stream = fso.GetFile(item).OpenAsTextStream(1, 0) ' 1: ForReading, 0: TristateFalse (Opens the file as ASCII)
Dim name
name = fso.GetFileName(item)
If myCount = 0 Then
ReDim myStreams(0)
ReDim myNames(0)
Else
ReDim Preserve myStreams(myCount)
ReDim Preserve myNames(myCount)
End If
Set myStreams(myCount) = stream
myNames(myCount) = name
myCount = myCount + 1
End Sub
Public Sub AddFolder(ByVal item)
Dim folder
Set folder = fso.GetFolder(item)
Dim subFile
For Each subFile In folder.Files
Me.AddFile subFile.Path
Next
Dim subFolder
For Each subFolder In folder.SubFolders
Me.AddFolder subFolder.Path
Next
End Sub
Public Sub AppendTo(ByVal fileName)
Dim stream
Set stream = fso.GetFile(fileName).OpenAsTextStream(8, 0) ' 8: ForAppending, 0: TristateFalse (Opens the file as ASCII)
Dim i
For i = 0 To myCount - 1
stream.Write "" & vbLf
stream.Write myNames(i) & vbLf
stream.Write String(Len(myNames(i)), "=") & vbLf
Do While myStreams(i).AtEndOfStream <> True
stream.Write myStreams(i).ReadLine & vbLf
Loop
Next
stream.Close
Set stream = Nothing
End Sub
Public Property Get Count
Count = myCount
End Property
End Class
Using the build script
Let’s assume that you have your custom CA certificate saved as C:\Dev\ssl\my-ca.crt
.
In order to create a file that contains both the official CA certificates and your custom CA certificates, you can for instance use this command:
CScript.exe //NoLogo "C:\Dev\ssl\cacert.vbs" "C:\Dev\ssl\cacert.pem" "C:\Dev\ssl\my-ca.crt"
The above command will create the C:\Dev\ssl\cacert.pem
file, containing the official CA certificates and your custom C:\Dev\ssl\my-ca.crt
certificate.
You can add as many custom CA certificate files as you want, and you can also specify a directory containing your custom certificates.
Of course, if you only specify one argument, the final file won’t contain any custom certificate, only the official ones.
You can schedule the execution of this script using the Windows Scheduler (Windows
+ R
-> taskschd.msc
):
- Action: Start a program
- Program/script:
C:\Windows\System32\cscript.exe
- Arguments:
//NoLogo "C:\Dev\ssl\cacert.vbs" "C:\Dev\ssl\cacert.pem" "C:\Dev\ssl\my-ca.crt"
Configuring PHP
Open your php.ini
file (type php.exe --ini
to determine its path), and add these lines:
curl.cainfo=C:\Dev\ssl\cacert.pem
openssl.cafile=C:\Dev\ssl\cacert.pem
Configuring Git
Open a command prompt and type:
git config --global http.sslCAInfo C:\Dev\ssl\cacert.pem