信用卡芯片和普通JavaScript中的加密
#javascript #encryption #finance

您当前的信用卡可以以四种不同的方式使用:

  1. 在卡号中打字。自1950年以来,我们一直以这种方式使用信用卡。
  2. 将卡的磁条滑动(黑色条纹)通过读卡器。这种方法是由IBM在1960年代发明的,此后一直在使用,但它正在出路。 In a few short years许多信用卡将不再有磁条。幸运的是,我们将在2030年代的某个时候完全将它们分出。
  3. 将卡插入中。芯片卡是在1960年代首次发明的,信不信由你。法国和德国在1980年代为他们制定了标准,但直到2000年代才掌握了全球标准(EMV),而直到2010年代中期,美国才开始发行芯片卡。到现在为止,每个主要信用卡都有一个微芯片。
  4. 在非接触式芯片读取器上挥舞卡。这与插入卡并使用相同的嵌入式微芯片基本相同。

今天,我将深入到最后两个,解释您的卡上的“芯片”是什么以及它的工作原理。所涉及的技术与确保网络浏览和应用程序下载安全的技术密切相关。

微芯片

毫无疑问,您注意到信用卡正面的闪亮金属芯片。除了那不是芯片;这是电气接触。当您插入卡时,将通过联系人的top left pin提供电力,以为嵌入卡内的实际 Microchip供电,并使用其他引脚在微芯片和付款终端之间进行通信。如果您的卡是“启用”启用的“点击付款”,则它在内部边缘周围也有一个天线,可以接收无线电源并通过无线电波与读卡器通信。

要清楚,信用卡上的微芯片是计算机!不完全是。这是一台运行一个或多个应用程序的非常小的计算机。所以很酷。您现在可能在钱包里携带两三台计算机。

为什么您的信用卡需要板载计算机?那不是过分吗?

想想现在窃取某人的信用卡信息有多容易。快速照片将做到这一点。该卡的“密码”名称,16位数字,到期日期和安全代码都清楚地显示了。我们中的许多人都有发现其他人正在使用我们的信用卡号的不愉快经验,我们甚至不知道他们是如何得到的。实际上,there's a thriving industry周围的信用卡欺诈:买卖偷窃数字的公司,其他测试它们是否仍在工作的公司,以及在他们和在线和离线盗贼的一支大军之间流传的数万美元。

即使卡除了磁条,卡仍然是空白的,它仍然是一个相对容易的目标。信用卡的号码永远不会改变。而且读取磁条是数十年历史的技术,因此可以用比信用卡小的设备(包括车载内存)来完成。这样的设备(称为“撇渣器”)通常插入加油站读取器中,然后在检索之前收集数据几天。因此,信用卡号和磁带都不符合保护您的财务身份的任务。

这是一个值得解决的问题。但是信用卡需要以某种方式识别自己 。杂货店的读卡器必须告诉您的银行您的支出数量,银行需要从您的帐户中扣除钱。如果黑客正在聆听该电子对话,我们如何阻止他们足够学习以将您的卡用于欺诈?

这与我写过的另一个主题非常相似:how HTTPS stops hackers from reading the information you send over the Internet。当您访问网站时,您的浏览器和网站在Open 中共同建立一个秘密解码键,即使他们一直在听,也没有人能弄清楚它是什么。那是素数的魔力。

那么,您的信用卡是否与银行进行HTTPS密钥交换?好吧,不是很。

加密和对称性

如果您曾经用化妆语言写信给朋友,或使用数字代码(a = 1,b = 2,c = 3 ...),则了解加密的概念。加密正在传达信息并用某种算法掩饰。解密正在逆转算法以解码并查看原始消息。如今,我们几乎在网上加密所有内容。我们也可能;它是快速和免费的,最好在隐私方面犯错。

有时我们需要加密如此艰难的东西,永远无法解密。我们这样做是为了密码(或尝试)。除了您知道您的密码外,任何人绝对没有理由。您登录到密码的不可逆加密的哈希的网站,然后每次登录时使用该哈希进行比较。他们不知道您的密码,但是他们可以检查一下您是否'''重新键入正确的一个。

这不是其中之一。您的信用卡需要提供银行(但其他人)可以迅速解密的信息。

做到这一点的一种方法是使用对称键加密(也称为私有密钥加密)。这要求信用卡和银行具有共享的,预定的:可用于加密或解密任何消息的字符串。这没问题,因为该卡首先来自银行。他们可以在将其邮寄给您之前将密钥编程到卡中。

让我们在JavaScript中实现两轮对称密钥加密算法。在现实生活中,它将太简单和不安全,但是它将说明像AES这样的算法是如何工作的。我们将使用位XOR操作符^,您可能不经常看到,但是您需要知道的只是a ^ b === c,而c ^ a === bc ^ b === a

//
// ENCRYPTION
//

// This is the entry point. It converts your shared key to a number
//  and your message to an array of numbers, then encrypts it in two
//  steps. It converts the resulting array of numbers back to a string
//  and returns it.
function encrypt(sharedKey, message) {
  const numericKey = stringKeyToNumeric(sharedKey)
  const messageBytes = stringToIntArray(message)
  const r1 = encryptRound1(numericKey, messageBytes)
  const r2 = encryptRound2(numericKey, r1)
  const encrypted = intArrayToString(r2)
  return encrypted
}

function encryptRound1(numericKey, byteArray) {
  // Create a "round key" so we aren't using the same key for every step
  const roundKey = numericKey ^ 4321

  // For each character in the message, XOR it with the round key
  return byteArray.map(byte => byte ^ roundKey)
}

function encryptRound2(numericKey, byteArray) {
  const roundKey = numericKey % 1234
  const newBytes = byteArray.slice()

  // "Rotate" the array (move the last element to the beginning) roundKey times
  for (let step = 0; step < roundKey; step++) {
    newBytes.unshift(newBytes.pop())
  }
  return newBytes
}

//
// DECRYPTION
//

// The other entry point. It does the same thing as `encrypt` but backwards.
function decrypt(sharedKey, encrypted) {
  const numericKey = stringKeyToNumeric(sharedKey)
  const messageBytes = stringToIntArray(encrypted)
  const r1 = decryptRound1(numericKey, messageBytes)
  const r2 = decryptRound2(numericKey, r1)
  const message = intArrayToString(r2)
  return message
}

function decryptRound1(numericKey, byteArray) {
  const roundKey = numericKey % 1234
  const newBytes = byteArray.slice()

  // "Unrotate" the array roundKey times
  for (let step = 0; step < roundKey; step++) {
    newBytes.push(newBytes.shift())
  }
  return newBytes
}

function decryptRound2(numericKey, byteArray) {
  const roundKey = numericKey ^ 4321

  // XOR each character with the same round key, which gives us the original
  return byteArray.map(byte => byte ^ roundKey)
}

//
// UTILITY
//

function stringToIntArray(message) {
  return message.split('').map(char => char.charCodeAt(0))
}

function intArrayToString(intArray) {
  return intArray.map(int => String.fromCharCode(int)).join('')
}

function stringKeyToNumeric(key) {
  const fourDigitCodes = key.split('').map(char => char.charCodeAt(0).toString().padStart(4, '0'))
  const numericKey = Number(fourDigitCodes.join(''))
  return numericKey
}

//
// TEST PROGRAM
//

const message = "Hello there."
const sharedKey = "ABCDEF"

console.log('Message:', message) // => Message: Hello there.

const encrypted = encrypt(sharedKey, message)
console.log('Encrypted:', encrypted) // => Encrypted: ႎჁ႕ႉႄ႓ႄ჏Ⴉႄႍႍ

const decrypted = decrypt(sharedKey, encrypted)
console.log('Decrypted:', decrypted) // => Decrypted: Hello there.

继续,如果您愿意,请将其粘贴到一个替补中。

你们中的一些人可能会说“ this 您称之为简单?!”其他人可能会说:“这太简单了,查找表和密码在哪里?”看,我不能取悦所有人,但是我 can 不高兴所有人,所以这就是我所做的。

让我们将此代码放在上下文中。如果我的信用卡的Microchip已将共享密钥"ABCDEF"编程到其中,并且我的银行知道将该密钥用于我来自我的消息,这是购买授权如何进行的简化示例:

付款终端:我正在验证在10222年10月10日下午11:15在PET Central购买四个龙猫。请回复。

Microchip:好的。我已经收到了交易,但是我需要一个PIN编号来授权。

付款终端:当然的事情。客户,请输入您的PIN。

我: *类型9999。 *

付款终端:是正确的PIN,Microchip?

Microchip:是的,那是一个。我相信这笔交易。 encrypt("ABCDEF", "Pet Central: 89.99 USD at 11:15 PM, Oct 10 2022") ...完成。交易已用我的私钥加密。请将其发送给第一银行,并告诉他们它来自Isaac Lyman,其帐号为12345678。

付款终端:很棒。联系银行...

第一银行:您好,第一银行演讲。

付款终端:我是PET Central的付款终端。我想处理以艾萨克·莱曼(Isaac Lyman)为单位的帐户12345678的交易。

第一银行:收到的交易。 decrypt("ABCDEF", transaction) ...完成。我可以验证此交易是从Isaac Lyman的信用卡中验证的,因为否则它不会解密。一切都排队,所以我将从他的帐户中扣除89.99美元并将其发送。

付款终端:交易成功。

请注意,我的信用卡号不必通过电线发送。我的私钥也没有。实际上,没有什么值得窃取的东西离开了航站楼。黑客可以抓住加密的交易消息,但是除非有时间机器并想在我最初购买的同时,除非他们有时间机器,否则无法使用它。

对称密钥加密由某些银行使用,但有几个小缺陷。其中之一是,从理论上讲,银行可以构成虚假交易并为您收取您的费用。他们拥有您的私钥,所以唯一阻止他们的是数百种国际法规。另一个缺陷是您的私钥存储在银行的Internet连接服务器上,因此,如果有安全性漏洞,它可能会被盗。

要清楚:对称密钥加密比信用卡号和磁条要好得多。但是有一个更好的方法。

不对称键

对称键很有意义。当然

不对称键进入魔术领域。或者,至少是违反直觉数学。

在非对称加密中,有两个不同的键:私钥和一个公钥。这些密钥在数学上是相关的,但是知道一个密钥不会帮助您找出另一个。它们提供相反的功能:您可以使用只能被公共密钥解密的私钥加密消息,并且您可以用公共密钥对消息进行加密,该消息只能被私钥解密。

不对称加密经常被记者用于敏感研究。他们可以生成一个密钥对,将私钥存储在他们信任的设备上,并发布公共密钥供全世界查看。任何想发送秘密消息的人都可以使用公共密钥对其进行加密,以确保只有记者能够解密它。在相反的方向上,记者可以使用其私钥(或加密其称为“签名” IT)加密消息,从而提供了作者身份的证明。如果可以使用其公共密钥解密消息(或哈希),则证明它来自它们。

返回信用卡。银行可以将公共/私钥对编程到您的信用卡上,然后完全忘记私钥。他们不需要将其存储在任何地方。他们只需要遵守公钥即可。他们将其用于两个目的:解密从您的卡发送的消息来证明这些消息来自您,而不是它们。

这消除了对称密钥的第一个风险:银行本身犯下的欺诈。只要他们能够证明他们在编程您的卡后没有保留您的私钥,就不能被指控产生假交易。它还消除了第二种风险。您的私钥未存储在任何连接的服务器上;它只生活在整个世界的一个地方,这在您的信用卡中。当然,您的公钥可能会被盗。但是盗贼唯一能做的就是阅读您的交易。他们将无法创建新的。

让我们在JavaScript中实现一些不对称的加密。这将说明像RSA这样的算法如何工作。

//
// ENCRYPTION
//

function encrypt(privateKey, message) {
  const messageBytes = stringToIntArray(message)
  const [modulus, exponent] = privateKey
  const encrypted = messageBytes.map(byte => (BigInt(byte) ** exponent) % modulus)
  return intArrayToString(encrypted)
}

//
// DECRYPTION
//

function decrypt(publicKey, encrypted) {
  const encryptedBytes = stringToIntArray(encrypted)
  const [modulus, exponent] = publicKey
  const decrypted = encryptedBytes.map(byte => (BigInt(byte) ** exponent) % modulus)
  return intArrayToString(decrypted)
}

//
// UTILITY
//

function stringToIntArray(message) {
  return message.split('').map(char => char.charCodeAt(0))
}

function intArrayToString(intArray) {
  return intArray.map(int => String.fromCharCode(Number(int))).join('')
}

//
// TEST PROGRAM
//

/* 
  Each key has two parts: a shared "modulus" and a non-shared "exponent."
  Key generation is complex. These three numbers all have to be mathematically related.
  In real life the modulus would be a very large number in the ballpark of
  5353683424958100487553692737855921351167231508809347122534824314767512079033611122618
  8508861310385963124739442570162267402922655660049366578306762217485423974277904708682
  5660398049299993503929195152693455928734884540497474885533510411221183157745113097158
  7147857841470416503261588799943431828622126181414376019549024568602030872555448200000
  2641304272163777569374865564566594216223640291860534772832785404395599963806669068407
  0321707428203752195331410456415056194233539898345799623002726720980709094812380346346
  3583607751796240362096547051130726215056560026642532836575212569426651466587428695230
  1071376112504419090641365.
  The exponents would be somewhat smaller. Both exponents must be prime numbers.

  We use the `n` suffix to get BigInts, since playing with exponents can exceed
  JavaScript's standard number limit pretty quickly.
*/
const privateKey = [25777n, 3n]
const publicKey = [25777n, 16971n]

const message = "Four chinchillas"
console.log("Message:", message) // => Message: Four chinchillas

const encrypted = encrypt(privateKey, message)
console.log("Encrypted:", encrypted) // => Encrypted: ớ֪൯⿟᭏䂦䁅宍㿵䂦䁅宍垐垐⣮ 

const decrypted = decrypt(publicKey, encrypted)
console.log("Decrypted:", decrypted) // => Decrypted: Four chinchillas

目前您可能会想的几件事:

  • 等等,这比其他代码段短。如果看起来不正确,那是因为不是。这只是不对称部分。在现实生活中,您会在使用非对称键之前进行一些可逆的加密(“填充”)回合,以便通过字母频率攻击无法破解加密文本。但这也不是整个故事。通常,消息发送者不对称地加密随机生成的键,将该键使用到对称性上加密消息,并将对称加密的消息和不对称的加密键一起发送在一起。 (这是因为对称加密更快。)
  • 嘿,这看起来很像Diffie-Hellman密钥交易!使用数字对应物加密和解密消息的一种方法。素数很容易乘以乘,结果很难因素。这是第一部分。第二部分:指数和模量具有神秘的属性,允许数字在维度之间传播。
  • 悲伤,这很复杂。,这就是为什么您永远不应该自己做。甚至犯一个小错误,您的安全性降低了。您应该始终使用您编程的任何语言使用流行的,vet的密码库。这是重复的:永远不要独自实施加密算法。

一些银行在信用卡芯片中使用不对称的密钥对。这为一些额外的安全功能创造了机会。例如,如果银行有自己的公共/私钥对,他们可以发布其公共密钥并使用其私钥签名 公共密钥,这会创建一系列证据,将您的卡链接到他们身上。当您使用卡时,它可以通过银行的加密签名将其公共密钥发送到终端,证明他们发行了它!付款终端只需要知道银行的公钥即可验证签名。然后,当卡签署交易时,终端可以检查其与签名的公钥匹配。这允许脱机,计算保证卡是真实的。

不幸的是,完全安全性是不可能的。仅在陷入错误的手之前,计算机才安全。您的卡片的微芯片是为了揭示其私钥的设计而设计的,但是在物理访问方面,所有赌注都关闭了。因此,如果您丢掉钱包,您仍然需要致电银行。

信用卡的未来

许多商店不愿升级其付款码头 - 这是一个困难的费用,但大多数商店在2015年这样做,当时发卡机构更新他们的协议,以使信用卡欺诈的商店承担责任,如果他们的终端没有终端。 t有EMV芯片读取器。责任是商业世界中的强大力量。因此,现在您购物的大多数地方都可以插入卡片的微芯片,并且有很多也有无接触式的付费终端。

下一步是什么?

摆脱可见的信用卡号和磁条将是很棒的。信用卡欺诈行业将喘着粗气。一个主要的保留是在线购物。您仍然必须输入您的姓名,卡号,到期日期和CVV代码才能从网站购买商品。但是世界已经成熟了。在过去五年中,大多数智能手机都有NFC芯片,并且所有电子商务大约发生在智能手机上。因此,在软件方面进行了一些工作,不难想象一个世界,您可以通过手机屏幕挥舞信用卡来支付一些费用。

然后,这可能是太多步骤。 iPhone和Android都具有移动钱包功能,因此您可以将卡编程到手机中,并使用手机付费(在Tap-To-to-top终端)和某些在线商店。这不需要消失;也许有一个未来的版本,您可以使用与发行银行的直接连接在移动钱包中添加卡片,而不是一些易于使用的数字字符串。

理想的信用卡将是无法欺诈的信用卡,除非它被先进技术和大量时间的人偷走了。即使您愿意,您也无法向骗局揭示其秘密。

喜欢许多其他事情,我们将其留在微芯片的能力中。

致谢

感谢以下作者,如果没有谁的工作,我根本不理解这个主题: