Signature et validation à l'aide de java.security.Provider

Remarques sur l'utilisation de java.security.Provider

Il existe un mécanisme appelé Provider dans java.security qui vous permet d'ajouter des implémentations de chiffrement et de signature, mais probablement seulement quelques personnes l'utilisent, il n'y a donc pas beaucoup de sites qui l'expliquent.

Tout d'abord, essayez de signer avec Java et de vérifier

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");
		//Je signerai
		sig.initSign(pair.getPrivate());
		sig.update(data);
		byte[] s = sig.sign();
		//Je vais le sortir avec Base 64
		System.out.println(java.util.Base64.getEncoder().encodeToString​(s));
		//Je vais vérifier
		sig.initVerify(pair.getPublic());
		sig.update(data);
		System.out.println(sig.verify(s));
	}
	public static void main(String[] args) throws Exception {
		//Je vais faire une clé
		KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
		KeyPair pair = gen.generateKeyPair();
		// "hello"Je vais signer et vérifier
		test("hello".getBytes(), pair);
	}
}

Essayez d'implémenter une sous-classe de Signature

La classe Signature est en charge de la signature.

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;
	}
}

La longueur de la clé est fixée à 2048 bits (256 octets). Tout ce que vous avez à faire est de créer un hachage, de le compléter, de le multiplier par exposant et de le diviser par module pour calculer le reste. Le rembourrage est une magie ou une règle.

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

Je l'ai fait. «Le reste de l'exposant d'équitation et de la division par module» est la clé du RSA, et a été découvert par de grands érudits dans le passé. Pour l'utiliser dans

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");
	}
}

Créez une sous-classe de fournisseur appelée

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");
		//Je signerai
		sig.initSign(pair.getPrivate());
		sig.update(data);
		byte[] s = sig.sign();
		//Je vais le sortir avec Base 64
		System.out.println(java.util.Base64.getEncoder().encodeToString​(s));
		//Je vais vérifier
		sig.initVerify(pair.getPublic());
		sig.update(data);
		System.out.println(sig.verify(s));
	}
	public static void main(String[] args) throws Exception {
		//Je vais faire une clé
		KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
		KeyPair pair = gen.generateKeyPair();
		// "hello"Je vais signer et vérifier
		test("hello".getBytes(), pair);
		//J'essaierai d'utiliser MyProvider
		Security.insertProviderAt(new MyProvider(), 1);
		test("hello".getBytes(), pair);
	}
}

Je vais l'enregistrer avant de signer et de vérifier. Si le deuxième argument de insertProviderAt est défini sur 1 en priorité, il sera utilisé avec la priorité la plus élevée.

Recommended Posts

Signature et validation à l'aide de java.security.Provider
Validation du ressort et code d'erreur
Animation utilisant matchedGeometryEffect et @Namespace
Hachage et authentification de mot de passe à l'aide de JBcrypt
Validation des messages du journal à l'aide de mockito
[Rails] Paramètres de validation et localisation en japonais
Implémentation de la validation à l'aide d'expressions régulières
Surveillance de l'infrastructure à l'aide de Graphite et StatsD