EST. 2026 — บันทึกงานเซิร์ฟเวอร์จริง SYSADMIN · SECURITY · AI · DIRECTADMIN เล่มที่ 01
THE SYSADMIN ALMANAC
หน้าหลัก / Linux / สร้าง .ppk ให้ plink/pscp บน Windows โดยไม่ต้องเปิด puttygen
Linux

สร้าง .ppk ให้ plink/pscp บน Windows โดยไม่ต้องเปิด puttygen

โดย Ton-Or ·15 ก.ค. 2026·อ่าน 7 นาที
สร้าง .ppk ให้ plink/pscp บน Windows โดยไม่ต้องเปิด puttygen

งาน automation ที่ต้องยิง plink/pscp จากสคริปต์ มักติดตรงต้องมี .ppk แต่ puttygen บน Windows แปลงจาก command line ไม่ได้ ทางออกคือเขียนไฟล์ PPK เองซึ่งไม่ยากเมื่อรู้ว่า MAC คำนวณจากอะไร

พอดีผมทำสคริปต์ deploy ที่ต้องยิง plink/pscp ขึ้นเซิร์ฟเวอร์อัตโนมัติจากเครื่อง Windows แล้วติดปัญหาเดิม ๆ คือมันต้องใช้ private key เป็นรูปแบบ .ppk ของ PuTTY ไม่กินไฟล์ OpenSSH ธรรมดา ปกติก็เปิด puttygen แปลงเอา แต่พองานเป็น automation ที่ต้อง generate key ใหม่เองในสคริปต์ เปิด GUI ทีละครั้งไม่ไหว

ลองสั่ง puttygen แบบ batch ดู เจอกำแพงทันที

puttygen id_ed25519 -O private -o out.ppk
# เด้ง dialog: unrecognised option "-O"
ข้อจำกัดของ puttygen บน Windows

puttygen.exe เวอร์ชัน Windows เป็น GUI-only — flag -O/-o ที่มีในฝั่ง Linux ถูกตัดออก สั่งจาก command line ไม่ได้เลย จะ automate การแปลง/สร้าง .ppk ด้วยตัวมันเองไม่ได้

ทางเลือกที่ใช้ได้จริง

มีสองทาง — ทางลัดถ้ามี WSL และทางเขียนเองถ้าไม่อยากพึ่งอะไร

มี WSL อยู่แล้ว

ใช้ putty-tools ฝั่ง Linux

ฝั่ง Ubuntu ใน WSL ตัว puttygen ตัวจริงมี CLI ครบ แปลงได้ในบรรทัดเดียว เหมาะถ้าไม่ซีเรียสเรื่อง dependency

อยากได้ pure Python

เขียนไฟล์ PPK v3 เอง

ไม่ต้องลงอะไรเพิ่ม รู้สูตร MAC แล้วประกอบไฟล์ได้เลย พกไปเครื่องไหนก็รัน เหมาะกับ automation

ทางลัด WSL:

sudo apt install putty-tools
puttygen id_ed25519 -O private -o out.ppk

เขียนไฟล์ PPK v3 เอง

ไฟล์ PPK เป็น text format ตรงไปตรงมา ส่วนที่หลอนที่สุดคือ MAC ท้ายไฟล์ ที่ต้องคำนวณให้ถูกไม่งั้น plink ปฏิเสธ key

PPK v3 MAC = HMAC-SHA-256 คำนวณจากการต่อ SSH-string ของ 5 อย่างเรียงกัน: ชื่อ algorithm, คำว่า encryption ("none" ถ้าไม่ตั้งรหัส), comment, public blob, private blob โดย SSH-string คือ "ความยาว 4 ไบต์ big-endian + ตัวข้อมูล" และคีย์ของ HMAC เป็น string ว่างเมื่อ key ไม่ได้เข้ารหัส

mac_data คือ SSH-string 5 ท่อนต่อกันตามลำดับท่อนที่ 1ssh-ed25519len 4B + dataท่อนที่ 2nonelen 4B + dataท่อนที่ 3commentlen 4B + dataท่อนที่ 4public bloblen 4B + dataท่อนที่ 5private bloblen 4B + dataHMAC-SHA-256คีย์ HMACstring ว่าง เมื่อไม่ตั้งรหัสPrivate-MAChex ท้ายไฟล์ .ppkขาดท่อนใดหรือสลับลำดับ plink จะเด้ง Unable to load key file
PPK v3 ที่ไม่เข้ารหัส คำนวณ MAC จาก 5 ท่อนต่อกัน โดยแต่ละท่อนคือความยาว 4 ไบต์ตามด้วยข้อมูล ส่วนคีย์ของ HMAC เป็นสตริงว่าง
import base64, hashlib, hmac, struct, sys

def ssh_string(b: bytes) -> bytes:
    return struct.pack('>I', len(b)) + b

def ed25519_ppk(openssh_priv_path, out_path, comment='imported'):
    # ---- ดึง seed 32 ไบต์ + public 32 ไบต์ จากไฟล์ OpenSSH ที่ decode แล้ว ----
    # (ในของจริงผม parse ตัว "-----BEGIN OPENSSH PRIVATE KEY-----" ออกมาเป็น pub/seed)
    pub, seed = load_openssh_ed25519(openssh_priv_path)   # bytes(32), bytes(32)
    alg = b'ssh-ed25519'
    public_blob  = ssh_string(alg) + ssh_string(pub)
    private_blob = ssh_string(seed)                        # ed25519: seed 32 ไบต์แรก

    pub_b64  = base64.b64encode(public_blob).decode()
    priv_b64 = base64.b64encode(private_blob).decode()

    mac_data = (ssh_string(alg) + ssh_string(b'none') +
                ssh_string(comment.encode()) +
                ssh_string(public_blob) + ssh_string(private_blob))
    mac = hmac.new(b'', mac_data, hashlib.sha256).hexdigest()

    def wrap(b64, per=64):
        return [b64[i:i+per] for i in range(0, len(b64), per)]

    lines = [f'PuTTY-User-Key-File-3: {alg.decode()}',
             'Encryption: none', f'Comment: {comment}',
             f'Public-Lines: {len(wrap(pub_b64))}', *wrap(pub_b64),
             f'Private-Lines: {len(wrap(priv_b64))}', *wrap(priv_b64),
             f'Private-MAC: {mac}']
    open(out_path, 'w', newline='\r\n').write('\n'.join(lines) + '\n')
    print('wrote', out_path)
จุดที่ต้องระวังกับ ed25519

private blob ของ ed25519 คือ seed 32 ไบต์แรก ของ field 64 ไบต์ในไฟล์ OpenSSH (32 ไบต์หลังเป็น public ซ้ำ) เอามาผิดช่วง key จะใช้ไม่ได้ทั้งที่ไฟล์ดู "ถูก"

เทสว่าใช้ได้จริง

อย่าเชื่อว่าไฟล์ถูกจนกว่า plink จะยอม authenticate จริง:

plink -i out.ppk -batch user@host "echo OK; id"
# ต้องได้ OK + uid ของ user กลับมา ไม่ใช่ prompt ถามรหัส

ถ้า plink เด้ง "Unable to load key file" แปลว่า MAC หรือ blob ผิด — กลับไปเช็คว่าต่อ SSH-string ครบ 5 ท่อนตามลำดับไหม และคีย์ HMAC เป็น string ว่างจริงไหม

สรุปคือปัญหาไม่ได้อยู่ที่ตัว key แต่อยู่ที่ puttygen บน Windows ที่ automate ไม่ได้ พอเข้าใจว่า PPK v3 แค่ต้องการ MAC ที่ถูกสูตร ก็ generate เองในสคริปต์ได้หมด งาน deploy อัตโนมัติที่ต้องพก key เลยไม่ต้องมีขั้นตอนเปิด GUI คั่นอีก ใครทำ automation ฝั่ง Windows→Linux เอาไปปรับใช้ได้ตามสะดวกครับ ตรงไหนผมอธิบายตกหล่นหรือพิมพ์ผิด เดี๋ยวกลับมาแก้เพิ่มให้ภายหลังครับ

อยากให้เราทำให้ไหม?

ให้เราช่วยติดตั้ง ตั้งค่า ย้ายระบบ หรือแก้ปัญหาเซิร์ฟเวอร์ เพื่อให้คุณมีเวลาไปทำงานที่สำคัญกว่า

คุยเรื่องงานกับเรา

เคยเจองานแบบนี้ไหม? ติดตรงไหน?

มาแลกวิธีกันต่อในบอร์ด LinuxThai

→ คุยต่อในบอร์ด

ความคิดเห็น 0

ยังไม่มีความคิดเห็น — เป็นคนแรกเลย