Newer
Older
return string.charAt(0).toUpperCase() + string.slice(1);
}
function capitalizeHeader(string) {
let result = "";
const tokens = string.split("-");
for (let i = 0; i < tokens.length; i++) {
result += capitalizeFirstLetter(tokens[i]);
if (i !== tokens.length - 1) {
result += "-";
}
}
return result;
}
function makeBoundary() {
let len = 20 + Math.random() * 20;
function hexString(buffer) { //TODO remove
const byteArray = new Uint8Array(buffer);
const hexCodes = [...byteArray].map(value => {
const hexCode = value.toString(16);
const paddedHexCode = hexCode.padStart(2, '0');
return paddedHexCode;
});
return hexCodes.join('');
}
export const parseSignedData = signatureBase64 => {
try {
const certificateDecoded = atob(signatureBase64);
const buffer = stringToArrayBuffer(certificateDecoded);
const asn1 = fromBER(buffer);
const contentInfo = new ContentInfo({ schema: asn1.result });
const signedData = new SignedData({ schema: contentInfo.content });
return signedData;
} catch (e) {
console.error("Error parsing signed data", e);
return null; //TODO implement proper error handling
}
};
export const parseCertificates = signedData => {
try {
//TODO remove block
console.log("ParseCerts", signedData.signerInfos);
console.log("DigestAttrib",signedData.signerInfos[0].signedAttrs.attributes[2]);
const digest = signedData.signerInfos[0].signedAttrs.attributes[2].values[0].valueBlock.valueHex;
const digestStr = hexString(digest);
console.log("DigestAttrib", digestStr);
return signedData.certificates.map((certificate, index) => {
const certificateData = { issuer: {}, subject: {}, validity: {} };
const serialNumber = bufferToHexCodes(
certificate.serialNumber.valueBlock.valueHex
);
const issuer = certificate.issuer.typesAndValues;
const subject = certificate.subject.typesAndValues;
const notAfter = certificate.notAfter.value;
const notBefore = certificate.notBefore.value;
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
let signatureAlgorithm =
algomap[certificate.signatureAlgorithm.algorithmId];
if (typeof signatureAlgorithm === "undefined") {
signatureAlgorithm = certificate.signatureAlgorithm.algorithmId;
} else {
signatureAlgorithm = `${signatureAlgorithm}`;
}
for (const typeAndValue of issuer) {
let typeVal = rdnmap[typeAndValue.type];
if (typeof typeVal === "undefined") {
typeVal = typeAndValue.type;
}
const subjVal = typeAndValue.value.valueBlock.value;
certificateData.issuer[typeVal] = subjVal;
}
for (const typeAndValue of subject) {
let typeVal = rdnmap[typeAndValue.type];
if (typeof typeVal === "undefined") {
typeVal = typeAndValue.type;
}
const subjVal = typeAndValue.value.valueBlock.value;
certificateData.subject[typeVal] = subjVal;
}
certificateData.signatureAlgorithm = signatureAlgorithm;
certificateData.serialNumber = serialNumber;
certificateData.validity = {
notAfter,
notBefore
};
return certificateData;
});
} catch (e) {
console.error("Error parsing certificate", e);
}
};
export const getCertificateChain = signedData => {
const certificateChain = [];
try {
const certificates = parseCertificates(signedData);
// Add first certificate in the chain
certificateChain.push(certificates[0]);
// Go through all certificates to build a chain from first certificate to the root
certificates.forEach(certificate => {
if (
certificateChain[0].issuer.commonName === certificate.subject.commonName
) {
certificateChain.unshift(certificate);
}
});
} catch (e) {
console.warn("Error getting certificate data", e);
}
return certificateChain;
};
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
export const verifySMIME = (smimeString, rootCaPem) => {
return new Promise(resolve => {
setTimeout(async () => {
const emailString = fixNewLines(smimeString);
const parts = parseMIME(emailString);
//const rawAttachments = getAttachments(emailString, parts);
//const attachments = [];
let signatureBase64;
let signatureBoundary;
for (const part of parts) {
let contentType = getHeaderValue("content-type", part);
if (contentType === null || contentType === undefined) {
continue;
}
contentType = contentType[0];
if (contentType.startsWith(SIGNATURE_CONTENT_TYPE)) {
signatureBase64 = getAttachment(emailString, part).base64;
signatureBoundary = part.boundary;
break;
}
}
let verificationResult = false;
let signedData;
if (signatureBase64) {
const dataPart = parts[0];
if (dataPart.boundary !== signatureBoundary) {
throw new Error(`Invalid SMIME format: wrong boundary on first MIME part`);
}
const data = emailString.slice(
dataPart.indices.from,
dataPart.indices.to
);
const dataBuffer = stringToArrayBuffer(data);
const rootCa = parseCertificate(rootCaPem);
signedData = parseSignedData(signatureBase64);
for (let i = 0; i < signedData.signerInfos.length; i++) {
console.log(`Validating message for signer ${i}`);
const result = await signedData.verify({
signer: i,
data: dataBuffer,
trustedCerts: [rootCa],
checkDate: new Date(),
checkChain: true,
extendedMode: true,
passedWhenNotRevValues: false
});
console.log(`Result for signer ${i}:`,result);
if (!result) {
verificationResult = false;
resolve(verificationResult);
return;
}
}
}
verificationResult = true;
resolve(verificationResult);
}, 50);
});
};