java library를 이용한 DKIM 서명 및 검증 기능 구현하기
java에서는 Email의 DKIM 서명 및 검증 처리를 위한 몇 가지 라이브러리를 제공한다.
이번 포스팅에서는 Email DKIM 서명 및 검증 구현을 위한 다음 라이브러리를 사용하는 방법에 대해서 기록한다.
- apache james 프로젝트의 jdkim 라이브러리
- SimpleJavaMail dkim 라이브러리
Email DKIM에 대한 상세 스펙에 대한 내용은 아래 포스팅을 참고하기 바란다.
2023.11.08 - [ETC] - Email DKIM(DomainKeys Identified Mail) 메시지 서명 및 검증 프로세스
우선 Email DKIM 기능을 제공하기 위해서는 RSA 개인키/공개키가 필요하며 공개키는 DNS의 TXT 레코드에 등록이 되어야 한다.
테스트를 위해서 실제 DNS에 공개키를 등록하지는 않을 것이다. 공개키를 코드 내에서 가져오도록 하여 검증 테스트는 진행할 것이다.
apache james 의 jdkim 라이브러리는 서명 및 검증을 위한 코드를 제공한다.
SimpleJavaMail dkim 라이브러리는 서명을 위한 코드를 제공한다. 또한 DKIM-Signature 헤더를 붙여 메일을 전송할 수도 있다.
public / private key 생성
DKIM 기능 제공을 위한 환경 구성을 위해서는 기본적으로 공개키(검증)/개인키(서명)가 필요하다.
$> openssl genrsa -out rsa.private 1024
위 명령은 1024 비트의 PEM 형식의 PKCS1 개인키를 생성한다.
공개키는 다음과 같이 생성한다.
$> openssl rsa -in rsa.private -out rsa.public -pubout -outform PEM
java security 패키지는 PKCS1을 지원하지 않기 때문에 java security를 사용하여 개인키를 로딩하기 위해서는 PKCS8로 변환이 필요하다. PKCS1 -> PKCS8 변환 명령은 다음과 같다.
$> openssl pkey -in rsa.private -out pkcs8.rsa.private
위 명령은 PKCS8 개인키를 pkcs8.rsa.private 파일에 저장한다.
DKIM 서명 및 검증을 위한 샘플 코드 프로젝트의 key 파일 경로는 위와 같다.
pkcs8.rsa.private 파일은 PKCS8 개인키 파일이다.
rsa.private 파일은 PKCS1 개인키 파일이다.
PKCS1, PKCS8 둘 중 하나를 선택해서 사용하면 되겠다.
rsa.public 파일은 공개키 파일이다.
만약 PKCS1 개인키를 로딩하려면 BouncyCastle 라이브러리를 사용한다.
디펜던시는 다음과 같다.
<!-- PKCS1 private key를 로딩하기 위한 라이브러리 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.69</version>
</dependency>
우선 bouncycastle 디펜던시 추가가 필요하다. PKCS1 개인키를 로딩하는 코드는 다음과 같다.
//PKCS1 유형의 private key를 loading 하기 위해서 BouncyCastle 라이브러리 사용
//java는 PKCS1을 지원하지 않는다.
//https://www.baeldung.com/java-read-pem-file-keys#2-read-private-key
//혹은 PKCS1 -> PKCS8로 변환 후
public static RSAPrivateKey loadPrivateKeyPKCS1(Path keyPath) throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {
Security.addProvider(new BouncyCastleProvider());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
try (FileReader keyReader = new FileReader(keyPath.toFile());
PemReader pemReader = new PemReader(keyReader)) {
PemObject pemObject = pemReader.readPemObject();
byte[] content = pemObject.getContent();
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(content);
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
}
}
이번 포스팅에서 구현하는 샘플은 PKCS8 개인키를 사용한다.
샘플 Email
테스트를 위한 샘플 EML 파일 위치는 다음과 같다.
src/main/resources/sample.eml
라이브러리 dependency
apache james jdkim
<!-- apache james jdkim 라이브러리 -->
<dependency>
<groupId>org.apache.james.jdkim</groupId>
<artifactId>apache-jdkim</artifactId>
<version>0.3</version>
</dependency>
simplejavamail dkim module
<!-- simplejavamail의 dkim 라이브러리 -->
<dependency>
<groupId>org.simplejavamail</groupId>
<artifactId>utils-mail-dkim</artifactId>
<version>3.0.0</version>
</dependency>
Email 메시지 서명하기 (DKIM-Signature 헤더 생성하기)
샘플 코드에서 메시지 서명에 사용되는 필수 태그는 다음과 같다.
- a=rsa-sha256
- s=selector
- c=simple/simple
- d=test.com
- h=Content-Type:MIME-Version:Subject:Message-ID:To:Sender:From:Date
Key Loader 클래스
개인키 파일 로딩을 위해 사용되는 공통 클래스다.
package com.example.spring.dkim.common;
import org.apache.james.jdkim.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
public class RSAKeyLoader {
//PKCS1 private key를 loading 하기 위해서 BouncyCastle 라이브러리 사용
//java는 PKCS1을 지원하지 않는다.
//https://www.baeldung.com/java-read-pem-file-keys#2-read-private-key
public static RSAPrivateKey loadPrivateKeyPKCS1(Path keyPath) throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {
Security.addProvider(new BouncyCastleProvider());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
try (FileReader keyReader = new FileReader(keyPath.toFile());
PemReader pemReader = new PemReader(keyReader)) {
PemObject pemObject = pemReader.readPemObject();
byte[] content = pemObject.getContent();
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(content);
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
}
}
//PKCS8 private key를 loading
public static RSAPrivateKey loadPrivateKeyPKCS8(Path keyPath) throws InvalidKeySpecException, NoSuchAlgorithmException, IOException {
String privateKeyData = Files.readString(keyPath, Charset.defaultCharset());
String privateKeyPEM = privateKeyData
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "");
byte[] encoded = Base64.decodeBase64(privateKeyPEM);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
}
}
apache james 라이브러리
다음은 apache james 라이브러리를 이용하여 DKIM-Signature를 생성하는 클래스다.
package com.example.spring.dkim.sign;
import com.example.spring.dkim.common.RSAKeyLoader;
import jakarta.mail.MessagingException;
import lombok.extern.slf4j.Slf4j;
import org.apache.james.jdkim.DKIMSigner;
import org.apache.james.jdkim.exceptions.FailException;
import org.springframework.core.io.ClassPathResource;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
@Slf4j
public class JamesDKIMSign {
public JamesDKIMSign() {}
public void makeDkimSignMessage(Path originEml, Path outputEml) throws IOException, InvalidKeySpecException, NoSuchAlgorithmException, FailException, MessagingException {
//DKIM-Signature header tag 셋팅
String signatureTemplate =
"v=1; a=rsa-sha256; s=selector; c=simple/simple; d=test.com; " +
"h=Content-Type:MIME-Version:Subject:Message-ID:To:Sender:From:Date; bh=; b=";
//PKCS8 개인키를 로딩한다.
ClassPathResource privateKeyResource = new ClassPathResource("keys/pkcs8.rsa.private");
PrivateKey privateKey = RSAKeyLoader.loadPrivateKeyPKCS8(privateKeyResource.getFile().toPath());
//PKCS1 개인키를 로딩하려면 아래 메서드를 호출한다.
// ClassPathResource privateKeyResource = new ClassPathResource("keys/rsa.private");
// PrivateKey privateKey = RSAKeyLoader.loadPrivateKeyPKCS1(privateKeyResource.getFile().toPath());
DKIMSigner signer = new DKIMSigner(signatureTemplate, privateKey);
String sign;
try (InputStream inputStream = Files.newInputStream(originEml)) {
//sign() 메서드 호출을 통해서 DKIM-Signature 헤더를 얻을 수 있다.
//sign() 호출 이후 inputStream은 close 된다.
sign = signer.sign(inputStream);
log.info("{}", sign);
sign += "\r\n";
}
//outputEml 에 DKIM-Signature 헤더를 추가하여 originEml을 복사한다.
try (InputStream inputStream = Files.newInputStream(originEml);
OutputStream outputStream = Files.newOutputStream(outputEml)) {
outputStream.write(sign.getBytes(Charset.defaultCharset()));
outputStream.write(inputStream.readAllBytes());
}
}
}
//target/classes/sample.eml 파일에 대한 DKIM-Signature 헤더를 생성하여
//DKIM-Signature 헤더가 삽입된 target/classes/sample-james-dkim.eml 파일을 생성한다.
Path jamesSign() {
try {
JamesDKIMSign jamesDKIMSign = new JamesDKIMSign();
ClassPathResource resource = new ClassPathResource("sample.eml");
Path originEmlPath = resource.getFile().toPath();
Path outputEmlPath = originEmlPath.resolveSibling("sample-james-dkim.eml");
jamesDKIMSign.makeDkimSignMessage(originEmlPath, outputEmlPath);
return outputEmlPath;
} catch (Exception e) {
log.error("fail to dkim sign by james library, {}", e.getMessage());
return null;
}
}
- apache james 라이브러리의 경우 메시지 서명을 위해서는 우선 signatureTemplate를 미리 정의해야 한다.
- signatureTemplate에서 b=, bh= 태그는 모두 공백문자로 세팅해야 한다.
- PrivateKey 인스턴스를 전달해야 한다.
- PrivateKey 인스턴스는 src/main/resources/keys/pkcs8.rsa.private 파일로부터 생성된다.
위 코드에서 DKIMSigner.sign() 메서드를 호출하여 얻은 DKIM-Signature 헤더는 다음과 같다.
DKIM-Signature: a=rsa-sha256; b=W0kFoDrivKbcY3xWfMWg2Z2YogyFy7e/rilBh5CBMDZwbbi/jNKbnC/yvlgyceYexk0jVRIoB0N2BYVB3j9P4K07XOS79TKdEVvyjmckN8UdnVS7338EezB8t6v/O3bNVRzSt5YsJF1RZXTUE2LJfV/eBaIqlOoo7DetgtCXZqQ=; s=selector; c=simple/simple; d=test.com; v=1; bh=QUg010Sn3BHY0WzkYjKB6QDkD5XZxhSEsiPcC0PyX0E=; h=Content-Type:MIME-Version:Subject:Message-ID:To:Sender:From:Date;
하지만 다음과 같은 문제가 있다.
- james jdkim 라이브러리를 통해서 얻은 DKIM-Signature 헤더는 folding이 되지 않는다.
- Email 메시지 헤더의 한 라인 길이의 제한이 있기 때문에 이를 그대로 사용하기에는 무리가 있다.
SimpleJavaMail 라이브러리
다음은 SimpleJavaMail 라이브러리를 이용하여 DKIM-Signature를 생성하는 클래스다.
package com.example.spring.dkim.sign;
import com.example.spring.dkim.common.RSAKeyLoader;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import org.simplejavamail.utils.mail.dkim.Canonicalization;
import org.simplejavamail.utils.mail.dkim.DkimMessage;
import org.simplejavamail.utils.mail.dkim.DkimSigner;
import org.simplejavamail.utils.mail.dkim.SigningAlgorithm;
import org.springframework.core.io.ClassPathResource;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
public class SimpleJavaDKIMSign {
/**
* @param originEml DKIM 서명하고자 하는 EML 경로
* @param outputEml DKIM 서명 헤더를 붙인 새로운 EML 경로
*/
public void makeDkimSignMessage(Path originEml, Path outputEml) throws IOException, MessagingException, InvalidKeySpecException, NoSuchAlgorithmException {
try (InputStream inputStream = Files.newInputStream(originEml);
OutputStream outputStream = Files.newOutputStream(outputEml)) {
MimeMessage originMessage = new MimeMessage(null, inputStream);
DkimMessage dkimMessage = getDkimMessage(originMessage);
dkimMessage.writeTo(outputStream);
}
//check exists DKIM-Signature header
try (InputStream inputStream = Files.newInputStream(outputEml)) {
MimeMessage outputMessage = new MimeMessage(null, inputStream);
String[] header = outputMessage.getHeader("DKIM-Signature");
assert header.length > 0;
}
}
private DkimMessage getDkimMessage(MimeMessage message) throws InvalidKeySpecException, NoSuchAlgorithmException, IOException, MessagingException {
final String domain = "test.com";
final String selector = "selector";
ClassPathResource privateKeyResource = new ClassPathResource("keys/pkcs8.rsa.private");
RSAPrivateKey privateKey = RSAKeyLoader.loadPrivateKeyPKCS8(privateKeyResource.getFile().toPath());
DkimSigner dkimSigner = new DkimSigner(domain, selector, privateKey);
dkimSigner.setHeaderCanonicalization(Canonicalization.SIMPLE);
dkimSigner.setBodyCanonicalization(Canonicalization.SIMPLE);
dkimSigner.setSigningAlgorithm(SigningAlgorithm.SHA256_WITH_RSA);
//지정된 도메인 및 지정된 selector에 대한 DNS 리소스 레코드가 올바르게 준비되었는지 확인하는 메서드다.
//테스트를 위해서 실제로 DNS 구성을 하지 않았기 때문에 false로 지정한다.
//리얼 환경에서는 아래 코드는 지정하지 않는 것이 좋다.
dkimSigner.setCheckDomainKey(false);
return new DkimMessage(message, dkimSigner);
}
}
//target/classes/sample.eml 파일에 대한 DKIM-Signature 헤더를 생성하여
//DKIM-Signature 헤더가 삽입된 target/classes/sample-simple-javamail-dkim.eml 파일을 생성한다.
Path simpleJavaMailSign() {
try {
SimpleJavaDKIMSign simpleJavaDKIMSign = new SimpleJavaDKIMSign();
ClassPathResource resource = new ClassPathResource("sample.eml");
Path originEmlPath = resource.getFile().toPath();
Path outputEmlPath = originEmlPath.resolveSibling("sample-simple-javamail-dkim.eml");
simpleJavaDKIMSign.makeDkimSignMessage(originEmlPath, outputEmlPath);
return outputEmlPath;
} catch (Exception e) {
log.error("fail to dkim sign by simple java mail library, {}", e.getMessage());
return null;
}
}
SimpleJavaMail 라이브러리를 사용하여 DKIM-Signature 헤더를 생성하는 이점은 다음과 같다.
- DKIM-Signature 헤더에 대한 folding이 자동 처리된다.
- 원문 EML에 대한 DKIM-Signature 헤더가 삽입된 새로운 EML을 생성하는 메서드를 제공한다. (DkimMessage.writeTo())
- signatureTemplate를 지정할 필요 없이 메서드를 통해 태그를 지정할 수 있다.
- 샘플코드에서는 비활성화 하였지만 공개키 조회를 위한 DNS 구성을 확인할 수 있는 메서드를 제공한다. (setCheckDomainKey(true))
생성된 DKIM-Signature 헤더는 다음과 같다.
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=simple/simple; t=1589272539;
s=selector; d=test.com;
h=Content-Type:MIME-Version:Subject:Message-ID:To:Sender:From:Date;
bh=QUg010Sn3BHY0WzkYjKB6QDkD5XZxhSEsiPcC0PyX0E=;
b=ZssTrtszInGe9heQjn5dSirqtjOOgcB80AgGpxa6Xzu8+7hyB+3St5Hw4Lfp7rtT
kjXwalwuT+qK0Bi0Tvb6l5t6Y2NFtP8MDsFCjqHdDxdkz0N7b8NRA9hYBWZlwMSRk+J
BNoOZNAiTv/yURjAucDxmgeyc6tz2+zNFGmdzhWo=
DKIM-Signature 헤더의 folding 처리가 잘 된 것을 확인할 수 있다.
DKIM 서명 검증하기 (DKIM-Signature 헤더 검증)
두개의 라이브러리 중에서 DKIM-Signature 헤더 검증 기능 제공은 apache james jdkim 라이브러리에서만 제공한다.
DKIMVerifier.verify() 메서드를 통해서 검증 기능을 제공하며 PublicKeyRecordRetriever 구현체를 생성자에 전달하지 않으면 기본적으로 DNSPublicKeyRecordRetriever 인스턴스가 사용되는데 DNSPublicKeyRecordRetriever 인스턴스는 DKIM-Signature 헤더의 s=(selector), d=(domain)을 기반으로 DNS에 등록된 공개키를 얻어와서 검증을 수행한다.
PublicKeyRecordRetriever 구현체 인스턴스를 생성자에 전달하면 공개키는 getRecords() 메서드를 통해서 얻어오게 된다.
PublicKeyRecordRetriever 인터페이스는 다음과 같다.
public interface PublicKeyRecordRetriever {
/**
* @param methodAndOption
* the options declared for the lookup method.
* @param selector
* the value of "s=" tag
* @param token
* the value of the "d=" tag
* @return A list of strings representing 0 to multiple records
* @throws TempFailException
* in case of timeout and other network errors.
* @throws PermFailException
* in case of unsupported options
*/
public List<String> getRecords(CharSequence methodAndOption,
CharSequence selector, CharSequence token)
throws TempFailException, PermFailException;
}
메시지 서명 및 검증을 위한 테스트로써 실제 DNS에 공개키를 등록하지 않았기 때문에 PublicKeyRecordRetriever를 구현한 구현체를 직접 정의하여 getRecords() 메서드에 공개키를 하드코딩하여 리턴하도록 할 것이다.
다음 클래스는 공개키를 DNS 조회없이 리턴하도록 하는 클래스다.
package com.example.spring.dkim.verify;
import org.apache.james.jdkim.api.PublicKeyRecordRetriever;
import org.apache.james.jdkim.exceptions.PermFailException;
import org.apache.james.jdkim.exceptions.TempFailException;
import java.util.List;
/**
* PublicKeyRecordRetriever 클래스는 apache james의 jdkim 라이브러리에서 제공하는 인터페이스다.
* 서명 검증시 내부적으로 DKIM-Signature 헤더의 selector, domain 정보를 기반으로 DNS 조회를 통해서 공개키를 가져오는데
* DNS 조회를 통해서 공개키를 가져오는 역할은 PublicKeyRecordRetriever 구현체가 담당한다.
* 검증 테스트를 위해서 DNS에 공개키를 등록하지 않았기 때문에 하드코딩된 공개키를 가져오도록 한다.
* PublicKeyRecordRetriever4Test 클래스는 하드코딩된 공개키 정보를 가져오도록 하기 위한 장치다.
* p= 태그에 BEGIN PUBLIC KEY, END PUBLIC KEY 라인을 제외한
* resource/keys/rsa.public 파일의 공개키 내용을 직접 셋팅하도록 한다.
*/
public class PublicKeyRecordRetriever4Test implements PublicKeyRecordRetriever {
@Override
public List<String> getRecords(
CharSequence methodAndOption,
CharSequence selector,
CharSequence token) {
//example
//String publicKeyRecord = "v=DKIM1; k=rsa; " +
// "p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjm1g0xmCM8eCveYP70SAdRNa1" +
// "2y0absnxuOcZy/hvSyQqNqS5hoFcfvn/tQCO4LrcccLoRSueMOOc6l0rHpRcHluX++t" +
// "FMbG7IkxcMT1Rk/vnBQ31GeTfeEbR4y/hBMdU5DDvRsvkSXXVlc9Us7m8te6hPFQYxtABCDEFGHIJKLMN";
String publicKeyRecord = "v=DKIM1; k=rsa; " +
"p=<공개키데이터>";
return List.of(publicKeyRecord);
}
}
PublicKeyRecordRetriever4 Test는 공개키가 DNS에 등록되어 있지 않기 때문에 테스트를 위해서 정의한 클래스 이므로 리얼 환경에서는 정의해서 사용하면 안 된다.
리얼 환경에서는 DKIMVerifier 인스턴스 생성 시 디폴트 생성자를 통해서 인스턴스를 생성하도록 한다.
다음은 DKIM-Signature 검증을 위한 클래스다.
package com.example.spring.dkim.verify;
import lombok.extern.slf4j.Slf4j;
import org.apache.james.jdkim.DKIMVerifier;
import org.apache.james.jdkim.api.PublicKeyRecordRetriever;
import org.apache.james.jdkim.api.SignatureRecord;
import org.apache.james.jdkim.exceptions.FailException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
@Slf4j
public class JamesDKIMVerify {
public JamesDKIMVerify() {}
public void verify(Path emlPath) throws IOException, FailException {
PublicKeyRecordRetriever publicKeyRecordRetriever = new PublicKeyRecordRetriever4Test();
try(InputStream inputStream = Files.newInputStream(emlPath)) {
//리얼 환경에서는 publicKeyRecordRetriever 전달없이 디폴트 생성자를 호출한다.
DKIMVerifier verifier = new DKIMVerifier(publicKeyRecordRetriever);
List<SignatureRecord> verify = verifier.verify(inputStream);
verify.forEach(signatureRecord -> log.info("signature record: {}", signatureRecord));
}
}
}
/**
* apache james jdkim 라이브러리를 사용하여 파라미터로 전달된 경로의 EML 에 대한 DKIM 검증을 수행한다.
* @param emlPath DKIM-Signature 헤더가 삽입된 eml 파일 경로
*/
void verify(Path emlPath) {
try {
JamesDKIMVerify jamesDKIMVerify = new JamesDKIMVerify();
jamesDKIMVerify.verify(emlPath);
} catch (Exception e) {
log.error("fail to verify, {}", e.getMessage());
}
}
지금까지 apache james jdkim 라이브러리와 SimpleJavaMail 라이브러리를 사용하여 Email DKIM 서명 및 검증을 위한 샘플 코드를 작성해 보았다.
결론
apache james jdkim을 사용하여 DKIM-Signature 헤더를 생성하면 몇 가지 문제가 있다.
- DKIM-Signature 헤더에 대해서 folding 처리가 되지 않기 때문에 헤더 라인 길이 제약에 문제가 생길 수 있다.
- 생성된 DKIM-Signature 헤더를 삽입한 EML을 생성할 수 있는 메서드를 제공하지 않아 이를 위해서는 직접 구현해야 한다.
- 미리 DKIM-Signature 헤더 템플릿을 정의해야 하며 사용함에 있어서 직관성이 떨어진다는 생각이 든다.
이에 반해 SimpleJavaMail 라이브러리를 사용하여 DKIM-Signature를 생성하면
- DKIM-Signature 헤더에 대한 folding 처리가 자동으로 수행된다.
- DKIM 공개키를 조회할 수 있는 DNS 구성이 제대로 되었는지 확인할 수 있는 기능을 제공한다.
- 생성된 DKIM-Signature 헤더를 삽입하여 새로운 EML 파일을 생성할 수 있도록 메서드를 제공한다.
- 사용에 있어서 조금 더 직관적이다.
- DKIM-Signature 헤더를 검증할 수 있는 기능을 제공하지 않는다.
DKIM 서명을 생성하는 코드는 SimpleJavaMail 라이브러리를 사용하고 검증을 위한 코드는 apache james jdkim 라이브러리를 사용하는 것이 좋을 듯하다.
전체 샘플 코드는 gitlab에서 확인할 수 있다.
https://gitlab.com/blog4031530/spring-dkim
참고링크
https://james.apache.org/jdkim/mailets/index.html
https://github.com/simple-java-mail/java-utils-mail-dkim
https://dkim.org/specs/rfc4871-dkimbase.html#creating-a-key