הצפנה אסימטרית לפרחחי ווב

איך אנחנו עובדים עם מפתח פרטי ומפתח ציבורי?

במאמר הקודם הסברתי על הצפנה סימטרית וראינו שלמרות שמדובר בהצפנה חזקה, בסופו של יום העקרון מאחוריה הוא אותו עקרון של הצפנות מאוד ותיקות כמו הצפנת אתבש או צופן קיסר. כלומר יש לנו שני צדדים, צד א' וצד ב' שמחליפים ביניהם מידע באמצעות מפתח שידוע לשניהם. אם צד שלישי גונב את המפתח, אז התקשורת בין צד א' לצד ב' הופכת להיות גלויה. נכון, ההצפנות החדשות הופכות את גילוי המפתח באמצעות brute force לקשה ביותר (ממש בלתי אפשרי בטכנולוגיה של היום), אבל יש דרכים אחרות לגנוב מפתח. אם צד א' וצד ב' מתקשרים ביניהם, אפשרות גניבת המפתח הופכת להיות מאוד ריאלית.

ובדיוק כאן באה ההצפנה האסימטרית מסוג RSA. למה RSA? על שם המפתחים שלה. השיטה הזו פותחה על ידי רונלד ריבסט, עדי שמיר ולאונרד אדלמן. ההצפנה הזו היא אסימטרית – כלומר לא צריך מפתח משותף וזו ההתקדמות, בה"א הידיעה, במדעי ההצפנה. (וכן, עדי שמיר הוא ישראלי וכן, הוא חושב שהביומטרי זה רעיון רע. ואם יש פה איזה גאון ממשלתי שחושב שהוא רעיון טוב, שיתווכח איתו. חזוס כריסטוס, מעדיף לכרות לעצמי את האצבעות מאשר לתת אותן למאגר, איפה היינו?).

אז איך זה עובד בתיאוריה? בתיאוריה צד א' מייצר מפתח ציבורי ומפתח פרטי. המפתח הציבורי משמש להצפנה בלבד והמפתח הפרטי משמש לפתיחת ההצפנה בלבד. צד א' יכול לפרסם את המפתח הציבורי ללא כל חשש וללא דאגה. אפשר להשתמש בו אך ורק כדי להצפין. צד ב' יכול לקחת את המפתח ולהצפין את המסר ולשלוח את המסר המוצפן לצד א'. צד א' ישתמש במפתח הפרטי שלו לפתוח את ההצפנה.

איך זה עובד? קצת מתמטיקה? מוכנים? מן הסתם זו תהיה הפשטה מטורפת, אבל כאמור בכותרת כתוב "לפרחחי ווב" ואני? אני פרחח ווב.

המפתח הציבורי שלי הוא (5,14) והמפתח הפרטי שלי הוא (11,14). את המפתח הפרטי אני שומר בסוד ומפרסם את המפתח הציבורי. עכשיו מישהו רוצה לשלוח אלי הודעה. איזו הודעה? נניח את המספר "2". איך הוא יצפין אותה? יש לו את המפתח הציבורי (5,14) שלי. אז תהליך ההצפנה יהיה:

2^5mod(14)

כלומר הספרה שלי 2 בחזקת 5 (החלק הראשון של המפתח) ואז לעשות מודולוס (כלומר מציאת השארית של חילוק ב) 14. אתם יכולים להעתיק את התרגיל ולשים אותו בגוגל, גוגל יחשב אותו בשבילכם ויביא לכם את התוצאה. במקרה הזה "4". וזה המסר המוצפן.

אותו מישהו, שהצפין את ההודעה שלו, שולח לי את הספרה "4". אני לוקח את הספרה הזו ומעביר אותה במפתח הפרטי שלי. איך זה נראה? ככה:

4^11mod(14)

המספר המצופן, בחזקת החלק הראשון של המפתח הפרטי ומודולס (זוכרים שזה מציאת שארית של חילוק ב) חלק שני. אם נעשה את התרגיל הזה נקבל "2" וזו ההודעה המקורית.

חישוב פרימיטיבי של RSA. מ-2 ל-4.
חישוב פרימיטיבי של RSA

זה נראה פשוט עד כדי טמטום, אבל שימו לב – אם צד שלישי מאזין, כל מה שהוא מקבל זה את ההודעה המוצפנת ואת המפתח הציבורי. המפתח הפרטי נשאר אך ורק בצד המקבל. הצד השלישי, המאזין, לא יכול לעשות דבר.

כלומר הוא יכול לעשות ברוט פורס ולנסות לפצח את המספרים, אבל במציאות המספרים האלו כל כך גדולים שאין דרך לפענח אותם. אם מישהו רוצה לדעת איך חישבנו את המפתח הפרטי והציבורי – אני מזמין אותו לקרוא את המאמר הבהיר והיפה ב-hackernoon שממנו לקחתי את הדוגמה.

אבל איך זה עובד בפועל? עם קוד? בואו ונדגים, כרגיל, עם JS\node ומי שלא יודע – לא להבהל, התייחסו אל זה בתור פסאודו קוד.

ראשית, עלינו לייצר מפתח פרטי ומפתח ציבורי. זה די קל! בין אם אתם משתמשים בחלונות, לינוקס או מק. פותחים טרמינל (בחלונות זה cmd) ומקלידים:

ssh-keygen -t rsa -b 4096 -C "[email protected]" -f app/example_id_rsa
הצעד הראשון בהתקנת מפתח SSH
הצעד הראשון בהתקנת מפתח SSH

כיוון שאנחנו עובדים עם node, אנחנו צריכים להמיר את המפתח הציבורי לפורמט pem (זה פשוט פורמט אחר של מפתח). גם זה קל מאוד:

ssh-keygen -f app/example_id_rsa -e -m pem > app/example_id_rsa.pub

עכשיו, התוכנה עצמה – שימו לב כמה זה קל!

const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const RSAPath = path.resolve('./app/example_id_rsa.pub');
const RSAPrivtaePath = path.resolve('./app/example_id_rsa');
const publicKey = fs.readFileSync(RSAPath, 'utf8');
const privateKey = fs.readFileSync(RSAPrivtaePath, 'utf8');


function encrypt(text) {
    const buffer = Buffer.from(text);
    const encrypted = crypto.publicEncrypt(publicKey, buffer);
    return encrypted.toString('base64');
};

function decrypt(encryptedText) {
    const buffer = Buffer.from(encryptedText, 'base64');
    const decrypted = crypto.privateDecrypt(privateKey, buffer);
    return decrypted.toString('utf8');
};


const encryptedText = encrypt('I am super secret');
const decryptedText = decrypt(encryptedText);

console.log(encryptedText);
console.log(decryptedText);

בגדול? בדיוק כמו הצפנה סימטרית! רק שפה אנו עובדים עם שני מפתחות. ציבורי – על מנת להצפין את המידע ופרטי – על מנת לפענח את המידע. הכי פשוט בעולם! את המפתח הציבורי אנחנו יכולים לתת לכל אחד ללא שום חשש. כל אחד יכול להשתמש בו על מנת להצפין מידע ולשלוח לנו. אבל רק למי שיש את המפתח הפרטי יש את היכולת לפענח את ההצפנה. זו הסיבה שמפתח פרטי צריך להשאר… ובכן, פרטי.

למי שרוצה לנסות, יצרתי ריפו קטן בגיטהאב שמאפשר לכל אחד להתנסות. הורידו אותו, יצרו את המפתחות (בקונסולה של לינוקס/מק או בקונסולה דמוית לינוקס בחלונות כמו git CMD או docker CLI שניתנו להתקנה בקלות על כל חלונות ואם אין לכם אותן אז כדאי שיהיה). בהרצה תוכלו לראות את התוכן המוצפן ואת התוכן הלא מוצפן.

פוסטים נוספים שכדאי לקרוא

גלילה לראש העמוד