An overview of the

Recently, we need to call the third-party development interface in the project. After consideration, we plan to use MD5+RSA algorithm to sign the request data, so as to achieve the goal of request authentication and filtering illegal requests. The digital signature is implemented using MD5+RSA algorithm. The RSA private key is strictly confidential and provides secure storage media. The MD5withRSA algorithm specified in the java.security.signature package is used to implement the digital Signature. Private key signature: Public key check means that the interface caller stores the private key and signs the request data with the private key. The platform party stores the public key provided by the caller and checks the signature of the caller. The data requested by the caller is received only after the check passes.

Simple and easy process

1, Obtain 32 bit businessId from the platform, standby 2, generate keyPair, where the privateKey is saved The signature field is businessId + signature and the signature data is subject to the interface qualification

KeyPair generation, signature and verification

KeyPair generated

private static final String KEY_ALGORITHM = "RSA";
private static final String SIGNATURE_ALGORITHM = "MD5withRSA";
private static final String CHARSET = "UTF-8";
private ThreadLocal<String> publicKey = new ThreadLocal<>();
private ThreadLocal<String> privateKey = new ThreadLocal<>();

public CustomKeyPair generateKeyPair(a) throws NoSuchAlgorithmException {
    KeyPairGenerator keygen = java.security.KeyPairGenerator
            .getInstance(KEY_ALGORITHM);
    SecureRandom secureRandom = new SecureRandom();
    secureRandom.setSeed("remote".getBytes()); // Initialize the random generator
    keygen.initialize(1024);
    KeyPair keys = keygen.genKeyPair();
    RSAPublicKey publicKey = (RSAPublicKey) keys.getPublic();
    RSAPrivateKey privateKey = (RSAPrivateKey) keys.getPrivate();
    CustomKeyPair customKeyPair = CustomKeyPair.builder()
            .privateKey(Base64Utils.encodeToString(privateKey.getEncoded()))
            .publicKey(Base64Utils.encodeToString(publicKey.getEncoded()))
            .build();
    log.info("privateKey:{}", customKeyPair.getPrivateKey());
    log.info("publicKey:{}", customKeyPair.getPublicKey());
    return customKeyPair;
}

@Data
@Builder
private static class CustomKeyPair {
    private String privateKey;
    private String publicKey;
}
Copy the code

Signature Invocation Example

SignatureTool.I.putPrivateKey(customKeyPair.privateKey).signature("Test signature data")
Copy the code

Signature code implementation:

public String signature(String data) {
    try {
        return signature(data.getBytes(CHARSET));
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
        throw new RuntimeException("Encryption algorithm does not exist.");
    } catch (SignatureException e) {
        e.printStackTrace();
        throw new RuntimeException("Data signature does not exist");
    } catch (InvalidKeyException | InvalidKeySpecException e) {
        e.printStackTrace();
        throw new RuntimeException("Digital signature key exception");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
        throw new RuntimeException("Unsupported character encoding"); }}/** * Digital signature algorithm **@paramData Indicates signature data *@returnSignature result *@throwsNoSuchAlgorithmException No such encryption algorithm exception *@throwsSignatureException SignatureException *@throwsInvalidKeyException Invalid private key */
private String signature(byte[] data) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidKeySpecException {
    byte[] keyBytes = Base64Utils.decodeFromString(this.privateKey.get());
    PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
    PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
    Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
    signature.initSign(privateKey);
    signature.update(data);
    return Base64Utils.encodeToString(Base64Utils.encodeToString(signature.sign()).getBytes());
}
Copy the code

Sample check call

SignatureTool.I.putPublicKey(customKeyPair.publicKey).verifySignature("Test signature data", signature);
Copy the code

Verification code implementation:

public boolean verifySignature(String data, String signature) {
    try {
        return verifySignature(data.getBytes(CHARSET), signature);
    } catch (NoSuchAlgorithmException e) {
        log.warn("Encryption algorithm does not exist.");
    } catch (SignatureException e) {
        log.warn("Data signature does not exist");
    } catch (InvalidKeyException | InvalidKeySpecException e) {
        log.warn("Digital signature key exception");
    } catch (UnsupportedEncodingException e) {
        log.warn("Unsupported character encoding");
    }
    return false;
}

  /** * Digital signature verification **@paramData Check data *@paramSign the signature *@returnTest result *@throwsNoSuchAlgorithmException No such encryption algorithm exception *@throwsSignatureException SignatureException *@throwsInvalidKeyException Invalid private key */
private boolean verifySignature(byte[] data, String sign) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, InvalidKeySpecException {
    byte[] signs = Base64Utils.decode(Base64Utils.decodeFromString(sign));
    byte[] publicKey = Base64Utils.decodeFromString(this.publicKey.get());
    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
    KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
    PublicKey pubKey = keyFactory.generatePublic(keySpec);
    Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
    signature.initVerify(pubKey);
    signature.update(data);
    return signature.verify(signs);
}
Copy the code

Invoke the sample

The signature data can be set separately based on the actual service interface

public static void main(String[] args) throws NoSuchAlgorithmException {
    // Generate a key pair
    CustomKeyPair customKeyPair = SignatureTool.I.generateKeyPair();
    // Private key signature
    String signature = SignatureTool.I.putPrivateKey(customKeyPair.privateKey).signature("Test signature data");
    // Public key check
    SignatureTool.I.putPublicKey(customKeyPair.publicKey).verifySignature("Test signature data", signature);
}
Copy the code

With businessId assigned to each caller, the public key is provided and mapped to businessId, and the private key is stored by the caller from beginning to end, we can directly determine which business is the source of the data delivery and the security of the interface invocation is effectively guaranteed. In the future, restrictions such as IP whitelist can be added to further strengthen the security of the interface. Especially in the scenario of multi-party invocation, the anti-pulling effect is remarkable. In addition, the called party can also generate a separate keypair for the business party to realize the double-insurance mechanism of two-way signature check.

SignatureTool complete code is as follows:

import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Base64Utils;

import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Locale;

@Slf4j
public enum SignatureTool {
    I;

    private static final String KEY_ALGORITHM = "RSA";
    private static final String SIGNATURE_ALGORITHM = "MD5withRSA";
    private static final String CHARSET = "UTF-8";
    private ThreadLocal<String> publicKey = new ThreadLocal<>();
    private ThreadLocal<String> privateKey = new ThreadLocal<>();

    /** * Digital signature **@paramData Signature content *@returnSignature * /
    public String signature(String data) {
        try {
            return signature(data.getBytes(CHARSET));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new RuntimeException("Encryption algorithm does not exist.");
        } catch (SignatureException e) {
            e.printStackTrace();
            throw new RuntimeException("Data signature does not exist");
        } catch (InvalidKeyException | InvalidKeySpecException e) {
            e.printStackTrace();
            throw new RuntimeException("Digital signature key exception");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            throw new RuntimeException("Unsupported character encoding"); }}public boolean verifySignature(String data, String signature) {
        try {
            return verifySignature(data.getBytes(CHARSET), signature);
        } catch (NoSuchAlgorithmException e) {
            log.warn("Encryption algorithm does not exist.");
        } catch (SignatureException e) {
            log.warn("Data signature does not exist");
        } catch (InvalidKeyException | InvalidKeySpecException e) {
            log.warn("Digital signature key exception");
        } catch (UnsupportedEncodingException e) {
            log.warn("Unsupported character encoding");
        }
        return false;
    }

    /** * Initializes the public and private keys */
    public SignatureTool initKeys(String privateKey, String publicKey) {
        this.publicKey.set(publicKey);
        this.privateKey.set(privateKey);
        return this;
    }

    public SignatureTool putPublicKey(String publicKey) {
        this.publicKey.set(publicKey);
        return this;
    }

    public SignatureTool putPrivateKey(String privateKey) {
        this.privateKey.set(privateKey);
        return this;
    }

    public CustomKeyPair generateKeyPair(a) throws NoSuchAlgorithmException {
        KeyPairGenerator keygen = KeyPairGenerator
                .getInstance(KEY_ALGORITHM);
        SecureRandom secureRandom = new SecureRandom();
        secureRandom.setSeed("ainoteRemote".getBytes()); // Initialize the random generator
        keygen.initialize(1024);
        KeyPair keys = keygen.genKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey) keys.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keys.getPrivate();
        CustomKeyPair customKeyPair = CustomKeyPair.builder()
                .privateKey(Base64Utils.encodeToString(privateKey.getEncoded()))
                .publicKey(Base64Utils.encodeToString(publicKey.getEncoded()))
                .build();
        log.info("privateKey:{}", customKeyPair.getPrivateKey());
        log.info("publicKey:{}", customKeyPair.getPublicKey());
        return customKeyPair;
    }

    /** * Digital signature algorithm **@paramData Indicates signature data *@returnSignature result *@throwsNoSuchAlgorithmException No such encryption algorithm exception *@throwsSignatureException SignatureException *@throwsInvalidKeyException Invalid private key */
    private String signature(byte[] data) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, InvalidKeySpecException {
        byte[] keyBytes = Base64Utils.decodeFromString(this.privateKey.get());
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateKey);
        signature.update(data);
        return Base64Utils.encodeToString(Base64Utils.encodeToString(signature.sign()).getBytes());
    }

    /** * Digital signature verification **@paramData Check data *@paramSign the signature *@returnTest result *@throwsNoSuchAlgorithmException No such encryption algorithm exception *@throwsSignatureException SignatureException *@throwsInvalidKeyException Invalid private key */
    private boolean verifySignature(byte[] data, String sign) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, InvalidKeySpecException {
        byte[] signs = Base64Utils.decode(Base64Utils.decodeFromString(sign));
        byte[] publicKey = Base64Utils.decodeFromString(this.publicKey.get());
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey pubKey = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(pubKey);
        signature.update(data);
        return signature.verify(signs);
    }

    @Data
    @Builder
    private static class CustomKeyPair {
        private String privateKey;
        private String publicKey;
    }

    public static void main(String[] args) throws NoSuchAlgorithmException {
        System.out.println(CommonUtil.newUUID().toUpperCase(Locale.ROOT));
        CustomKeyPair customKeyPair = SignatureTool.I.generateKeyPair();
        Long timeStamp = System.currentTimeMillis();
        String signature = SignatureTool.I.putPrivateKey(customKeyPair.privateKey).signature(timeStamp+"");
        System.out.println(timeStamp);
        log.debug(signature);
        SignatureTool.I.putPublicKey(customKeyPair.publicKey).verifySignature(timeStamp+"", signature); }}Copy the code