11-bo‘lim

Raqamli imzolar

Kim imzolaganini isbotlash - va tekshirish kodidagi eng ko'p uchraydigan xato.

🕑 12 daqiqa o‘qish 📄 755 so‘z 👁 1 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Imzolash va tekshirish
  2. Tekshirish
  3. Amaliy misol: dasturiy ta'minot relizi
  4. Imzo algoritmini tanlash
  5. Imzo va HMAC: qaysi biri kerak
  6. Xulosa

Raqamli imzo - maxfiy kalit bilan yaratiladigan, ochiq kalit bilan tekshiriladigan "muhr".

HMAC dan farqi: tekshirish uchun maxfiy kalit kerak emas. Demak imzoni har kim tekshira oladi, lekin faqat bitta odam yarata oladi.

Imzolash va tekshirish #

PHP
// DIQQAT: darslik uchun ochiq e'lon qilingan kalit. Real tizimda ishlatmang.
const DEMO_MAXFIY = <<<'PEM'
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDAMaCT68asinZp
/aYbrjiNHhKSbFXOLpXpcwlZYSejSoVuSyLchWD55wE/P7zLt+ZKB34zrpwFNPfq
ICInvr5o2VwoUQ30XsGOPP680send61FKUtiqZp7kMG1R/56TJXT+Lrt/ls5kRGi
IFfcWY0KhaQ6+88nMX7WAv40ivQnhhuFTo/hLIMrbdLYpoJOgNzC4gKAuyhLVIfg
2yVFyKy50MtfBa2gv5Jmz1s9FAW38pRQIThLd2TEqMtxtqB6kk3Z7W+lzZDrvvw0
XGCK2P58jBRx9utNABb4wyMWGYbrXZY9bkt64Gx+ioTlZl8rGQuNLM0MT1DNCosh
gEb5PGHjAgMBAAECggEAVmQ4bSV3mclnrv8gcqLesrhI2t6tt0+DfXuDQVUFr0Xp
Mom9suiVjd/D1L1+zQfuEWrK1MpxQXns0qX9FGaOSq0h2tGMd0M8uAY8733D10hZ
b5Q/WaspUM/h8u3YuvrAApd011QVh4vpJDprpBXw60DlcMoN9O0vuV2BDPdFBppf
RId6Fz4NzO63WjTbTvbocwZSTPWur1rbiLFDTc5jbhOUCiLmIyKuLh4MheQa8PFS
y6CCA8YPzLWLXOXW5cDTrOTut0cr8J9fdinm1TuxjHlgxm7tWFEQZwJHOLVHNB4I
o5qBDDD7XwBnijiLSejmAc2OiCPHUPZZY52VkvvLmQKBgQDhuRC+UOgQO/7O4wWf
3hwjsxMWydThnK10+i4EakVEOOC3DtjbC/lv4zvrVdS8w1/gX2qyeZ4F/0UAL/Sc
31C9NDkaqhIGjF/AN4NEBp5d0+dJYR2hoJDb/j4vnGBx+Fa/cFSOj443ZnJB9PMp
OoGcRXKSTIXwQdSgq7tyKngCnwKBgQDZ+Ttn9LSlT8RZZpI2FQ8oObI/3cvaTvHI
eYoviwUmBVJlwj27JNAiYqfy4VLmXcxqbn+snkpYL50qPm+/SfRXcXIm6CngSLtS
be2wf4OyAuYvAvtoJb5zhimU+uVpF3HTBi8nPGGwK93XoRDVwSRsNN17ahdmrCqJ
nBwNNbb+PQKBgQCA07r2o/6OpW88bN2c7Itq5J2UeAUD7wQgq1jwNCbsVPoMAHFM
M31dmndsTURpp6hkypB+5GKIUbTz8mrNzbarBc9BpKc9DYjoj8ZfYNep/XMXslHT
GW0T4ftj+QAii/YWtOnNo6krfVDgzAisEzQ+GHTXgs0+3pOYT+tfsVVlSQKBgQCt
HcbSr4TxbkLRhidFkMAzAG/TTYJj5iMdR3o6fD0gj0V70CNbCMfNJ3Gf8tj2YTTC
qvP30tfU5iv6qw/xavkGofE5eVYY7Wro8qm+befYb3kFiWbGtoNmGP3ZdtLQ/m/G
W7kDfm9PjedpJGDMg9M7pVjqdb4UK1S1aijVTP4DOQKBgHMmAkzUb6XG2fNPJ3oi
dmcKb6VLKhAFtUiasNeo2EtN3F2jBbs7hLbbAqJEknhyqZttmZQA/azq6BYUUFuO
JaI72NPuT7d3qxciD1IrcwLXbGYbRiHbMvMNrS//FNFgdoNgLqIy6tu8AieLa769
bIqqSM4fDts4Kt1eSpsvjKYo
-----END PRIVATE KEY-----
PEM;

const DEMO_OCHIQ = <<<'PEM'
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwDGgk+vGrIp2af2mG644
jR4SkmxVzi6V6XMJWWEno0qFbksi3IVg+ecBPz+8y7fmSgd+M66cBTT36iAiJ76+
aNlcKFEN9F7Bjjz+vNLHp3etRSlLYqmae5DBtUf+ekyV0/i67f5bOZERoiBX3FmN
CoWkOvvPJzF+1gL+NIr0J4YbhU6P4SyDK23S2KaCToDcwuICgLsoS1SH4NslRcis
udDLXwWtoL+SZs9bPRQFt/KUUCE4S3dkxKjLcbagepJN2e1vpc2Q6778NFxgitj+
fIwUcfbrTQAW+MMjFhmG612WPW5LeuBsfoqE5WZfKxkLjSzNDE9QzQqLIYBG+Txh
4wIDAQAB
-----END PUBLIC KEY-----
PEM;

function imzola(string $xabar): string
{
    $maxfiy = openssl_pkey_get_private(DEMO_MAXFIY);
    if ($maxfiy === false) {
        throw new RuntimeException('maxfiy kalit oqilmadi');
    }

    if (!openssl_sign($xabar, $imzo, $maxfiy, OPENSSL_ALGO_SHA256)) {
        throw new RuntimeException('imzolash muvaffaqiyatsiz');
    }

    return $imzo;
}
PHP
$xabar = 'Reliz 2.4.0, sha256: 3b1f...';

$imzo = imzola($xabar);

printf("%-16s %d bayt%s", 'imzo uzunligi:', strlen($imzo), PHP_EOL);
printf("%-16s %s...%s", 'imzo boshi:', substr(bin2hex($imzo), 0, 32), PHP_EOL);

// RSA PKCS#1 v1.5 imzosi DETERMINISTIK - ikkinchi marta ham bir xil
$ikkinchi = imzola($xabar);
printf("%-16s %s%s", 'ikki imzo teng:',
    hash_equals($imzo, $ikkinchi) ? 'ha' : 'yoq', PHP_EOL);
Natija
imzo uzunligi:   256 bayt
imzo boshi:      46ee07c63e8f0f51855d7e341a734105...
ikki imzo teng:  ha

Tekshirish #

PHP
$xabar = 'Reliz 2.4.0, sha256: 3b1f...';
$imzo = imzola($xabar);

// 1. To'g'ri xabar va to'g'ri imzo
$natija = openssl_verify($xabar, $imzo, DEMO_OCHIQ, OPENSSL_ALGO_SHA256);
printf("%-22s %d%s", 'togri xabar:', $natija, PHP_EOL);

// 2. Xabar o'zgartirilgan
$natija = openssl_verify('Reliz 2.4.1, sha256: 3b1f...', $imzo,
    DEMO_OCHIQ, OPENSSL_ALGO_SHA256);
printf("%-22s %d%s", 'ozgartirilgan xabar:', $natija, PHP_EOL);

// 3. Imzo o'zgartirilgan
$buzuq = $imzo;
$buzuq[0] = chr(ord($buzuq[0]) ^ 0x01);
$natija = openssl_verify($xabar, $buzuq, DEMO_OCHIQ, OPENSSL_ALGO_SHA256);
printf("%-22s %d%s", 'buzilgan imzo:', $natija, PHP_EOL);

// 4. Butunlay noto'g'ri imzo
$natija = openssl_verify($xabar, 'axlat', DEMO_OCHIQ, OPENSSL_ALGO_SHA256);
printf("%-22s %d%s", 'axlat imzo:', $natija, PHP_EOL);
Natija
togri xabar:           1
ozgartirilgan xabar:   0
buzilgan imzo:         0
axlat imzo:            0
openssl_verify uchta qiymat qaytaradi

Bu PHP dagi eng xavfli tuzoqlardan biri:

QaytadiMa'nosi
1Imzo to'g'ri
0Imzo noto'g'ri
-1Xato yuz berdi (kalit o'qilmadi va h.k.)

Endi diqqat qiling:

KOD
if (openssl_verify($x, $imzo, $kalit)) {
    // KIRISHGA RUXSAT
}

Bu kod buzuq. -1 ham "rost" deb hisoblanadi, chunki PHP da nolga teng bo'lmagan har qanday son rost.

Ya'ni kalit o'qilmagan holatda kod imzoni to'g'ri deb qabul qiladi.

To'g'ri yozilishi:

KOD
if (openssl_verify($x, $imzo, $kalit, OPENSSL_ALGO_SHA256) === 1) {

=== 1 - majburiy.

PHP
// Boshqa TURDAGI kalit bilan tekshirish - xato holati
const EC_OCHIQ = <<<'PEM'
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEotEY2TwZ3ViMr0eo5gvz2SLX49re
uaXUCsG/9XmYgdCQNRukuCAv9+vfRY8M5wwxf2vj+wOsj9ekJzXZgAK8aw==
-----END PUBLIC KEY-----
PEM;

$xabar = 'test';
$imzo = imzola($xabar);   // RSA imzosi

// RSA imzosini EC kaliti bilan tekshiramiz
$natija = @openssl_verify($xabar, $imzo, EC_OCHIQ, OPENSSL_ALGO_SHA256);

printf("%-28s %s%s", 'natija:', var_export($natija, true), PHP_EOL);
printf("%-28s %s%s", 'if ($natija) ishlaydimi:',
    $natija ? 'HA - IMZO QABUL QILINDI!' : 'yoq', PHP_EOL);
printf("%-28s %s%s", '$natija === 1 ishlaydimi:',
    $natija === 1 ? 'ha' : 'yoq - TOGRI RAD ETILDI', PHP_EOL);
Natija
natija:                      -1
if ($natija) ishlaydimi:     HA - IMZO QABUL QILINDI!
$natija === 1 ishlaydimi:    yoq - TOGRI RAD ETILDI
Mana -1 ning oqibati

RSA imzosi EC kaliti bilan tekshirildi. Bu xato holat - kalit turi imzoga mos kelmaydi.

openssl_verify -1 qaytardi. Va -1 PHP da rost.

Demak if (openssl_verify(...)) yozilgan kod bu imzoni qabul qiladi - garchi u umuman tekshirilmagan bo'lsa ham.

Bu shunchaki nazariy xavf emas: hujumchi tizimga o'z ochiq kalitini (boshqa turdagi) kiritishga erisha olsa, istalgan imzo "to'g'ri" bo'lib qoladi.

Shuning uchun === 1 yozing.

Raqamli imzo - maxfiy kalit bilan yaratiladi, ochiq bilan tekshiriladi Imzolovchi (bitta odam) 1. xesh = SHA-256(xabar) 2. MAXFIY kalit bilan amal 3. imzo (256 bayt) faqat u qila oladi Ochiq tarqatiladi xabar + imzo sayt, oyna do'koni, e-pochta Tekshiruvchi (HAR KIM) 1. xesh = SHA-256(xabar) 2. OCHIQ kalit bilan tekshirish 3. natija === 1 ? maxfiy kalit KERAK EMAS PHP dagi tuzoq if (openssl_verify(...)) { ... } ← BUZUQ: -1 ham "rost" if (openssl_verify(...) === 1) { ... } ← TO'G'RI Imzo nima beradi Butunlik + autentiklik + rad eta olmaslik - imzolovchi keyin inkor qila olmaydi. Lekin maxfiylik bermaydi: xabar ochiq ketadi.
HMAC dan asosiy farq - tekshirish uchun sir kerak emas

Amaliy misol: dasturiy ta'minot relizi #

PHP
final class RelizImzosi
{
    public function imzola(string $fayl): array
    {
        $xesh = hash_file('sha256', $fayl);
        $manifest = json_encode([
            'fayl' => basename($fayl),
            'hajm' => filesize($fayl),
            'sha256' => $xesh,
            'versiya' => '2.4.0',
        ], JSON_UNESCAPED_SLASHES);

        // MANIFESTNI imzolaymiz, faylning o'zini emas
        return [
            'manifest' => $manifest,
            'imzo' => base64_encode(imzola($manifest)),
        ];
    }

    public function tekshir(string $fayl, array $reliz): string
    {
        // 1. Imzo to'g'rimi
        $natija = openssl_verify($reliz['manifest'],
            base64_decode($reliz['imzo']), DEMO_OCHIQ, OPENSSL_ALGO_SHA256);

        if ($natija !== 1) {
            return $natija === -1
                ? 'RAD: tekshirishda xato'
                : 'RAD: imzo notogri';
        }

        // 2. Manifestni endi ishonchli deb o'qiymiz
        $m = json_decode($reliz['manifest'], true, 512, JSON_THROW_ON_ERROR);

        // 3. Fayl manifestga mos keladimi
        if (filesize($fayl) !== $m['hajm']) {
            return 'RAD: hajm mos kelmadi';
        }

        if (!hash_equals($m['sha256'], hash_file('sha256', $fayl))) {
            return 'RAD: fayl xeshi mos kelmadi';
        }

        return "QABUL: {$m['fayl']} v{$m['versiya']}";
    }
}
PHP
$fayl = sys_get_temp_dir() . '/reliz-2.4.0.sh';
file_put_contents($fayl, "#!/bin/sh\necho 'dastur 2.4.0'\n");

$imzolovchi = new RelizImzosi();
$reliz = $imzolovchi->imzola($fayl);

echo 'manifest: ', $reliz['manifest'], PHP_EOL;
echo 'imzo:     ', substr($reliz['imzo'], 0, 40), '...', PHP_EOL;
echo PHP_EOL;

// 1. Toza fayl
echo '1. ozgarishsiz:  ', $imzolovchi->tekshir($fayl, $reliz), PHP_EOL;

// 2. Fayl o'zgartirilgan (zararli kod qo'shilgan)
file_put_contents($fayl, "#!/bin/sh\necho 'dastur 2.4.0'\n# qoshimcha\n");
echo '2. fayl ozgardi: ', $imzolovchi->tekshir($fayl, $reliz), PHP_EOL;

// 3. Manifest ham o'zgartirilgan (hujumchi xeshni yangiladi)
$soxta = $reliz;
$m = json_decode($soxta['manifest'], true);
$m['sha256'] = hash_file('sha256', $fayl);
$m['hajm'] = filesize($fayl);
$soxta['manifest'] = json_encode($m, JSON_UNESCAPED_SLASHES);
echo '3. manifest ham: ', $imzolovchi->tekshir($fayl, $soxta), PHP_EOL;

unlink($fayl);
Natija
manifest: {"fayl":"reliz-2.4.0.sh","hajm":30,"sha256":"98fedfa5cb178d56ab67ebbe64108bed4676e7301a75877a80d7c12a646703b0","versiya":"2.4.0"}
imzo:     MCS7Che5Yd+Qpmkjx+RyK/GhJNsdnH5d3Y8qa34g...

1. ozgarishsiz:  QABUL: reliz-2.4.0.sh v2.4.0
2. fayl ozgardi: RAD: fayl xeshi mos kelmadi
3. manifest ham: RAD: imzo notogri
Uchinchi holat - naqshning butun ma'nosi

Hujumchi faylni o'zgartirdi va manifestdagi xeshni ham yangiladi. Mantiqan hammasi mos keladi.

Lekin imzo mos kelmadi, chunki hujumchida maxfiy kalit yo'q.

Bu 5-bo'limdagi xesh muammosining yechimi: xesh yolg'iz o'zi hech narsani isbotlamaydi, chunki uni har kim qayta hisoblaydi. Imzo esa sirni talab qiladi.

Diqqat qiling: manifest imzolangan, faylning o'zi emas. Bu ataylab shunday:

FoydaIzoh
Imzolash tez200 baytlik manifest, 2 GB lik fayl emas
Metama'lumot ham himoyalanganVersiya, hajm, nom
Bir imzo - ko'p faylManifestda ro'yxat bo'lishi mumkin
Tekshirish bosqichma-bosqichAvval imzo, keyin xesh

Tartib muhim: avval imzoni tekshiring, keyin manifest mazmuniga ishoning. Teskarisi - ishonchsiz ma'lumotga tayanish demakdir.

Imzo algoritmini tanlash #

PHP
$xabar = 'test xabar';
$maxfiy = openssl_pkey_get_private(DEMO_MAXFIY);

foreach ([
    'SHA-1' => OPENSSL_ALGO_SHA1,
    'SHA-256' => OPENSSL_ALGO_SHA256,
    'SHA-384' => OPENSSL_ALGO_SHA384,
    'SHA-512' => OPENSSL_ALGO_SHA512,
] as $nom => $algo) {
    openssl_sign($xabar, $imzo, $maxfiy, $algo);
    printf('%-9s imzo %d bayt, tekshiruv: %d%s', $nom, strlen($imzo),
        openssl_verify($xabar, $imzo, DEMO_OCHIQ, $algo), PHP_EOL);
}
Natija
SHA-1     imzo 256 bayt, tekshiruv: 1
SHA-256   imzo 256 bayt, tekshiruv: 1
SHA-384   imzo 256 bayt, tekshiruv: 1
SHA-512   imzo 256 bayt, tekshiruv: 1
Algoritm imzo ichida yozilgan - lekin ishonmang

RSA PKCS#1 imzosi ichida qaysi xesh ishlatilgani ko'rsatilgan. Shuning uchun openssl_verify ga noto'g'ri algoritm bersangiz, tekshirish 0 beradi.

Lekin bu himoya emas. Xavfli naqsh shunday ko'rinadi:

KOD
// XATO: algoritmni ISHONCHSIZ manbadan olish
$algo = $sorov['alg'];
openssl_verify($xabar, $imzo, $kalit, $algo);

Hujumchi alg ni o'zgartirib, zaifroq algoritmga "tushirishi" mumkin.

To'g'ri yondashuv:

QoidaSabab
Algoritm kodda qat'iy yozilsinHujumchi tanlay olmasin
Faqat ruxsat etilgan ro'yxatmatch bilan tekshirish
SHA-1 rad etilsin3-bo'lim: buzilgan
Kalit turi ham tekshirilsinRSA imzosi RSA kaliti bilan

Bu algoritm chalkashligi (algorithm confusion) hujumi. Uning eng mashhur shakli JWT da uchraydi va 17-bo'limda batafsil ko'riladi.

Imzo va HMAC: qaysi biri kerak #

PHP
$jadval = [
    ['Kim yarata oladi', 'ikkala tomon', 'faqat kalit egasi'],
    ['Kim tekshira oladi', 'ikkala tomon', 'HAR KIM'],
    ['Rad eta olmaslik', 'yoq', 'HA'],
    ['Kalit tarqatish', 'xavfsiz kanal kerak', 'ochiq kalit erkin'],
    ['Tezlik', 'juda tez', 'sekin'],
    ['Olcham', '32 bayt', '64-256 bayt'],
];

printf("%-20s %-22s %s%s", '', 'HMAC', 'Raqamli imzo', PHP_EOL);
echo str_repeat('-', 62), PHP_EOL;
foreach ($jadval as $q) {
    printf("%-20s %-22s %s%s", ...[...$q, PHP_EOL]);
}

echo PHP_EOL, 'Qoida: uchinchi tomon tekshirishi kerakmi?', PHP_EOL;
echo '  ha  -> raqamli imzo', PHP_EOL;
echo '  yoq -> HMAC (tezroq va soddaroq)', PHP_EOL;
Natija
                     HMAC                   Raqamli imzo
--------------------------------------------------------------
Kim yarata oladi     ikkala tomon           faqat kalit egasi
Kim tekshira oladi   ikkala tomon           HAR KIM
Rad eta olmaslik     yoq                    HA
Kalit tarqatish      xavfsiz kanal kerak    ochiq kalit erkin
Tezlik               juda tez               sekin
Olcham               32 bayt                64-256 bayt

Qoida: uchinchi tomon tekshirishi kerakmi?
  ha  -> raqamli imzo
  yoq -> HMAC (tezroq va soddaroq)
Amaliy topshiriq
  1. Xabarni imzolang va imzo uzunligini yozing.
  2. Xuddi shu xabarni ikki marta imzolang - imzolar bir xilmi?
  3. Xabarni o'zgartirib tekshiring - natija nima?
  4. Imzoning bir baytini buzib tekshiring.
  5. openssl_verify ning uchta qaytish qiymatini sanab bering.
  6. if ($natija) va if ($natija === 1) farqini ko'rsating.
  7. Reliz manifestini imzolovchi sinf yozing.
  8. Faylni va manifestni ikkalasini ham o'zgartirib ko'ring - imzo o'tdimi?
  9. Nima uchun manifest imzolanadi, fayl emas - tushuntiring.
  10. Algoritmni foydalanuvchi kiritmasidan olish nima uchun xavfli ekanini yozing.

Xulosa #

  • Raqamli imzo maxfiy kalit bilan yaratiladi, ochiq kalit bilan tekshiriladi.
  • HMAC dan farqi: tekshirish uchun sir kerak emas - har kim tekshira oladi.
  • Shu sababli imzo rad eta olmaslikni beradi.
  • RSA PKCS#1 v1.5 imzosi deterministik - bir xil xabar bir xil imzo beradi.
  • openssl_verify uchta qiymat qaytaradi: 1, 0 va -1.
  • if (openssl_verify(...)) - buzuq kod: -1 ham rost hisoblanadi.
  • Har doim === 1 bilan tekshiring.
  • Katta fayllar uchun manifest imzolanadi: xesh, hajm, versiya.
  • Avval imzoni tekshiring, keyin manifest mazmuniga ishoning.
  • Imzo algoritmi kodda qat'iy bo'lsin - ishonchsiz manbadan olinmasin.

Keyingi bo'limda ikki tomon umumiy kalitga qanday kelishishini - kalit almashinuvini ko'ramiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.