diff --git a/ChangeLog.txt b/ChangeLog.txt index 2bed4d75..4ee1a9ee 100755 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,6 +1,27 @@ ChangeLog for jsrsasign +support ISO 8859-1 TeletexString and BMPString for X500Name +* Changes from 10.5.11 to 10.5.12 (2022-Mar-13) + - src/asn1hex.js + - ASN1HEX.parse fixed for TeletexString and BMPString + - ASN1HEX.parse TeletexString supports non-ASCII + ISO 8859-1 Latin1 characters. Before this version, + only supports ASCII characters. + - hextoipv6 bug fix raised in some of enviroment + - src/base64x.js + - iso88591hextoutf8/utf8toiso88591hex added + - iso88591hextoutf8hex/utf8hextoiso88591hex added + - hextoipv6 fixed + - src/x509.js + - refactoring for X509.get{X500NameArray,RDN,AttrTypeAndValue}. + Add support for Teletex/BMPString and more attrTypes + - test/qunit-do-*.html + - test case added for above updates. + - qunit-do-base64x: add iso8859-1 / utf-8 converter tests + - qunit-do-asn1hex-parse: add TeletexString parse tests + - qunit-do-x509-ext: add NumericString/TeletexString X500Name tests + asn1hex update * Changes from 10.5.10 to 10.5.11 (2022-Mar-12) - src/ash1hex.js diff --git a/api/files.html b/api/files.html index 18515e27..d2c8fe07 100644 --- a/api/files.html +++ b/api/files.html @@ -601,7 +601,7 @@
ASN1HEX.parse("31193017...") → // RDN +{set: [{seq: [{oid: "localityName"}, {utf8str: {str: "Test"}}] }]}diff --git a/api/symbols/X509.html b/api/symbols/X509.html index c7349eca..b96fb68d 100644 --- a/api/symbols/X509.html +++ b/api/symbols/X509.html @@ -1937,7 +1937,9 @@
iso88591hextoutf8("41a9fa") → "A©ú"+ + + + +
iso88591hextoutf8hex("41a9fa") → "41c2a9c3ba"+ + + + +
utf8hextoiso88591hex("41c2a9c3ba") → "41a9fa"+ + + + +
utf8toiso88591hex("A©ú") → "41a9fa"+ + + + +
1 /* asn1hex-1.2.12.js (c) 2012-2022 Kenji Urushima | kjur.github.io/jsrsasign/license +1 /* asn1hex-1.2.13.js (c) 2012-2022 Kenji Urushima | kjur.github.io/jsrsasign/license 2 */ 3 /* 4 * asn1hex.js - Hexadecimal represented ASN.1 string library @@ -23,7 +23,7 @@ 16 * @fileOverview 17 * @name asn1hex-1.1.js 18 * @author Kenji Urushima kenji.urushima@gmail.com - 19 * @version jsrsasign 10.5.11 asn1hex 1.2.12 (2022-Mar-12) + 19 * @version jsrsasign 10.5.12 asn1hex 1.2.13 (2022-Mar-13) 20 * @license <a href="https://kjur.github.io/jsrsasign/license/">MIT License</a> 21 */ 22 @@ -1021,337 +1021,349 @@ 1014 * 1015 * @description 1016 * This method parses ASN.1 DER hexadecimal string. -1017 * Its result can be applied to KJUR.asn1.ASN1Util.newOjbect. +1017 * Its result can be applied to {@link KJUR.asn1.ASN1Util.newOjbect}. 1018 * 1019 * @example -1020 */ -1021 ASN1HEX.parse = function(h) { -1022 var _ASN1HEX = ASN1HEX, -1023 _parse = _ASN1HEX.parse, -1024 _isASN1HEX = _ASN1HEX.isASN1HEX, -1025 _getV = _ASN1HEX.getV, -1026 _getTLV = _ASN1HEX.getTLV, -1027 _getChildIdx = _ASN1HEX.getChildIdx, -1028 _KJUR_asn1 = KJUR.asn1, -1029 _oidHexToInt = _KJUR_asn1.ASN1Util.oidHexToInt, -1030 _oid2name = _KJUR_asn1.x509.OID.oid2name, -1031 _hextoutf8 = hextoutf8; -1032 -1033 var tagName = { -1034 "0c": "utf8str", "12": "numstr", "13": "prnstr", -1035 "14": "telstr", "16": "ia5str", "17": "utctime", -1036 "18": "gentime", "1a": "visstr", "1e": "bmpstr", -1037 "30": "seq", "31": "set" -1038 }; -1039 -1040 var _parseChild = function(h) { -1041 var result = []; -1042 var aIdx = _getChildIdx(h, 0); -1043 for (var i = 0; i < aIdx.length; i++) { -1044 var idx = aIdx[i]; -1045 var hTLV = _getTLV(h, idx); -1046 var pItem = _parse(hTLV); -1047 result.push(pItem); -1048 } -1049 return result; -1050 }; -1051 -1052 var tag = h.substr(0, 2); -1053 var result = {}; -1054 var hV = _getV(h, 0); -1055 if (tag == "01") { -1056 if (h == "0101ff") return {bool: true}; -1057 return {bool: false}; -1058 } else if (tag == "02") { -1059 return {"int": {hex: hV}}; -1060 } else if (tag == "03") { -1061 try { -1062 if (hV.substr(0, 2) != "00") throw "not encap"; -1063 var hV1 = hV.substr(2); -1064 if (! _isASN1HEX(hV1)) throw "not encap"; -1065 return {bitstr: {obj: _parse(hV1)}}; -1066 } catch(ex) { -1067 var bV = null; -1068 if (hV.length <= 6) bV = bitstrtobinstr(hV); -1069 if (bV == null) { -1070 return {bitstr: {hex: hV}}; -1071 } else { -1072 return {bitstr: {bin: bV}}; -1073 } -1074 } -1075 } else if (tag == "04") { -1076 try { -1077 if (! _isASN1HEX(hV)) throw "not encap"; -1078 return {octstr: {obj: _parse(hV)}}; -1079 } catch(ex) { -1080 return {octstr: {hex: hV}}; -1081 } -1082 } else if (tag == "05") { -1083 return {"null": ''}; -1084 } else if (tag == "06") { -1085 var oidDot = _oidHexToInt(hV); -1086 var oidName = _oid2name(oidDot); -1087 if (oidName == "") { -1088 return {oid: oidDot}; -1089 } else { -1090 return {oid: oidName}; -1091 } -1092 } else if (tag == "0a") { -1093 if (hV.length > 4) { -1094 return {"enum": {hex: hV}}; -1095 } else { -1096 return {"enum": parseInt(hV, 16)}; -1097 } -1098 } else if (tag == "30" || tag == "31") { -1099 result[tagName[tag]] = _parseChild(h); -1100 return result; -1101 } else if (":0c:12:13:14:16:17:18:1a:1e:".indexOf(tag) != -1) { -1102 var s = _hextoutf8(hV); -1103 result[tagName[tag]] = {str: s}; +1020 * ASN1HEX.parse("31193017...") → // RDN +1021 * {set: [{seq: [{oid: "localityName"}, {utf8str: {str: "Test"}}] }]} +1022 */ +1023 ASN1HEX.parse = function(h) { +1024 var _ASN1HEX = ASN1HEX, +1025 _parse = _ASN1HEX.parse, +1026 _isASN1HEX = _ASN1HEX.isASN1HEX, +1027 _getV = _ASN1HEX.getV, +1028 _getTLV = _ASN1HEX.getTLV, +1029 _getChildIdx = _ASN1HEX.getChildIdx, +1030 _KJUR_asn1 = KJUR.asn1, +1031 _oidHexToInt = _KJUR_asn1.ASN1Util.oidHexToInt, +1032 _oid2name = _KJUR_asn1.x509.OID.oid2name, +1033 _hextoutf8 = hextoutf8, +1034 _ucs2hextoutf8 = ucs2hextoutf8, +1035 _iso88591hextoutf8 = iso88591hextoutf8; +1036 +1037 var tagName = { +1038 "0c": "utf8str", "12": "numstr", "13": "prnstr", +1039 "14": "telstr", "16": "ia5str", "17": "utctime", +1040 "18": "gentime", "1a": "visstr", "1e": "bmpstr", +1041 "30": "seq", "31": "set" +1042 }; +1043 +1044 var _parseChild = function(h) { +1045 var result = []; +1046 var aIdx = _getChildIdx(h, 0); +1047 for (var i = 0; i < aIdx.length; i++) { +1048 var idx = aIdx[i]; +1049 var hTLV = _getTLV(h, idx); +1050 var pItem = _parse(hTLV); +1051 result.push(pItem); +1052 } +1053 return result; +1054 }; +1055 +1056 var tag = h.substr(0, 2); +1057 var result = {}; +1058 var hV = _getV(h, 0); +1059 if (tag == "01") { +1060 if (h == "0101ff") return {bool: true}; +1061 return {bool: false}; +1062 } else if (tag == "02") { +1063 return {"int": {hex: hV}}; +1064 } else if (tag == "03") { +1065 try { +1066 if (hV.substr(0, 2) != "00") throw "not encap"; +1067 var hV1 = hV.substr(2); +1068 if (! _isASN1HEX(hV1)) throw "not encap"; +1069 return {bitstr: {obj: _parse(hV1)}}; +1070 } catch(ex) { +1071 var bV = null; +1072 if (hV.length <= 6) bV = bitstrtobinstr(hV); +1073 if (bV == null) { +1074 return {bitstr: {hex: hV}}; +1075 } else { +1076 return {bitstr: {bin: bV}}; +1077 } +1078 } +1079 } else if (tag == "04") { +1080 try { +1081 if (! _isASN1HEX(hV)) throw "not encap"; +1082 return {octstr: {obj: _parse(hV)}}; +1083 } catch(ex) { +1084 return {octstr: {hex: hV}}; +1085 } +1086 } else if (tag == "05") { +1087 return {"null": ''}; +1088 } else if (tag == "06") { +1089 var oidDot = _oidHexToInt(hV); +1090 var oidName = _oid2name(oidDot); +1091 if (oidName == "") { +1092 return {oid: oidDot}; +1093 } else { +1094 return {oid: oidName}; +1095 } +1096 } else if (tag == "0a") { +1097 if (hV.length > 4) { +1098 return {"enum": {hex: hV}}; +1099 } else { +1100 return {"enum": parseInt(hV, 16)}; +1101 } +1102 } else if (tag == "30" || tag == "31") { +1103 result[tagName[tag]] = _parseChild(h); 1104 return result; -1105 } else if (tag.match(/^8[0-9]$/)) { -1106 var s = _hextoutf8(hV); -1107 if (s == null | s == "") { -1108 return {"tag": {"tag": tag, explicit: false, hex: hV}}; -1109 } else if (s.match(/[\x00-\x1F\x7F-\x9F]/) != null || -1110 s.match(/[\u0000-\u001F\u0080–\u009F]/) != null) { -1111 return {"tag": {"tag": tag, explicit: false, hex: hV}}; -1112 } else { -1113 return {"tag": {"tag": tag, explicit: false, str: s}}; -1114 } -1115 } else if (tag.match(/^a[0-9]$/)) { -1116 try { -1117 if (! _isASN1HEX(hV)) throw new Error("not encap"); -1118 return {"tag": {"tag": tag, -1119 explicit: true, -1120 obj: _parse(hV)}}; -1121 } catch(ex) { -1122 return {"tag": {"tag": tag, explicit: true, hex: hV}}; -1123 } -1124 } else { -1125 var d = new KJUR.asn1.ASN1Object(); -1126 d.hV = hV; -1127 var hL = d.getLengthHexFromValue(); -1128 return {"asn1": {"tlv": tag + hL + hV}}; -1129 } -1130 }; -1131 -1132 /** -1133 * check if a hexadecimal tag is a specified ASN.1 context specific tag -1134 * @name isContextTag -1135 * @memberOf ASN1HEX -1136 * @function -1137 * @param {hTag} hex string of a hexadecimal ASN.1 tag consists by two characters (e.x. "a0") -1138 * @param {sTag} context specific tag in string represention (OPTION) (e.x. "[0]") -1139 * @return {Boolean} true if hTag is a ASN.1 context specific tag specified by sTag value. -1140 * @since jsrsasign 8.0.21 asn1hex 1.2.2 -1141 * @description -1142 * This method checks if a hexadecimal tag is a specified ASN.1 context specific tag. -1143 * Structured and non-structured type of tag have the same string representation -1144 * of context specific tag. For example tag "a0" and "80" have the same string -1145 * representation "[0]". -1146 * The sTag has a range from from "[0]" to "[31]". -1147 * @example -1148 * ASN1HEX.isContextTag('a0', '[0]') → true // structured -1149 * ASN1HEX.isContextTag('a1', '[1]') → true // structured -1150 * ASN1HEX.isContextTag('a2', '[2]') → true // structured -1151 * ASN1HEX.isContextTag('80', '[0]') → true // non structured -1152 * ASN1HEX.isContextTag('81', '[1]') → true // non structured -1153 * ASN1HEX.isContextTag('82', '[2]') → true // non structured -1154 * ASN1HEX.isContextTag('a0', '[3]') → false -1155 * ASN1HEX.isContextTag('80', '[15]') → false -1156 * -1157 * ASN.1 tag bits -1158 * 12345679 -1159 * ++ tag class(universal:00, context specific:10) -1160 * + structured:1, primitive:0 -1161 * +++++ tag number (0 - 31) -1162 */ -1163 ASN1HEX.isContextTag = function(hTag, sTag) { -1164 hTag = hTag.toLowerCase(); -1165 var ihtag, istag; -1166 -1167 try { -1168 ihtag = parseInt(hTag, 16); -1169 } catch (ex) { -1170 return -1; -1171 } -1172 -1173 if (sTag === undefined) { -1174 if ((ihtag & 192) == 128) { -1175 return true; -1176 } else { -1177 return false; -1178 } -1179 } -1180 -1181 try { -1182 var result = sTag.match(/^\[[0-9]+\]$/); -1183 if (result == null) return false; -1184 istag = parseInt(sTag.substr(1,sTag.length - 1), 10); -1185 if (istag > 31) return false; -1186 if (((ihtag & 192) == 128) && // ihtag & b11000000 == b10000000 -1187 ((ihtag & 31) == istag)) { // ihtag & b00011111 == istag (0-31) -1188 return true; -1189 } -1190 return false; -1191 } catch (ex) { -1192 return false; -1193 } -1194 }; -1195 -1196 /** -1197 * simple ASN.1 DER hexadecimal string checker<br/> -1198 * @name isASN1HEX -1199 * @memberOf ASN1HEX -1200 * @function -1201 * @param {String} hex string to check whether it is hexadecmal string for ASN.1 DER or not -1202 * @return {Boolean} true if it is hexadecimal string of ASN.1 data otherwise false -1203 * @since jsrsasign 4.8.3 asn1hex 1.1.6 -1204 * @description -1205 * This method checks wheather the argument 'hex' is a hexadecimal string of -1206 * ASN.1 data or not. -1207 * @example -1208 * ASN1HEX.isASN1HEX('0203012345') → true // PROPER ASN.1 INTEGER -1209 * ASN1HEX.isASN1HEX('0203012345ff') → false // TOO LONG VALUE -1210 * ASN1HEX.isASN1HEX('02030123') → false // TOO SHORT VALUE -1211 * ASN1HEX.isASN1HEX('fa3bcd') → false // WRONG FOR ASN.1 -1212 */ -1213 ASN1HEX.isASN1HEX = function(hex) { -1214 var _ASN1HEX = ASN1HEX; -1215 if (hex.length % 2 == 1) return false; -1216 -1217 var intL = _ASN1HEX.getVblen(hex, 0); -1218 var hT = hex.substr(0, 2); -1219 var hL = _ASN1HEX.getL(hex, 0); -1220 var hVLength = hex.length - hT.length - hL.length; -1221 if (hVLength == intL * 2) return true; -1222 -1223 return false; -1224 }; -1225 -1226 /** -1227 * strict ASN.1 DER hexadecimal string checker -1228 * @name checkStrictDER -1229 * @memberOf ASN1HEX -1230 * @function -1231 * @param {String} hex string to check whether it is hexadecmal string for ASN.1 DER or not -1232 * @return unspecified -1233 * @since jsrsasign 8.0.19 asn1hex 1.2.1 -1234 * @throws Error when malformed ASN.1 DER hexadecimal string -1235 * @description -1236 * This method checks wheather the argument 'hex' is a hexadecimal string of -1237 * ASN.1 data or not. If the argument is not DER string, this -1238 * raise an exception. -1239 * @example -1240 * ASN1HEX.checkStrictDER('0203012345') → NO EXCEPTION FOR PROPER ASN.1 INTEGER -1241 * ASN1HEX.checkStrictDER('0203012345ff') → RAISE EXCEPTION FOR TOO LONG VALUE -1242 * ASN1HEX.checkStrictDER('02030123') → false RAISE EXCEPITON FOR TOO SHORT VALUE -1243 * ASN1HEX.checkStrictDER('fa3bcd') → false RAISE EXCEPTION FOR WRONG ASN.1 -1244 */ -1245 ASN1HEX.checkStrictDER = function(h, idx, maxHexLen, maxByteLen, maxLbyteLen) { -1246 var _ASN1HEX = ASN1HEX; -1247 -1248 if (maxHexLen === undefined) { -1249 // 1. hex string check -1250 if (typeof h != "string") throw new Error("not hex string"); -1251 h = h.toLowerCase(); -1252 if (! KJUR.lang.String.isHex(h)) throw new Error("not hex string"); -1253 -1254 // 2. set max if needed -1255 // max length of hexadecimal string -1256 maxHexLen = h.length; -1257 // max length of octets -1258 maxByteLen = h.length / 2; -1259 // max length of L octets of TLV -1260 if (maxByteLen < 0x80) { -1261 maxLbyteLen = 1; -1262 } else { -1263 maxLbyteLen = Math.ceil(maxByteLen.toString(16)) + 1; -1264 } -1265 } -1266 //console.log(maxHexLen + ":" + maxByteLen + ":" + maxLbyteLen); -1267 -1268 // 3. check if L(length) string not exceeds maxLbyteLen -1269 var hL = _ASN1HEX.getL(h, idx); -1270 if (hL.length > maxLbyteLen * 2) -1271 throw new Error("L of TLV too long: idx=" + idx); -1272 -1273 // 4. check if V(value) octet length (i.e. L(length) value) -1274 // not exceeds maxByteLen -1275 var vblen = _ASN1HEX.getVblen(h, idx); -1276 if (vblen > maxByteLen) -1277 throw new Error("value of L too long than hex: idx=" + idx); -1278 -1279 // 5. check V string length and L's value are the same -1280 var hTLV = _ASN1HEX.getTLV(h, idx); -1281 var hVLength = -1282 hTLV.length - 2 - _ASN1HEX.getL(h, idx).length; -1283 if (hVLength !== (vblen * 2)) -1284 throw new Error("V string length and L's value not the same:" + -1285 hVLength + "/" + (vblen * 2)); -1286 -1287 // 6. check appending garbled string -1288 if (idx === 0) { -1289 if (h.length != hTLV.length) -1290 throw new Error("total length and TLV length unmatch:" + -1291 h.length + "!=" + hTLV.length); -1292 } -1293 -1294 // 7. check if there isn't prepending zeros in DER INTEGER value -1295 var hT = h.substr(idx, 2); -1296 if (hT === '02') { -1297 var vidx = _ASN1HEX.getVidx(h, idx); -1298 // check if DER INTEGER VALUE have least leading zeros -1299 // for two's complement -1300 // GOOD - 3fabde... 008fad... -1301 // BAD - 000012... 007fad... -1302 if (h.substr(vidx, 2) == "00" && h.charCodeAt(vidx + 2) < 56) // '8'=56 -1303 throw new Error("not least zeros for DER INTEGER"); +1105 } else if (tag == "14") { // TeletexString +1106 var s = _iso88591hextoutf8(hV); +1107 result[tagName[tag]] = {str: s}; +1108 return result; +1109 } else if (tag == "1e") { // BMPString +1110 var s = _ucs2hextoutf8(hV); +1111 result[tagName[tag]] = {str: s}; +1112 return result; +1113 } else if (":0c:12:13:16:17:18:1a:".indexOf(tag) != -1) { // Other Strings types +1114 var s = _hextoutf8(hV); +1115 result[tagName[tag]] = {str: s}; +1116 return result; +1117 } else if (tag.match(/^8[0-9]$/)) { +1118 var s = _hextoutf8(hV); +1119 if (s == null | s == "") { +1120 return {"tag": {"tag": tag, explicit: false, hex: hV}}; +1121 } else if (s.match(/[\x00-\x1F\x7F-\x9F]/) != null || +1122 s.match(/[\u0000-\u001F\u0080–\u009F]/) != null) { +1123 return {"tag": {"tag": tag, explicit: false, hex: hV}}; +1124 } else { +1125 return {"tag": {"tag": tag, explicit: false, str: s}}; +1126 } +1127 } else if (tag.match(/^a[0-9]$/)) { +1128 try { +1129 if (! _isASN1HEX(hV)) throw new Error("not encap"); +1130 return {"tag": {"tag": tag, +1131 explicit: true, +1132 obj: _parse(hV)}}; +1133 } catch(ex) { +1134 return {"tag": {"tag": tag, explicit: true, hex: hV}}; +1135 } +1136 } else { +1137 var d = new KJUR.asn1.ASN1Object(); +1138 d.hV = hV; +1139 var hL = d.getLengthHexFromValue(); +1140 return {"asn1": {"tlv": tag + hL + hV}}; +1141 } +1142 }; +1143 +1144 /** +1145 * check if a hexadecimal tag is a specified ASN.1 context specific tag +1146 * @name isContextTag +1147 * @memberOf ASN1HEX +1148 * @function +1149 * @param {hTag} hex string of a hexadecimal ASN.1 tag consists by two characters (e.x. "a0") +1150 * @param {sTag} context specific tag in string represention (OPTION) (e.x. "[0]") +1151 * @return {Boolean} true if hTag is a ASN.1 context specific tag specified by sTag value. +1152 * @since jsrsasign 8.0.21 asn1hex 1.2.2 +1153 * @description +1154 * This method checks if a hexadecimal tag is a specified ASN.1 context specific tag. +1155 * Structured and non-structured type of tag have the same string representation +1156 * of context specific tag. For example tag "a0" and "80" have the same string +1157 * representation "[0]". +1158 * The sTag has a range from from "[0]" to "[31]". +1159 * @example +1160 * ASN1HEX.isContextTag('a0', '[0]') → true // structured +1161 * ASN1HEX.isContextTag('a1', '[1]') → true // structured +1162 * ASN1HEX.isContextTag('a2', '[2]') → true // structured +1163 * ASN1HEX.isContextTag('80', '[0]') → true // non structured +1164 * ASN1HEX.isContextTag('81', '[1]') → true // non structured +1165 * ASN1HEX.isContextTag('82', '[2]') → true // non structured +1166 * ASN1HEX.isContextTag('a0', '[3]') → false +1167 * ASN1HEX.isContextTag('80', '[15]') → false +1168 * +1169 * ASN.1 tag bits +1170 * 12345679 +1171 * ++ tag class(universal:00, context specific:10) +1172 * + structured:1, primitive:0 +1173 * +++++ tag number (0 - 31) +1174 */ +1175 ASN1HEX.isContextTag = function(hTag, sTag) { +1176 hTag = hTag.toLowerCase(); +1177 var ihtag, istag; +1178 +1179 try { +1180 ihtag = parseInt(hTag, 16); +1181 } catch (ex) { +1182 return -1; +1183 } +1184 +1185 if (sTag === undefined) { +1186 if ((ihtag & 192) == 128) { +1187 return true; +1188 } else { +1189 return false; +1190 } +1191 } +1192 +1193 try { +1194 var result = sTag.match(/^\[[0-9]+\]$/); +1195 if (result == null) return false; +1196 istag = parseInt(sTag.substr(1,sTag.length - 1), 10); +1197 if (istag > 31) return false; +1198 if (((ihtag & 192) == 128) && // ihtag & b11000000 == b10000000 +1199 ((ihtag & 31) == istag)) { // ihtag & b00011111 == istag (0-31) +1200 return true; +1201 } +1202 return false; +1203 } catch (ex) { +1204 return false; +1205 } +1206 }; +1207 +1208 /** +1209 * simple ASN.1 DER hexadecimal string checker<br/> +1210 * @name isASN1HEX +1211 * @memberOf ASN1HEX +1212 * @function +1213 * @param {String} hex string to check whether it is hexadecmal string for ASN.1 DER or not +1214 * @return {Boolean} true if it is hexadecimal string of ASN.1 data otherwise false +1215 * @since jsrsasign 4.8.3 asn1hex 1.1.6 +1216 * @description +1217 * This method checks wheather the argument 'hex' is a hexadecimal string of +1218 * ASN.1 data or not. +1219 * @example +1220 * ASN1HEX.isASN1HEX('0203012345') → true // PROPER ASN.1 INTEGER +1221 * ASN1HEX.isASN1HEX('0203012345ff') → false // TOO LONG VALUE +1222 * ASN1HEX.isASN1HEX('02030123') → false // TOO SHORT VALUE +1223 * ASN1HEX.isASN1HEX('fa3bcd') → false // WRONG FOR ASN.1 +1224 */ +1225 ASN1HEX.isASN1HEX = function(hex) { +1226 var _ASN1HEX = ASN1HEX; +1227 if (hex.length % 2 == 1) return false; +1228 +1229 var intL = _ASN1HEX.getVblen(hex, 0); +1230 var hT = hex.substr(0, 2); +1231 var hL = _ASN1HEX.getL(hex, 0); +1232 var hVLength = hex.length - hT.length - hL.length; +1233 if (hVLength == intL * 2) return true; +1234 +1235 return false; +1236 }; +1237 +1238 /** +1239 * strict ASN.1 DER hexadecimal string checker +1240 * @name checkStrictDER +1241 * @memberOf ASN1HEX +1242 * @function +1243 * @param {String} hex string to check whether it is hexadecmal string for ASN.1 DER or not +1244 * @return unspecified +1245 * @since jsrsasign 8.0.19 asn1hex 1.2.1 +1246 * @throws Error when malformed ASN.1 DER hexadecimal string +1247 * @description +1248 * This method checks wheather the argument 'hex' is a hexadecimal string of +1249 * ASN.1 data or not. If the argument is not DER string, this +1250 * raise an exception. +1251 * @example +1252 * ASN1HEX.checkStrictDER('0203012345') → NO EXCEPTION FOR PROPER ASN.1 INTEGER +1253 * ASN1HEX.checkStrictDER('0203012345ff') → RAISE EXCEPTION FOR TOO LONG VALUE +1254 * ASN1HEX.checkStrictDER('02030123') → false RAISE EXCEPITON FOR TOO SHORT VALUE +1255 * ASN1HEX.checkStrictDER('fa3bcd') → false RAISE EXCEPTION FOR WRONG ASN.1 +1256 */ +1257 ASN1HEX.checkStrictDER = function(h, idx, maxHexLen, maxByteLen, maxLbyteLen) { +1258 var _ASN1HEX = ASN1HEX; +1259 +1260 if (maxHexLen === undefined) { +1261 // 1. hex string check +1262 if (typeof h != "string") throw new Error("not hex string"); +1263 h = h.toLowerCase(); +1264 if (! KJUR.lang.String.isHex(h)) throw new Error("not hex string"); +1265 +1266 // 2. set max if needed +1267 // max length of hexadecimal string +1268 maxHexLen = h.length; +1269 // max length of octets +1270 maxByteLen = h.length / 2; +1271 // max length of L octets of TLV +1272 if (maxByteLen < 0x80) { +1273 maxLbyteLen = 1; +1274 } else { +1275 maxLbyteLen = Math.ceil(maxByteLen.toString(16)) + 1; +1276 } +1277 } +1278 //console.log(maxHexLen + ":" + maxByteLen + ":" + maxLbyteLen); +1279 +1280 // 3. check if L(length) string not exceeds maxLbyteLen +1281 var hL = _ASN1HEX.getL(h, idx); +1282 if (hL.length > maxLbyteLen * 2) +1283 throw new Error("L of TLV too long: idx=" + idx); +1284 +1285 // 4. check if V(value) octet length (i.e. L(length) value) +1286 // not exceeds maxByteLen +1287 var vblen = _ASN1HEX.getVblen(h, idx); +1288 if (vblen > maxByteLen) +1289 throw new Error("value of L too long than hex: idx=" + idx); +1290 +1291 // 5. check V string length and L's value are the same +1292 var hTLV = _ASN1HEX.getTLV(h, idx); +1293 var hVLength = +1294 hTLV.length - 2 - _ASN1HEX.getL(h, idx).length; +1295 if (hVLength !== (vblen * 2)) +1296 throw new Error("V string length and L's value not the same:" + +1297 hVLength + "/" + (vblen * 2)); +1298 +1299 // 6. check appending garbled string +1300 if (idx === 0) { +1301 if (h.length != hTLV.length) +1302 throw new Error("total length and TLV length unmatch:" + +1303 h.length + "!=" + hTLV.length); 1304 } 1305 -1306 // 8. check if all of elements in a structured item are conformed to -1307 // strict DER encoding rules. -1308 if (parseInt(hT, 16) & 32) { // structured tag? -1309 var intL = _ASN1HEX.getVblen(h, idx); -1310 var sum = 0; -1311 var aIdx = _ASN1HEX.getChildIdx(h, idx); -1312 for (var i = 0; i < aIdx.length; i++) { -1313 var tlv = _ASN1HEX.getTLV(h, aIdx[i]); -1314 sum += tlv.length; -1315 _ASN1HEX.checkStrictDER(h, aIdx[i], -1316 maxHexLen, maxByteLen, maxLbyteLen); -1317 } -1318 if ((intL * 2) != sum) -1319 throw new Error("sum of children's TLV length and L unmatch: " + -1320 (intL * 2) + "!=" + sum); -1321 } -1322 }; -1323 -1324 /** -1325 * get hexacedimal string from PEM format data<br/> -1326 * @name oidname -1327 * @memberOf ASN1HEX -1328 * @function -1329 * @param {String} oidDotOrHex number dot notation(i.e. 1.2.3) or hexadecimal string for OID -1330 * @return {String} name for OID -1331 * @since jsrsasign 7.2.0 asn1hex 1.1.11 -1332 * @description -1333 * This static method gets a OID name for -1334 * a specified string of number dot notation (i.e. 1.2.3) or -1335 * hexadecimal string. -1336 * @example -1337 * ASN1HEX.oidname("2.5.29.37") → extKeyUsage -1338 * ASN1HEX.oidname("551d25") → extKeyUsage -1339 * ASN1HEX.oidname("0.1.2.3") → 0.1.2.3 // unknown -1340 */ -1341 ASN1HEX.oidname = function(oidDotOrHex) { -1342 var _KJUR_asn1 = KJUR.asn1; -1343 if (KJUR.lang.String.isHex(oidDotOrHex)) -1344 oidDotOrHex = _KJUR_asn1.ASN1Util.oidHexToInt(oidDotOrHex); -1345 var name = _KJUR_asn1.x509.OID.oid2name(oidDotOrHex); -1346 if (name === "") name = oidDotOrHex; -1347 return name; -1348 }; -1349 -1350