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