目次
1.環境
- Windows7 32bit
- Java1.8
2. OpenSSLのインストール
以下のOpenSSLのサイトにアクセスして、インストーラーをダウンロードしてインストールを完了させる。
3.今回の対象の暗号化アルゴリズム
3-1.【RSASSA-PKCS1-v1_5 using SHA-256(RS256)】
★コマンド
[秘密鍵]
openssl genrsa -out rs256.key 2048
[公開鍵]
openssl rsa -pubout < rs256.key > rs256.pub.key
pkcs8形式に変換
openssl pkcs8 -in rs256.key -out rs256.key.pkcs8 -topk8 -nocrypt
3-2.【HMAC using SHA-256(HS256)】
3-3.【ECDSA using P-256 and SHA-256(ES256)】
★コマンド
秘密鍵と公開鍵生成のもとになるペアファイルを作成する
openssl ecparam -genkey -name prime256v1 -noout -out es256-key-pair.key
[公開鍵]
openssl ec -in es256-key-pair.key -outform PEM -pubout -out es256.pub.key
[秘密鍵]
openssl ec -in es256-key-pair.key -outform PEM -out es256.key
pkcs8形式に変換
openssl pkcs8 -in es256.key -out es256.key.pkcs8 -topk8 -nocrypt
4. 暗号化アルゴリズムを用いた暗号化と検証のjavaプログラムサンプル
Algorithm.java
/** 署名アルゴリズム名 */
private String algName;
/** アルゴリズム種別 */
private String alg;
/**
* ロガー.
*/
private static Logger LOGGER = LoggerFactory.getLogger(Algorithm.class);
/**
* コンストラクタ(直接new不可)
* @param algName 署名アルゴリズム名
* @param alg アルゴリズム種別
*/
protected Algorithm(String algName, String alg) {
this.algName = algName;
this.alg = alg;
}
/**
* 署名オブジェクトの生成
* @param alg アルゴリズム種別
* @param service サービス
* @param clientId クライアントID
* @return 署名インスタンス
*/
public static Algorithm createSignatureObject(String alg, String sercret) {
Algorithm algorithm = null;
switch (alg) {
case "HS256":
LOGGER.debug("Generate the secret key (hash value) of HS 256 used for signature...");
//ハッシュ値生成に使用する値に変更する
byte secretKey = sercret.getBytes();
algorithm = Algorithm.HMAC256(secretKey);
break;
case "RS256":
LOGGER.debug("Read the secret key of RS 256...");
RSAPrivateKey rsaPrivateKey = null;
try {
rsaPrivateKey = (RSAPrivateKey)
KeyFactory.getInstance("RSA").generatePrivate(
new PKCS8EncodedKeySpec(readKey(★ファイルパス)));
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
LOGGER.debug(e.getMessage(), e);
}
algorithm = Algorithm.RSA256(rsaPrivateKey);
break;
case "ES256":
LOGGER.debug("Read the secret key of ES 256...");
ECPrivateKey esPrivateKey = null;
try {
esPrivateKey = (ECPrivateKey)
KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(
readKey(★ファイルパス)));
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
LOGGER.debug(e.getMessage(), e);
}
algorithm = Algorithm.ECDSA256(esPrivateKey);
break;
}
return algorithm;
}
/**
* 署名検証オブジェクトの生成
* @param alg アルゴリズム種別
* @param service サービス
* @param clientId クライアントID
* @return 署名検証インスタンス
*/
public static Algorithm createVerificationObject(String alg, String sercret){
Algorithm algorithm = null;
switch (alg) {
case "HS256":
LOGGER.debug("Generate the secret key (hash value) of HS 256 used for verification...");
byte secretKey = sercret.getBytes();
algorithm = Algorithm.HMAC256(secretKey);
break;
case "RS256":
LOGGER.debug("Read the public key of RS 256...");
RSAPublicKey rsaPublicKey = null;
try {
rsaPublicKey = (RSAPublicKey) KeyFactory.getInstance("RSA")
.generatePublic(new X509EncodedKeySpec(★ファイルパス)));
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
LOGGER.debug(e.getMessage(), e);
}
algorithm = Algorithm.RSA256(rsaPublicKey);
break;
case "ES256":
LOGGER.debug("Read the public key of ES 256...");
ECPublicKey esPublicKey = null;
try {
esPublicKey = (ECPublicKey) KeyFactory.getInstance("EC").generatePublic(new X509EncodedKeySpec(
readKey(★ファイルパス)));
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
LOGGER.debug(e.getMessage(), e);
}
algorithm = Algorithm.ECDSA256(esPublicKey);
break;
}
return algorithm;
}
/**
* アルゴリズム名を取得する
* @return algoName
*/
public String getAlgName() {
return algName;
}
/**
* アルゴリズム種別を取得する
* @return alg
*/
public String getAlg() {
return alg;
}
/**
* 署名を行う
* @param content 署名対象のByte配列
* @return
*/
public abstract byte sign(byte content);
/**
* 署名検証を行う
* @param content 署名検証対象のByte配列
* @param signature 署名検証に使用する署名
* @return
*/
public abstract boolean verify(byte content, byte signature);
/**
* @param key 鍵のByte配列
*/
private static Algorithm HMAC256(byte key) {
return new HmacAlgorithm("HS256", "HmacSHA256", key);
}
/**
* @param key 鍵のByte配列
*/
private static Algorithm RSA256(RSAKey key) {
return new RsaAlgorithm("RS256", "SHA256withRSA", key);
}
/**
* @param key 鍵のByte配列
*/
private static Algorithm ECDSA256(ECKey key) {
return new EcdsaAlgorithm("ES256", "SHA256withECDSA", key);
}
/**
* 鍵ファイルを読み込む
* @param fileName 読み込み対象ファイル名
* @return
*/
private static byte readKey(final String fileName) {
byte keyBytes = null;
BufferedReader br = null;
try {
InputStream keyStream = new FileInputStream(fileName);
LOGGER.debug("★keyStream -> " + keyStream);
br = new BufferedReader(new InputStreamReader(keyStream));
String line;
StringBuilder sb = new StringBuilder();
boolean isContents = false;
while ((line = br.readLine()) != null) {
if (line.matches("[-]+BEGIN[ A-Z]+[-]+")) {
//内容がある。内容存在フラグを立てておく。
isContents = true;
} else if (line.matches("[-]+END[ A-Z]+[-]+")) {
//内容の終了行であるため、ループを抜ける
break;
} else if (isContents) {
//読み込んだ内容を連結する
sb.append(line);
}
}
keyBytes = Base64.getDecoder().decode(sb.toString());
} catch (IOException e) {
LOGGER.debug(e.getMessage(), e);
} finally {
try {
br.close();
} catch (IOException e) {
LOGGER.debug(e.getMessage(), e);
}
}
return keyBytes;
}
}
EcdsaAlgorithm.java
/** EC鍵 */
private ECKey key;
/** ロガー*/
private static Logger LOGGER = LoggerFactory.getLogger(EcdsaAlgorithm.class);
/**
* コンストラクタ
* @param algName アルゴリズム名
* @param alg アルゴリズム種別
* @param key 鍵
*/
public EcdsaAlgorithm(String algName, String alg, ECKey key) {
super(algName, alg);
this.key = key;
}
@Override
public byte sign(byte contentByte) {
//*******************************************
//ECDSAで署名を行う
//*******************************************
byte encryptedContent = null;
if (key == null) {
LOGGER.debug("秘密鍵が存在しない");
return encryptedContent;
}
if (!(this.key instanceof PrivateKey)) {
return encryptedContent;
}
try {
Signature signature = Signature.getInstance(getAlg());
signature.initSign(
(PrivateKey) this.key
);
signature.update(contentByte);
encryptedContent = signature.sign();
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
LOGGER.debug(e.getMessage(), e);
}
return encryptedContent;
}
@Override
public boolean verify(byte contentByte, byte signatureByte) {
//*******************************************
//ECDSAで署名検証を行う
//*******************************************
boolean isValid = false;
if (key == null) {
LOGGER.debug("公開鍵が存在しない");
return isValid;
}
if (!(this.key instanceof PublicKey)) {
return isValid;
}
try {
Signature signature = Signature.getInstance(getAlg());
signature.initVerify(
(PublicKey) this.key
);
signature.update(contentByte);
isValid = signature.verify(signatureByte);
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
LOGGER.debug(e.getMessage(), e);
}
return isValid;
}
}
HmacAlgorithm.java
/** 鍵 */
private byte key;
/** ロガー*/
private static Logger LOGGER = LoggerFactory.getLogger(HmacAlgorithm.class);
/**
* コンストラクタ
* @param algName アルゴリズム名
* @param alg アルゴリズム種別
* @param key 鍵
*/
public HmacAlgorithm(String algName, String alg, byte key) {
super(algName, alg);
this.key = key;
}
@Override
public byte sign(byte contentByte) {
//*******************************************
//HMACで署名を行う
//*******************************************
byte encryptedContent = null;
if (key == null) {
LOGGER.debug("秘密鍵が存在しない");
return encryptedContent;
}
try {
mac.init(new SecretKeySpec(key, getAlg()));
encryptedContent = mac.doFinal(contentByte);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
LOGGER.debug(e.getMessage(), e);
}
return encryptedContent;
}
@Override
public boolean verify(byte contentByte, byte signatureByte) {
//*******************************************
//HMACで署名検証を行う
//*******************************************
boolean isValid = false;
if (key == null) {
LOGGER.debug("秘密鍵が存在しない");
return isValid;
}
try {
mac.init(new SecretKeySpec(key, getAlg()));
byte encryptedContent = mac.doFinal(contentByte);
isValid = MessageDigest.isEqual(encryptedContent, signatureByte);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
LOGGER.debug(e.getMessage(), e);
}
return isValid;
}
}
RsaAlgorithm.java
/** RSA鍵 */
private RSAKey key;
/** ロガー */
private static Logger LOGGER = LoggerFactory.getLogger(RsaAlgorithm.class);
/**
* コンストラクタ
* @param algName アルゴリズム名
* @param alg アルゴリズム種別
* @param key 鍵
*/
public RsaAlgorithm(String algName, String alg, RSAKey key) {
super(algName, alg);
this.key = key;
}
@Override
public byte sign(byte contentByte) throws IdpAppException {
//*******************************************
//RSAで署名を行う
//*******************************************
byte encryptedContent = null;
if (key == null) {
LOGGER.debug("秘密鍵が存在しない");
return encryptedContent;
}
if (!(this.key instanceof PrivateKey)) {
return encryptedContent;
}
try {
Signature signature = Signature.getInstance(getAlg());
signature.initSign((PrivateKey) this.key);
signature.update(contentByte);
encryptedContent = signature.sign();
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
LOGGER.debug(e.getMessage(), e);
}
return encryptedContent;
}
@Override
public boolean verify(byte contentByte, byte[] signatureByte) {
//*******************************************
//RSAで署名検証を行う
//*******************************************
boolean isValid = false;
if (key == null) {
LOGGER.debug("公開鍵が存在しない");
return isValid;
}
if (!(this.key instanceof PublicKey)) {
return isValid;
}
try {
Signature signature = Signature.getInstance(getAlg());
signature.initVerify((PublicKey) this.key);
signature.update(contentByte);
isValid = signature.verify(signatureByte);
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
LOGGER.debug(e.getMessage(), e);
}
return isValid;
}
}
5.最後に
RSAとHMACに関しては、すぐ情報が見つかって簡単であったがECDSAがなかなか良い情報がなくて苦労した。
最終的に断片的な知識の状態でこうすればいけるかな?という状態で作ったのでまともに動いたときは感動しましたわ(笑
特にECDSAの暗号化に関しては何かの参考になれば良いと思います。
以上