Skip to content

Commit b19a272

Browse files
committed
Fix OpenSSL data parsing
OpenSSL data parsing could be confused when parsing certificates which have Country/Org and other parameters in the subject line. This is fixed by writing a more robust parser of the output lines, and using that to do parsing which now correctly handles this case.
1 parent 2dd4434 commit b19a272

File tree

1 file changed

+29
-8
lines changed

1 file changed

+29
-8
lines changed

backend/internal/certificate.js

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,28 @@ const internalCertificate = {
726726
});
727727
},
728728

729+
/**
730+
* Parse the X509 subject line as returned by the OpenSSL command when
731+
* invoked with openssl x509 -in <certificate name> -subject -noout
732+
*
733+
* @param {String} line emitted from the openssl command
734+
* @param {String} prefix expected to be removed
735+
* @return {Object} object containing the parsed fields from the subject line
736+
*/
737+
parseX509Output: (line, prefix) => {
738+
// Remove the subject= part
739+
const subject_value = line.slice(prefix.length);
740+
741+
const subject = subject_value.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/)
742+
.map( (e) => { return e.trim().split(' = ', 2); })
743+
.reduce((obj, [key, value]) => {
744+
obj[key] = value.replace(/^"/, '').replace(/"$/, '');
745+
return obj;
746+
}, {});
747+
748+
return subject;
749+
},
750+
729751
/**
730752
* Uses the openssl command to both validate and get info out of the certificate.
731753
* It will save the file to disk first, then run commands on it, then delete the file.
@@ -739,28 +761,27 @@ const internalCertificate = {
739761
return utils.exec('openssl x509 -in ' + certificate_file + ' -subject -noout')
740762
.then((result) => {
741763
// subject=CN = something.example.com
742-
const regex = /(?:subject=)?[^=]+=\s+(\S+)/gim;
743-
const match = regex.exec(result);
764+
// subject=C = NoCountry, O = NoOrg, OU = NoOrgUnit, CN = Some Value With Spaces
765+
const subjectParams = internalCertificate.parseX509Output(result, 'subject=');
744766

745-
if (typeof match[1] === 'undefined') {
767+
if (typeof subjectParams.CN === 'undefined') {
746768
throw new error.ValidationError('Could not determine subject from certificate: ' + result);
747769
}
748770

749-
certData['cn'] = match[1];
771+
certData['cn'] = subjectParams.CN;
750772
})
751773
.then(() => {
752774
return utils.exec('openssl x509 -in ' + certificate_file + ' -issuer -noout');
753775
})
754776
.then((result) => {
755777
// issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
756-
const regex = /^(?:issuer=)?(.*)$/gim;
757-
const match = regex.exec(result);
778+
const issuerParams = internalCertificate.parseX509Output(result, 'issuer=');
758779

759-
if (typeof match[1] === 'undefined') {
780+
if (typeof issuerParams.CN === 'undefined') {
760781
throw new error.ValidationError('Could not determine issuer from certificate: ' + result);
761782
}
762783

763-
certData['issuer'] = match[1];
784+
certData['issuer'] = issuerParams.CN;
764785
})
765786
.then(() => {
766787
return utils.exec('openssl x509 -in ' + certificate_file + ' -dates -noout');

0 commit comments

Comments
 (0)