Initial import of NFC GNSS SDK project

This commit is contained in:
chenjintao
2026-09-15 16:10:12 +08:00
commit 2746cc3d4e
3070 changed files with 892062 additions and 0 deletions
+28
View File
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDCXAJHRR2K7v9B
+L6HaHHH/ZQroZN7biOvz0MXsUkxe5hAYBFz32vzCzXvYfAfL3zn2/w2kd6lB8Xy
cEnHwHhGOnNq9Y1tRH1QtyiySdRsT+4wlb27Gn6qZn+hqHBKT0lh3eCpu8iV3L1F
c1dfYHnsm+bIfc8KjrUttQYR7GNw4+WZyFRyVNvwqDExfmIkXGy9xK+c9P7mWWuu
XIWKE8ImQ26tzCcWDQknI5X2XCwuUmss9wcVw3LvmCMOf929pvvgjyl2ff3iPWNc
EaoXESSQJs8/UrZm9XkZJNzVmXzn7WXp0WEO+MH/xx0ly1dDFt5VkSXsLqhpk/u8
NoKFjE5xAgMBAAECggEBAK3gLlGgLWvLoCRe6eSttYza+KGH7S7xO4/e9gDOI9LW
5UBCMZrd9+74yombrnvXugCbNymCmhI9wALCHd9L+s2pIPRBNQLZ0NpS4V+O2TEE
2OwyKginDUZYaDoxPDPedqHNI131lt8rGVB2fWXgI3Nfjbkzg/PH86IuXn3OYq/s
QRfvB8Q6uV25L6YdYoz1WRh8Zvn8c0mBXtZ/7kNRObpySHx3KQx0VzJI4t8qhC+c
hO9aQOvftC+g+vy/MMyZmOg2gQ3Km75jLKs1S5RO3f7bjZPIsQyJmhxk5khpLZ91
xN9be2bVjT/5U495lL4CBl2TiOPc84IfrV6btE0OWbkCgYEA8/KEMt6YeNoI5dtU
oHGAHOZ3Ms23kdpllvXOJFH7sxZiq1fOEY6lCoiQ0pr3wmT9OWjTBarLkB7KD/Bk
fiJx08SevdC051XQ9EAcaAo/vCPfzCbpGByEyK4DJJgOeE6rZCyeTsrfmMynVZTr
IY95W9c65o7nukGbU/Iug5P87EMCgYEAy/ZL+7KfFO/s04JUKO1BvggGWCXYTdqD
e5zaQ4xv2mez+AIrJ+VCcgUNMOH3XtXGGzFP4uV6ICy6tiZv6sEoij+Wtl0RzH/V
8DHcIWiqurQ1+zrlnpUTdKbVPoST0gZOXLYz+erzTd7ZzCflCbvvKL2ATWRKRxXo
Jxx0bRzGiTsCgYAM1Z2O75cmBJKjRT7WkiIjYxyyV5Bo5AykPJpz3rxXV74pNZ1p
3w9f1n+IFQ8j8xw/gPxZN/stwfDvlb4zxKwocBfJ9MkPwrwTnavg7WkdBOYpX4Kz
+gwiYsVXj4hzW/A4fQB3NMU+zMWhAwy730iLRG+c4bffXMRHTl6UHZ0Y8QKBgDkW
7528GZsarQdm63epMUWnqV7y+OCR0UvAU7ekj4pqjI1Wzg2UtkyD7XVJSpxY6eKi
ouG3qb9nxbtGdv0Wo+pOcjkJQ/ohrhhVXHBdg6xO1O1FRxBkWdXE/NtK8LVQ3o5E
0beadA/P2Na0M//Ngtu8bzU6ZTAQibKo3E3OSCQRAoGBANFe9kKVsbA7xpcQdw4M
Da4LUSOFLe8SgaP3wZFJT5X0/ybBs/zdM4xT9lqYCnlvWiYQzO2pzLOrjG2jSW68
67eLHaLbLgs6kiixpMRjIIlOpTy+VOlvuZtDxYzo/qZl7ATxpmolnSYwTEowEz1M
/5YXqfOGQu1Grzuw7sll2jIg
-----END PRIVATE KEY-----
+9
View File
@@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwlwCR0Udiu7/Qfi+h2hx
x/2UK6GTe24jr89DF7FJMXuYQGARc99r8ws172HwHy9859v8NpHepQfF8nBJx8B4
RjpzavWNbUR9ULcosknUbE/uMJW9uxp+qmZ/oahwSk9JYd3gqbvIldy9RXNXX2B5
7JvmyH3PCo61LbUGEexjcOPlmchUclTb8KgxMX5iJFxsvcSvnPT+5llrrlyFihPC
JkNurcwnFg0JJyOV9lwsLlJrLPcHFcNy75gjDn/dvab74I8pdn394j1jXBGqFxEk
kCbPP1K2ZvV5GSTc1Zl85+1l6dFhDvjB/8cdJctXQxbeVZEl7C6oaZP7vDaChYxO
cQIDAQAB
-----END PUBLIC KEY-----
+46
View File
@@ -0,0 +1,46 @@
quectel签名工具默认使用目录下的ql_secota_xxx.pem密钥对
使用前需要注意安装python,安装后部分电脑可能缺少环境,可根据错误安装依赖包,win环境下在命令行输入如下指令
pip install cryptography
pip install pyasn1
pip install requests
如果权限不足可用以下指令,或者命令行管理员权限运行
python -m pip install cryptography
python -m pip install pyasn1
python -m pip install requests
如需替换自定义公私密钥对,将文件内容覆盖即可
签名工具内置四种功能,使用说明如下:
1. 本地签名
usage: python sign_tool.py local [-h] -orig ORIG -out OUT -priv PRIV
options:
-h, --help show this help message and exit
-orig ORIG Path to the original package
-out OUT Path to save the output package
-priv PRIV Path to the private key
2. 远程签名(仅quectel使用, 如想远程签名, 需自行使用 sign_tool.py集成在远程服务器)
usage: python sign_tool.py remote [-h] -orig ORIG -out OUT -url URL -token TOKEN
options:
-h, --help show this help message and exit
-orig ORIG Path to the original package
-out OUT Path to save the output package
-url URL HSM URL for remote signing
-token TOKEN HSM request token
3. PEM公钥转C文件
usage: python sign_tool.py pubkey2c [-h] -orig ORIG -out OUT -array ARRAY
options:
-h, --help show this help message and exit
-orig ORIG Path to the input PEM public key file
-out OUT Path to the output C header file
-array ARRAY Name of the array in the C header file
4. PEM私钥转C文件
usage: python sign_tool.py pubkey2c [-h] -orig ORIG -out OUT -array ARRAY
options:
-h, --help show this help message and exit
-orig ORIG Path to the input PEM private key file
-out OUT Path to the output C header file
-array ARRAY Name of the array in the C header file
+405
View File
@@ -0,0 +1,405 @@
import sys
import subprocess
import os
import argparse
import hashlib
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
from datetime import datetime, timedelta, timezone
from cryptography.x509.oid import ObjectIdentifier
from pyasn1.type import univ, namedtype
from pyasn1.codec.der import encoder as der_encoder
from cryptography.x509 import load_pem_x509_certificate
import binascii
import base64
import requests
class MyFileManager:
@staticmethod
def read_file(file_path, description):
try:
with open(file_path, 'rb') as f:
return f.read()
except FileNotFoundError:
print(f"Error: {description} file not found - {file_path}")
except Exception as e:
print(f"Error: Failed to read {description} file - {e}")
MySystemUtils.custom_exit(1)
@staticmethod
def write_file(file_path, data, description):
try:
if os.path.isdir(file_path):
print(f"Error: {file_path} is a directory, expected a file path.")
MySystemUtils.custom_exit(1)
if os.path.dirname(file_path):
os.makedirs(os.path.dirname(file_path), exist_ok=True)
with open(file_path, "wb") as f:
f.write(data)
print(f"{description} generated successfully: {file_path}")
except Exception as e:
print(f"Error: Failed to write {description} - {e}")
MySystemUtils.custom_exit(1)
class MySystemUtils:
cert_file_path = "x509_cert.der"
@staticmethod
def welcome_print():
print("=======================================================================")
print(" Starting sign_tool.py ")
print("=======================================================================")
@staticmethod
def custom_exit(code=0):
# 删除证书
os.remove(MySystemUtils.cert_file_path) if os.path.exists(MySystemUtils.cert_file_path) else None
print("=======================================================================")
print(" SUCCESS" if code == 0 else " FAILED")
print("=======================================================================")
sys.exit(code)
@staticmethod
def calc_sha256(file_path):
print(f"Calculating SHA256 for file: {file_path}")
try:
with open(file_path, 'rb') as f:
return hashlib.sha256(f.read()).hexdigest()
except FileNotFoundError:
print(f"Error: File not found - {file_path}")
except Exception as e:
print(f"Error: Failed to calculate SHA256 - {e}")
MySystemUtils.custom_exit(1)
@staticmethod
def load_private_key(priv_key_path):
print("Loading private key...")
try:
with open(priv_key_path, "rb") as key_file:
return serialization.load_pem_private_key(key_file.read(), password=None)
except Exception as e:
print(f"Error: Failed to load private key - {e}")
MySystemUtils.custom_exit(1)
@staticmethod
def install_missing_packages():
"""Check and install missing packages."""
required_packages = [
"cryptography",
"pyasn1",
"requests"
]
for package in required_packages:
try:
__import__(package)
except ImportError:
print(f"Package '{package}' is not installed. Installing...")
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
class MyCertificateBuilder:
def __init__(self, private_key, sha256):
print("Initializing MyCertificateBuilder...")
self.private_key = private_key
self.sha256 = sha256
self.__subject__ = self.__issuer__ = x509.Name([
x509.NameAttribute(NameOID.COMMON_NAME, "QUECTEL_ASR_SECFOTA"),
])
print("MyCertificateBuilder initialized successfully.")
def build(self):
print("Building certificate...")
try:
builder = x509.CertificateBuilder().subject_name(
self.__subject__
).issuer_name(
self.__issuer__
).public_key(
self.private_key.public_key()
).serial_number(
x509.random_serial_number()
).not_valid_before(
datetime.now(timezone.utc)
).not_valid_after(
datetime.now(timezone.utc) + timedelta(days=3650)
)
# .add_extension(
# x509.BasicConstraints(ca=False, path_length=None), critical=True
# ).add_extension(
# x509.SubjectKeyIdentifier.from_public_key(self.private_key.public_key()), critical=False
# ).add_extension(
# x509.AuthorityKeyIdentifier.from_issuer_public_key(self.private_key.public_key()), critical=False
# )
custom_der = self._build_custom_extension()
builder = builder.add_extension(
x509.UnrecognizedExtension(ObjectIdentifier("1.3.6.1.4.1.4128.2100.8005"), custom_der),
critical=False
)
print("Certificate built successfully.")
return builder.sign(self.private_key, hashes.SHA256())
except Exception as e:
print(f"Error: Failed to build certificate - {e}")
MySystemUtils.custom_exit(1)
def _build_custom_extension(self):
print("Preparing custom extension value...")
sha256_oid = "2.16.840.1.101.3.4.2.1"
class AlgorithmIdentifier(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('algorithm', univ.ObjectIdentifier()),
namedtype.NamedType('parameters', univ.Null())
)
class CustomExtensionValue(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('algorithm', AlgorithmIdentifier()),
namedtype.NamedType('hashValue', univ.OctetString())
)
sha256_bin = binascii.unhexlify(self.sha256) if isinstance(self.sha256, str) else self.sha256
custom_value = CustomExtensionValue()
custom_value['algorithm']['algorithm'] = sha256_oid
custom_value['algorithm']['parameters'] = univ.Null()
custom_value['hashValue'] = sha256_bin
print("Custom extension value prepared successfully.")
return der_encoder.encode(custom_value)
class MyPackBuilder:
def __init__(self, pack_path, der_cert_path):
print(f"Initializing MyPackBuilder with pack_path: {pack_path}, der_cert_path: {der_cert_path}")
self.cert = MyFileManager.read_file(der_cert_path, "Certificate")
self.pack = MyFileManager.read_file(pack_path, "Original pack")
print("MyPackBuilder initialized successfully.")
def build(self):
print("Building signed pack...")
try:
self.pack += self.cert
cert_len = len(self.cert)
pack_len = len(self.pack)
print(f"Certificate length: {cert_len}, Certificate length % 4096 = {cert_len % 4096}")
if cert_len % 4096 != 0:
padding16_size = (16 - (pack_len % 16))%16
print(f"Padding16 size: {padding16_size}")
padding16_tag = b'\0' * padding16_size
padding_size = (4096 - padding16_size) - (cert_len % (4096 - padding16_size))
padding_tag = b"QUEC_SECFOTA\0\0\0\0"
padding_data = padding16_tag + padding_tag * (padding_size // len(padding_tag)) + padding_tag[:padding_size % len(padding_tag)]
self.pack += padding_data
print(f"Padding added: {padding_size} bytes")
print("Signed pack built successfully.")
return self.pack
except Exception as e:
print(f"Error: Failed to build pack - {e}")
MySystemUtils.custom_exit(1)
class MyRemoteSigner:
def __init__(self, hsm_url, hsm_req_token, sha256):
self.hsm_url = hsm_url
self.hsm_req_token = hsm_req_token
self.sha256 = sha256
def handle_remote_signing(self, orig_pack_path, sign_pack_path, cert_file_path):
print("Building request...")
custom_extension = MyCertificateBuilder(None, self.sha256)._build_custom_extension().hex()
print(f"custom_extension: {custom_extension}")
headers = {
"Content-Type": "application/json",
"token": self.hsm_req_token
}
data = {
"oidKey": "1.3.6.1.4.1.4128.2100.8005",
"oidValue": custom_extension
}
print(f"request data: {data}")
try:
response = requests.post(self.hsm_url, json=data, headers=headers)
print(f"request response, data = {response.json()}")
if response.status_code == 200:
response_json = response.json()
data_base64 = response_json.get("data")
if data_base64:
cert_pem_data = base64.b64decode(data_base64)
cert_der_data = load_pem_x509_certificate(cert_pem_data).public_bytes(serialization.Encoding.DER)
# print(f"request cert_pem_data: {cert_pem_data}")
# print(f"convert cert_der_data: {cert_der_data}")
MyFileManager.write_file(cert_file_path, cert_der_data, "[Remote certificate]")
sign_pack = MyPackBuilder(orig_pack_path, cert_file_path).build()
MyFileManager.write_file(sign_pack_path, sign_pack, "[Signed pack]")
else:
print("Error: No certificate data found in response")
MySystemUtils.custom_exit(1)
else:
print(f"Request error, status code: {response.status_code}")
MySystemUtils.custom_exit(1)
except requests.RequestException as e:
print(f"Request API error: {e}")
MySystemUtils.custom_exit(1)
class PubKey2CConverter:
@staticmethod
def detect_key_type(key_data):
"""检测密钥类型"""
try:
# Attempt to load as a public key
serialization.load_pem_public_key(key_data)
return "public"
except ValueError:
try:
# Attempt to load as a private key
serialization.load_pem_private_key(key_data, password=None)
return "private"
except ValueError:
return "unknown"
@staticmethod
def extract_public_key_from_private(key_data):
"""从私钥中提取公钥"""
try:
priv_key = serialization.load_pem_private_key(key_data, password=None)
return priv_key.public_key()
except Exception as e:
print(f"Error: Failed to extract public key from private key - {e}")
MySystemUtils.custom_exit(1)
@staticmethod
def convert_pubkey_to_c(input_path, output_path, array_name):
try:
# Read PEM public key file
with open(input_path, "rb") as pem_file:
key_data = pem_file.read()
# Detect key type
key_type = PubKey2CConverter.detect_key_type(key_data)
print(f"Detected key type: {key_type}")
public_key = None
if key_type == "public":
# Directly load the public key
public_key = serialization.load_pem_public_key(key_data)
print("Processing public key directly")
elif key_type == "private":
# Extract the public key from the private key
public_key = PubKey2CConverter.extract_public_key_from_private(key_data)
print("Extracted public key from private key")
elif key_type == "unknown":
print(f"Error: The input file is neither a valid public key nor private key")
MySystemUtils.custom_exit(1)
else:
print(f"Error: Unknown key type detected")
MySystemUtils.custom_exit(1)
# Convert the public key to DER format
der_data = public_key.public_bytes(
serialization.Encoding.DER,
serialization.PublicFormat.SubjectPublicKeyInfo
)
# Create output header file
with open(output_path, "w") as out_file:
out_file.write("// Auto-generated by pubkey2c\n")
out_file.write(f"// Key type: {key_type}\n")
out_file.write(f"const unsigned char {array_name}[] = {{\n")
# Write the data in hexadecimal format, with 16 bytes per line.
for i, byte in enumerate(der_data):
if i % 16 == 0:
out_file.write(" ")
out_file.write(f"0x{byte:02x}")
if i != len(der_data) - 1:
out_file.write(",")
if i % 16 == 15 or i == len(der_data) - 1:
out_file.write("\n")
else:
out_file.write(" ")
# Define the size of the array to be written into
out_file.write(f"}};\n\n")
out_file.write(f"const unsigned int {array_name}_len = sizeof({array_name});\n")
print(f"Conversion successful!")
print(f"Input: {input_path} ({key_type} key)")
print(f"Output: {output_path} (array '{array_name}')")
print(f"DER data length: {len(der_data)} bytes")
except FileNotFoundError:
print(f"Error: File not found - {input_path}")
MySystemUtils.custom_exit(1)
except Exception as e:
print(f"Error: Failed to convert public key to C header - {e}")
MySystemUtils.custom_exit(1)
# Ensure all required packages are installed before running the script
MySystemUtils.install_missing_packages()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Sign a package locally or remotely.")
subparsers = parser.add_subparsers(dest="mode", required=True, help="Mode of operation: 'local', 'remote', or 'pubkey2c'")
# Local signing arguments
local_parser = subparsers.add_parser("local", help="Perform local signing")
local_parser.add_argument("-orig", required=True, help="Path to the original package")
local_parser.add_argument("-out", required=True, help="Path to save the output package")
local_parser.add_argument("-priv", required=True, help="Path to the private key")
# Remote signing arguments
remote_parser = subparsers.add_parser("remote", help="Perform remote signing")
remote_parser.add_argument("-orig", required=True, help="Path to the original package")
remote_parser.add_argument("-out", required=True, help="Path to save the output package")
remote_parser.add_argument("-url", required=True, help="HSM URL for remote signing")
remote_parser.add_argument("-token", required=True, help="HSM request token")
# Bin2C arguments
pubkey2c_parser = subparsers.add_parser("pubkey2c", help="Convert PEM public key file to C header file")
pubkey2c_parser.add_argument("-orig", required=True, help="Path to the input PEM public key or private key file")
pubkey2c_parser.add_argument("-out", required=True, help="Path to the output C header file")
pubkey2c_parser.add_argument("-array", required=True, help="Name of the array in the C header file")
args = parser.parse_args()
MySystemUtils.welcome_print()
if args.mode == "local":
orig_pack_path = args.orig
out_pack_path = args.out
priv_key_path = args.priv
sha256 = MySystemUtils.calc_sha256(orig_pack_path)
print(f"SHA256 value: {sha256}")
priv_key = MySystemUtils.load_private_key(priv_key_path)
x509_cert = MyCertificateBuilder(priv_key, sha256).build()
x509_cert_der = x509_cert.public_bytes(serialization.Encoding.DER)
MyFileManager.write_file(MySystemUtils.cert_file_path, x509_cert_der, "Certificate")
out_pack = MyPackBuilder(orig_pack_path, MySystemUtils.cert_file_path).build() # Changed from sign_pack to out_pack
MyFileManager.write_file(out_pack_path, out_pack, "Output pack") # Changed description
elif args.mode == "remote":
orig_pack_path = args.orig
out_pack_path = args.out
hsm_url = args.url
hsm_req_token = args.token
sha256 = MySystemUtils.calc_sha256(orig_pack_path)
print(f"SHA256 value: {sha256}")
MyRemoteSigner(hsm_url, hsm_req_token, sha256).handle_remote_signing(orig_pack_path, out_pack_path, MySystemUtils.cert_file_path) # Changed sign_pack_path to out_pack_path
elif args.mode == "pubkey2c":
orig_pubkey_path = args.orig
out_c_path = args.out
array_name = args.array
PubKey2CConverter.convert_pubkey_to_c(orig_pubkey_path, out_c_path, array_name)
MySystemUtils.custom_exit(0)