Network connection receipt message signature verification and sensitive information decryption error: failed to construct sequence from byte[]: corrupted stream detected [National Secret SM2]

Network connection receipt message signature verification and sensitive information decryption error: failed to construct sequence from byte[]: corrupted stream detected [National Secret SM2]

Signature verification problem scenario parameters:

Use BC package version 1.57 to convert the network signature value format: ASN.1(R,S) to the encryption machine signature value format: RS. The process reports an error.

Network signature value format: ASN.1(R,S)

Encryption machine signature value format: RS

Conversion and parsing process: org.bouncycastle.asn1.ASN1Sequence.getInstance (network signature value)

BC package version: 1.57

Exception information: java.lang.IllegalArgumentException: failed to construct sequence from byte[]: corrupted stream detected
Abnormal situation: Random occurrence

Cause of the problem

Use BC package version 1.57 to convert the network signature value format: ASN.1(R,S) to the encryption machine signature value format: RS. The process reports an error. It is inferred that BC package version 1.57 has a problem in interpreting the ASN1 data format of the network signature value.

Solution:

Analyze and read the R value and S value according to the signature value format of the network signature algorithm

  • SM2 signature result
    The signature format is TLV nested format, and the body of the signature is divided into two parts: R and S. The length of R (or S) is equal to the length of the ECC private key. The T before R (or S) is 0x02, and the signature T is 0x30. The overall format is as follows:
  1. 30 + LEN1 + 02 + LEN2 + 00 (optional) + r + 02 + LEN3 + 00 (optional) + s
    When the first byte of r or s is greater than 0x80, 1 byte 0x00 needs to be added before r or s.
  2. LEN3 is the byte length of 0x00(optional) + s.
  3. LEN2 is the byte length of 0x00(optional) + r.
  4. LEN1 is, LEN2 + LEN3 + 4 bytes in length.
    Example signature value:

** 1.57 No error signature: ** 304502210090538DC25A2F8FA9AB251C43D74351C524906188DEA497315A7EF9FA4D56D5210220001C61BA47FBA968F975A0A2381B2B9C1128C1B 0C622AA8AA263CB08BA1F788B
**1.57 error signature: ** 3045022100B9BEA487F8E7D53950F197129CB3F2071E6F72B397891D28956B1FBA249A09EB0220208CFF213212A6D311CB4E8D122BE020E924CBC1E 033B14C4BDFA12EE2BFD8D9

With explanation of test code
//Signature value in hexadecimal
String signvalue = "3045022100B9BEA487F8E7D53950F197129CB3F2071E6F72B397891D28956B1FBA249A09EB0220208CFF213212A6D311CB4E8D122BE020E924CBC1E033B14C 4BDFA12EE2BFD8D9";
//Convert signature value to binary
Byte[] signValueData = Hex.decode(signvalue);
//Read LEN1
int len1 = (int) signValueData [1];
//Read LEN2
int len2 = signValueData [3];
//Calculate LEN3
int len3 = len1 - len2 - 4;
//Calculate R value length & R value PADDING length
int padRLen = len2 - 32;
int rLen = 32;
if(len2 < 32){
    rLen = len2;
    padRLen = 0;
}
//Read R value
byte[] rByte = readField(signValueData, 4 + padRLen, rLen);
if(len3>32){
    len3 = 32;
}
 //Read S value
byte[] sByte = readField(signValueData, signValueData .length - len3, len3);
byte[] rsByte = mergeByte(rByte, sByte);
return rsByte;


  • Related method code
/**
     * Merge byte arrays
     *
     * @param bt1 byte array 1
     * @param bt2 byte array 2
     * @return
     * @throwsException
     */
    public static byte[] mergeByte(byte[] bt1, byte[] bt2) {
        byte[] dest = new byte[bt1.length + bt2.length];
        System.arraycopy(bt1, 0, dest, 0, bt1.length);
        int offset = bt1.length;
        System.arraycopy(bt2, 0, dest, offset, bt2.length);
        return dest;
    }
 /**
     * Read data field
     *
     * @param data data
     * @param fieldIndex data field starting index
     * @param fieldLen data field data length
     * @return
     */
    public static byte[] readField(byte[] data, int fieldIndex, int fieldLen) {
        byte[] filedData = new byte[fieldLen];
        System.arraycopy(data, fieldIndex, filedData, 0, fieldLen);
        return filedData;
    }

    /**
     * Convert ASN1 format signature value to RS format signature value
     * @param asn1Base64Signature
     * @return
     */
    public static byte[] toRSSignature(String asn1Base64Signature) {
        byte[] signatureByte = Base64.getDecoder().decode(asn1Base64Signature);
        return toRSSignature(signatureByte);
    }

    /**
     * Convert ASN1 format signature value to RS format signature value
     * @param asn1Signature
     * @return
     */
    public static byte[] toRSSignature(byte[] asn1Signature) {
        int len1 = (int) asn1Signature[1];
        int len2 = asn1Signature[3];
        int len3 = len1 - len2 - 4;
        int padRLen = len2 - 32;
        int rLen = 32;
        if(len2 < 32){
            rLen = len2;
            padRLen = 0;
        }
        byte[] rByte = EMUtil.readField(asn1Signature, 4 + padRLen, rLen);
        if(len3>32){
            len3 = 32;
        }
       
        byte[] sByte = EMUtil.readField(asn1Signature, asn1Signature.length - len3, len3);
        byte[] rsByte = EMUtil.mergeByte(rByte, sByte);
        return rsByte;
    }
Decryption problem scenario:

Similar to signature verification, this problem is due to an error caused by the process of reading and parsing the ciphertext value of the network connection format ASN.1 (C1C3C2) and converting it to the encryption machine (encrypted ciphertext value format: C1C3C2) for decryption.

Problem scenario parameters:

Network ciphertext value format: ASN.1(C1C3C2)

Encrypted secret text value format: C1C3C2

Conversion and parsing process: org.bouncycastle.asn1.ASN1Sequence.getInstance (network connection ciphertext value)

BC package version: 1.57

Exception information: java.lang.IllegalArgumentException: failed to construct sequence from byte[]: corrupted stream detected
Abnormal situation: Random occurrence

Cause of the problem

The ciphertext value of the network connection format ASN.1 (C1C3C2) is converted and passed to the encryption machine (encrypted secret text value format: C1C3C2). An error is reported during the decryption process. It is inferred that BC package version 1.57 has a problem in interpreting the ASN1 data format of the network connection encrypted data value.

Solution:

Parse and read the ciphertext value in C1C3C2 format according to the network ASN.1 ciphertext value format.

  • The ASN.1 data structure of encrypted data is as follows:
    CipherData ::= SEQUENCE {
    xcoordinateINTEGER,
    ycoordinate INTEGER,
    hash OCTET STRING,
    cigherText OCTET STRING
    }

  • C1C3C2 format corresponds to:
    c1 = xcoordinate + ycoordinate
    c3 = hash;
    c2 = cigherText;

  • The format is parsed as follows:
    30 + totalLen + xTag + xlen + x + yTag + ylen + y + c3Tag + c3len + c3 + c2Tag + c2len + c2;

  • Example data:
    307c022100e739650179d8dea27e17c126c927a4c9934090b8921599bbe6463aff546ed7b5022021de9b9b3ca414c3e4d947cfb0df1a146caa427ee41cd687c4eed230 97b9ea4e04200eb2f3e3f230cd0e5dd6d109d24c0913b76cb4e2a48e554a0018bd0b69b8e21d04130b5f55e5b98ee53bc43cdb55b730114cf75fd6

Attached is the test code for converting ASN.1 encrypted data into C1C3C2 format ciphertext

  • Directly upload the code
/**
   * The ASN.1 data structure of encrypted data is as follows:
   * CipherData ::= SEQUENCE {
   * xcoordinate INTEGER,
   * ycoordinate INTEGER,
   * hash OCTET STRING,
   * cigherText OCTET STRING
   * }
   *
   * C1C3C2 format corresponds to:
   * c1 = xcoordinate + ycoordinate
   * c3 = hash;
   * c2 = cigherText;
   *
   * The format is analyzed as follows:
   * 30 + totalLen + xTag + xlen + x + yTag + ylen + y + c3Tag + c3len + c3 + c2Tag + c2len + c2;
   * @paramasn1Data
   * @throwsIOException
   */

  publicstaticbyte[] asn1CipherDataToc1c3c2(byte[] asn1Data) throwsIOException {

      inttotalLen = (int) asn1Data[1];
      intxlen = (int) asn1Data[3];
      intylen = (int) asn1Data[5 + xlen];
      intc3len = (int) asn1Data[7 + xlen + ylen];
      intc2len = totalLen - xlen - ylen - c3len - 8;
      System.out.println("totalLen>>" + totalLen);
      System.out.println("xlen>>" + xlen);
      System.out.println("ylen>>" + ylen);
      System.out.println("c3len>>" + c3len);
      System.out.println("c2len>>" + c2len);

      byte[] xByte = EMUtil.readField(asn1Data, 4, xlen);
      byte[] yByte = EMUtil.readField(asn1Data, 6 + xlen, ylen);
      xByte = fixToCurveLengthBytes(xByte);
      yByte = fixToCurveLengthBytes(yByte);
      byte[] c3Byte = EMUtil.readField(asn1Data, 8 + xlen + ylen, c3len);
      byte[] c2Byte = EMUtil.readField(asn1Data, asn1Data.length - c2len, c2len);
      byte[] c1c3c2Byte = mergeC1c3c2Byte(xByte, yByte, c3Byte, c2Byte);
      returnc1c3c2Byte;
  }

  /**
   * Filter large number padding and convert to Curve length bytes
   * @paramsrc
   * @return
   */
  privatestaticbyte[] fixToCurveLengthBytes(byte[] src) {

      if (src.length == CURVE_LEN) {
          returnsrc;
      }
      byte[] result = newbyte[CURVE_LEN];
      if (src.length > CURVE_LEN) {
          System.arraycopy(src, src.length - result.length, result, 0, result.length);
      } else {
          System.arraycopy(src, 0, result, result.length - src.length, src.length);
      }
      returnresult;
  }

  /**
   * Merge c1c3c2 data
   * @paramxByte
   * @paramyByte
   * @paramc3
   * @paramc2
   * @return
   */
  privatestaticbyte[] mergeC1c3c2Byte(byte[] xByte, byte[] yByte, byte[] c3, byte[] c2) {
      byte[] c1c3c2 = newbyte[xByte.length + yByte.length + c3.length + c2.length];
      System.arraycopy(xByte, 0, c1c3c2, 0, xByte.length);
      inoffset = xByte.length;
      System.arraycopy(yByte, 0, c1c3c2, offset, yByte.length);
      offset + = yByte.length;
      System.arraycopy(c3, 0, c1c3c2, offset, c3.length);
      offset + = c3.length;
      System.arraycopy(c2, 0, c1c3c2, offset, c2.length);
      returnc1c3c2;
  }