java.security.Providerを使った署名と検証

#java.security.Providerの使い方メモ
java.securityにはProviderという仕組みがあって暗号化や署名の実装を追加できるのですが、たぶんごく一部の人達しか使わないので、説明しているサイトもあまりありません。
##まずは普通にJavaで署名して検証してみる

```java:Sample.java
package io.github.tshibata.sample;

import java.security.*;

public class Sample {
	static void test(byte[] data, KeyPair pair) throws Exception {
		Signature sig = Signature.getInstance("SHA256withRSA");
		// 署名するよ
		sig.initSign(pair.getPrivate());
		sig.update(data);
		byte[] s = sig.sign();
		// Base64で出力してみるよ
		System.out.println(java.util.Base64.getEncoder().encodeToString​(s));
		// 検証するよ
		sig.initVerify(pair.getPublic());
		sig.update(data);
		System.out.println(sig.verify(s));
	}
	public static void main(String[] args) throws Exception {
		// 鍵作るよ
		KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
		KeyPair pair = gen.generateKeyPair();
		// "hello"を署名して検証するよ
		test("hello".getBytes(), pair);
	}
}
```
##Signatureのサブクラスを実装してみる
署名の場合はSignatureクラスが担当します。

```java:MySignature.java
package io.github.tshibata.sample;

import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

import java.math.BigInteger;

public class MySignature extends Signature {

// echo | openssl dgst -sign private.pem | openssl rsautl -verify -inkey private.pem -raw | head --bytes=-32 | base64

	private static byte[] padding = java.util.Base64.getDecoder().decode(
		"AAH/////////////////////////////////////////////////////////////////////////" +
		"////////////////////////////////////////////////////////////////////////////" +
		"////////////////////////////////////////////////////////////////////////////" +
		"////////////////////////////////////////////ADAxMA0GCWCGSAFlAwQCAQUABCA=");

	private RSAPrivateKey privateKey;

	private RSAPublicKey publicKey;

	private MessageDigest messageDigest;

	public MySignature() {
		super("SHA256withRSA");
		try {
			messageDigest = MessageDigest.getInstance("SHA-256");
		} catch (NoSuchAlgorithmException exc) {
			throw new UnsupportedOperationException(exc);
		}
	}

	protected Object engineGetParameter(String param) throws InvalidParameterException {
		throw new UnsupportedOperationException("engineGetParameter");
	}

	protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
		throw new UnsupportedOperationException("engineSetParameter");
	}

	protected void engineInitSign(PrivateKey key) throws InvalidKeyException {
		messageDigest.reset();
		privateKey = (RSAPrivateKey) key;
	}

	protected void engineInitVerify(PublicKey key) throws InvalidKeyException {
		messageDigest.reset();
		publicKey = (RSAPublicKey) key;
	}

	protected void engineUpdate(byte[] data, int offset, int length) throws SignatureException {
		messageDigest.update(data, offset, length);
	}

	protected void engineUpdate(byte data) throws SignatureException {
		messageDigest.update(data);
	}

	protected byte[] engineSign() throws SignatureException {
		byte[] data = new byte[256];
		System.arraycopy(padding, 0, data, 0, padding.length);
		byte[] digest = messageDigest.digest();
		System.arraycopy(digest, 0, data, padding.length, digest.length);
		BigInteger m = new BigInteger(1, data);
		BigInteger d = privateKey.getPrivateExponent();
		BigInteger n = privateKey.getModulus();
		BigInteger c = m.modPow(d, n);

		byte[] signed = new byte[256];
		for (int i = signed.length - 1; 0 <= i; i--) {
			signed[i] = c.byteValue();
			c = c.shiftRight(8);
		}
		return signed;
	}

	protected boolean engineVerify(byte[] signed) throws SignatureException {
		BigInteger c = new BigInteger(1, signed);
		BigInteger e = publicKey.getPublicExponent();
		BigInteger n = publicKey.getModulus();
		BigInteger m = c.modPow(e, n);

		byte[] data = new byte[256];
		System.arraycopy(padding, 0, data, 0, padding.length);
		byte[] digest = messageDigest.digest();
		System.arraycopy(digest, 0, data, padding.length, digest.length);
		for (int i = data.length - 1; 0 <= i; i--) {
			if (data[i] != m.byteValue()) {
				return false;
			}
			m = m.shiftRight(8);
		}
		return true;
	}
}
```
鍵の長さは2048bit（256byte）固定にしました。
ハッシュを作ってパディング付けてexponent乗してmodulusで割った余りを計算するだけです。
パディングはおまじないというか決まり事です。

> echo | openssl dgst -sign private.pem | openssl rsautl -verify -inkey private.pem -raw | head --bytes=-32 | base64

ってやって作りました。
「exponent乗してmodulusで割った余り」ってがRSAのキモで、昔偉い学者さん達が見つけました。
でこれを使ってもらうために

```java:MyProvider.java
package io.github.tshibata.sample;

import java.security.*;

public class MyProvider extends Provider {

	public MyProvider() {
		super("MyProvider", "1.0", "My provider");
		put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.11", "SHA256withRSA");
		put("Signature.SHA256withRSA SupportedKeyClasses", "java.security.interfaces.RSAPublicKey|java.security.interfaces.RSAPrivateKey");
		put("Signature.SHA256withRSA", "io.github.tshibata.sample.MySignature");
		put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256withRSA");
	}
}
```
というProviderのサブクラスを作って

```java:Sample.java
package io.github.tshibata.sample;

import java.security.*;

public class Sample {
	static void test(byte[] data, KeyPair pair) throws Exception {
		Signature sig = Signature.getInstance("SHA256withRSA");
		// 署名するよ
		sig.initSign(pair.getPrivate());
		sig.update(data);
		byte[] s = sig.sign();
		// Base64で出力してみるよ
		System.out.println(java.util.Base64.getEncoder().encodeToString​(s));
		// 検証するよ
		sig.initVerify(pair.getPublic());
		sig.update(data);
		System.out.println(sig.verify(s));
	}
	public static void main(String[] args) throws Exception {
		// 鍵作るよ
		KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
		KeyPair pair = gen.generateKeyPair();
		// "hello"を署名して検証するよ
		test("hello".getBytes(), pair);
		// MyProviderを使ってみるよ
		Security.insertProviderAt(new MyProvider(), 1);
		test("hello".getBytes(), pair);
	}
}
```
という感じで署名や検証の前に登録しておきます。insertProviderAtの第２引数は優先順位で1にしておけば最優先で使われます。