본문 바로가기

개발이야기

PGP sign and encrypt

반응형

What is PGP ?
'Pretty Good Privacy'의 약자로서, 컴퓨터 파일을 암호화하고 복호화하는 프로그램이다.
1991년 필립 짐머만이 개발하였으며, 현재 전 세계적으로 이메일 보안의 표준으로 자리잡았다.

나무위키의 PGP 역사가 재미납니다. https://namu.wiki/w/PGP )
여담으로 1997년 PGP 국제버전인 PGPi 5.0이 개발되었고, 필 지머먼은 이를 국외로 수출하려고 했으나 당시 미국 정부는 암호화 프로그램을 무기로 간주하여 이를 저지한다. 이에 지머먼은 법률을 검토하던 중, 소프트웨어는 수출 금지 항목에 포함되어 있어도, 종이에 인쇄된 책은 수출에 제약이 없다는 점을 발견했다. 법의 허점이라기 보다는, 미국에서는 강력한 수정헌법 1조 덕에 출판의 자유가 보장되기 때문. 그리하여 총 6천여 페이지의 소스코드를 출력하여 12권의 책으로 묶었고 ...

 
OpenPGP integration in Java and Javascript

https://github.com/codesandnotes/openpgp-integration

 

codesandnotes/openpgp-integration

Tutorial code to work with OpenPGP in Java and JavaScript, and exchanging PGP messages between them - codesandnotes/openpgp-integration

github.com

위의 저장소의 샘플 코드가 아주 잘되어 있습니다. 

절차를 간단하게 요약 하자면 ..  
1. public/private 키 생성 
2. keyRing 생성 
3. 암복호화 및 서명검증 


1. public /private 키 생성 하기
아래 함수의 인자값 중에 패스프레이즈(passphrase) 는 private 키를 보호하기 위해 사용되는 일종의 비밀번호임.
일반적으로 유추하기 힘든 문자로 하고 (20자 이상) private 키와 같은 곳에 보관하지 않는다. 

public ArmoredKeyPair generateKeys(int keySize, String userIdName, String userIdEmail, String passphrase) throws PGPException {

        Date now = new Date();

        RSAKeyPairGenerator keyPairGenerator = keyPairGenerator(keySize);

        PGPKeyPair encryptionKeyPair = encryptionKeyPair(now, keyPairGenerator);
        PGPSignatureSubpacketVector encryptionKeySignature = encryptionKeySignature();

        PGPKeyPair signingKeyPair = signingKeyPair(keyPairGenerator, now);
        PGPSignatureSubpacketVector signingKeySignature = signingKeySignature();

        PGPKeyRingGenerator keyRingGenerator = new PGPKeyRingGenerator(
                PGPSignature.POSITIVE_CERTIFICATION,
                signingKeyPair,
                userIdName + " <" + userIdEmail + ">",
                new BcPGPDigestCalculatorProvider().get(HashAlgorithmTags.SHA1),
                signingKeySignature,
                null,
                new BcPGPContentSignerBuilder(signingKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1),
                secretKeyEncryptor(passphrase)
        );
        keyRingGenerator.addSubKey(encryptionKeyPair, encryptionKeySignature, null);

        try {
            return ArmoredKeyPair.of(
                    generateArmoredSecretKeyRing(keyRingGenerator),
                    generateArmoredPublicKeyRing(keyRingGenerator));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

 

2. keyRing 구하기 
상대측에서 발행한 공개키와 우리쪽 keyPair로 keyRing을 구한다. (열쇠 묶음 정도로 이해함)
키링을 구할 때도 패스프레이즈가 사용됩니다. 

private InMemoryKeyring keyring(String passphrase,
                                    ArmoredKeyPair armoredKeyPair,
                                    String... recipientsArmoredPublicKey)
            throws IOException, PGPException {
        InMemoryKeyring keyring = KeyringConfigs.forGpgExportedKeys(KeyringConfigCallbacks.withPassword(passphrase));
        keyring.addSecretKey(armoredKeyPair.privateKey().getBytes(UTF_8));
        keyring.addPublicKey(armoredKeyPair.publicKey().getBytes(UTF_8));
        for (String recipientArmoredPublicKey : recipientsArmoredPublicKey) {
            keyring.addPublicKey(recipientArmoredPublicKey.getBytes(UTF_8));
        }
        return keyring;
    }

 

3. encrypt and sign 
사실 이 과정에서 위의 2번 keyRing 메소드롤 호출함.  
decode and verify 과정도 거의 흡사함 

public String encryptAndSign(String unencryptedMessage,
                                 String senderUserIdEmail,
                                 String senderPassphrase,
                                 ArmoredKeyPair senderArmoredKeyPair,
                                 String receiverUserId,
                                 String receiverArmoredPublicKey)
            throws IOException, PGPException, NoSuchAlgorithmException, SignatureException, NoSuchProviderException {

        InMemoryKeyring keyring = keyring(senderPassphrase, senderArmoredKeyPair, receiverArmoredPublicKey);

        ByteArrayOutputStream encryptedOutputStream = new ByteArrayOutputStream();
        try (
                BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(encryptedOutputStream);
                OutputStream bouncyGPGOutputStream = BouncyGPG.encryptToStream()
                        .withConfig(keyring)
                        .withStrongAlgorithms()
                        .toRecipient(receiverUserId)
                        .andSignWith(senderUserIdEmail)
                        .armorAsciiOutput()
                        .andWriteTo(bufferedOutputStream)
        ) {
            Streams.pipeAll(new ByteArrayInputStream(unencryptedMessage.getBytes()), bouncyGPGOutputStream);
        }

        return encryptedOutputStream.toString(UTF_8.name());
    }
반응형