summaryrefslogtreecommitdiff
path: root/scripts/merge-certs-info.py
blob: c6b105bfe5ee18cdbba24ec8f8ebcde1dc0312bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
####################################################
#
# Copyright (c) 2018 - 2024 Samsung Electronics Co., Ltd All Rights Reserved
#
#    Licensed under the Apache License, Version 2.0 (the "License");
#    you may not use this file except in compliance with the License.
#    You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.
#
#
#   author: Tomasz Swierczek
#   email : t.swierczek@samsung.com
#   description : Generates, on stdout, tab-separated data on certificates from different sources,
#                 possibly outputting merged list to specified dir;
#                 the output dir has to exist and preferably, be empty;
#                 certs are stored with filename being openssl-generated subject hash;
#                 script can remove expired certs from output dir if option is used.
#   usage: gen-certs-info.py <certs directory path1> ... <certs directory pathN> [--merged=<output merged certs dir>] [--remove-expired]
#
#
####################################################
import subprocess
import sys
import os
import os.path
from time import strptime
from datetime import datetime

####################################################
#   Executes command and returns list of output lines
####################################################
def consoleCommand(cmd):
    process = None
    process = subprocess.Popen(cmd, shell=True, bufsize=-1, stdout=subprocess.PIPE)
    lines = process.stdout.readlines()
    retcode = process.wait()
    if retcode != 0:
        return []
    ret = []
    for l in lines:
        ret.append(l.decode())
    return ret

####################################################
#   Returns certificate start date or empty string on error
####################################################
def getStartDate(path):
    output = consoleCommand("openssl x509 -in " + path + " -startdate -noout | sed -e 's/^notBefore=//g' | sed -e 's/[0-9][0-9]:[0-9][0-9]:[0-9][0-9] //g' | sed -e 's/ GMT$//g'")
    if len(output) > 0:
        return output[0].strip()
    return ""

####################################################
#   Returns certificate end date or empty string on error
####################################################
def getEndDate(path):
    output = consoleCommand("openssl x509 -in " + path + " -enddate -noout | sed -e 's/^notAfter=//g' | sed -e 's/[0-9][0-9]:[0-9][0-9]:[0-9][0-9] //g' | sed -e 's/ GMT$//g'")
    if len(output) > 0:
        return output[0].strip()
    return ""

####################################################
#  Returns true if certificate under path is not expired
####################################################
def isCertValid(path):
    endDate = datetime.strptime(getEndDate(path), "%b %d %Y")
    return endDate > datetime.now()

####################################################
#   Returns sha1 fingerprint of DER or PEM cert, empty string on error (ie. file is not a certificate)
####################################################
def calculateCertSHA1(path):
    output = consoleCommand("openssl x509 -noout -fingerprint -sha1 -inform pem -in " + path + " | sed -e 's/Fingerprint=//g'")
    if len(output) == 0:
        # oops it may be DER-encoded, lets try out
        output = consoleCommand("openssl x509 -noout -fingerprint -sha1 -inform der -in " + path + " | sed -e 's/Fingerprint=//g'")
    if len(output) == 0:
        return ""
    return output[0].strip()

####################################################
#   Returns sha256 fingerprint of DER or PEM cert, empty string on error
####################################################
def calculateCertSHA256(path):
    output = consoleCommand("openssl x509 -noout -fingerprint -sha256 -inform pem -in " + path + " | sed -e 's/Fingerprint=//g'")
    if len(output) == 0:
        # oops it may be DER-encoded, lets try out
        output = consoleCommand("openssl x509 -noout -fingerprint -sha256 -inform der -in " + path + " | sed -e 's/Fingerprint=//g'")
    if len(output) == 0:
        return ""
    return output[0].strip()


####################################################
#   Returns certificate subject or emtpy string on error
####################################################
def getSubject(path):
    output = consoleCommand("openssl x509 -in " + path + " -subject -noout | sed -e 's/^subject= \///'")
    if len(output) > 0:
        return output[0].strip()
    return ""

####################################################
#   Returns certificate issuer or emtpy string on error
####################################################
def getIssuer(path):
    output = consoleCommand("openssl x509 -in " + path + " -issuer -noout | sed -e 's/^issuer= \///'")
    if len(output) > 0:
        return output[0].strip()
    return ""


####################################################
# Main
####################################################
if len(sys.argv) == 1:
    print ("Usage: merge-certs-info.py <certs directory path1> ... <certs directory pathN> [--merged=<output merged certs dir>] [--remove-expired]")
    sys.exit(1)

removeExpired = False
outputDir = ""
directories = []

for i in range(1, len(sys.argv)):
    arg = sys.argv[i]
    if arg.find("--merged=") == 0:
        outputDir = arg.split("=")[1].strip()
    elif arg.strip() == "--remove-expired":
        removeExpired = True
    else:
        directories.append(arg.strip());

allSha1ToAnyPath = {}
allSha1ToNewName = {}
dirToSha1ToName = {}

for directory in directories:
    # for each directory, find certificates, calc. their SHA1 hash and store a map
    # then, output all details in csv-like format
    sha1ToName = {}
    files = consoleCommand("ls -1 " + directory)
    for f in files:
        f = f.strip()
        path = directory + "/" + f
        sha1 = calculateCertSHA1(path)
        if len(sha1) > 0:
            sha1ToName[sha1] = f;
            allSha1ToAnyPath[sha1] = path
    dirToSha1ToName[directory] = sha1ToName;

if outputDir != "":
    for sha1 in allSha1ToAnyPath:
        path = allSha1ToAnyPath[sha1]
        newName = consoleCommand("openssl x509 -in " + path + "  -subject_hash -noout")[0].strip()
        i = 0
        while os.path.exists(outputDir + "/" + newName + "." + str(i)):
            i = i + 1
        newName = newName + "." + str(i)
        if not removeExpired or isCertValid(path):
            consoleCommand("cp " + path + " " + outputDir + "/" + newName)
        allSha1ToNewName[sha1] = newName

line = ""
for directory in dirToSha1ToName:
    line = line + directory + "\t"
if outputDir != "":
    line = line + outputDir + "\t"
line = line + "SUBJECT\tISSUER\tFINGERPRINT\tFINGERPRINT SHA256\tSTART DATE\tEND DATE"
print (line)

for sha1 in allSha1ToAnyPath:
    line = ""
    for directory in dirToSha1ToName:
        if sha1 in dirToSha1ToName[directory]:
            line = line + dirToSha1ToName[directory][sha1] + "\t"
        else:
            line = line + "----------\t"
    if outputDir != "":
        line = line + allSha1ToNewName[sha1] + "\t"
    subject = getSubject(allSha1ToAnyPath[sha1])
    issuer = getIssuer(allSha1ToAnyPath[sha1])
    sha256 = calculateCertSHA256(allSha1ToAnyPath[sha1])
    startDate = getStartDate(allSha1ToAnyPath[sha1])
    endDate = getEndDate(allSha1ToAnyPath[sha1])
    line = line + subject + "\t" + issuer + "\t" + sha1 + "\t" + sha256 + "\t" + startDate + "\t" + endDate
    print (line)