基于CAN通信的UDS安全访问算法服务器Python实现

本文仅介绍基本CAN报文收发的实现,所有USB-CAN硬件为ValueCAN;
pip install pycryptodome
pip install python-can
pip install python-ics
for value_can driver with python-can, the script must run under 32bit python!!!

1 组帧

#!/usr/bin/env python3
# File Name:UDSTransLayer.py
# Author   :Patek
# Data     :2019-03-04
# Note     : This script is used to $27 service of UDS.
import can
#import math
#import SecurityAccessAlgrithm as SA



#函数名:fun_Bytes_To_Hex_InStr
#参  数:bytes类型
#返  回:str类型
#说  明:将列表十进制数据元素转换为十六进制并以字符串形式返回,主要用于数据十六进制打印便于查看
def fun_Bytes_To_Hex_InStr(bytes):
    l = [('%02x' %i) for i in bytes]
    return "|".join(l) 

#函数名:fun_can_send_OneFrame
#参  数:bus(object),can_id(Number),can_data(list),is_extended_id=False
#返  回:None
#说  明:通过CAN报文DLC<=8时,调用本函数
def fun_can_send_OneFrame(bus,can_id,can_data,is_extended_id=False):
    Msg_send = can.Message(extended_id=is_extended_id,arbitration_id=can_id,data=can_data,)
    print(Msg_send)
    try:
        bus.send(Msg_send)
    except can.CanError:
        pass

#函数名:fun_can_rev_OneFrame
#参  数:总线对象
#返  回:CAN Msg对象
#说  明:CAN报文接收时,首先调用本函数
def fun_can_rev_OneFrame(bus):
    try:
        while True:
            Msg_recv = bus.recv(1)
            if Msg_recv is not None:
                print(Msg_recv)
                break
    except can.CanError:
        pass
    return Msg_recv

#函数名:fun_Get_PCI
#参  数:CAN Msg.data
#返  回:Number
#       0<->单帧
#       1<->首帧
#       2<->连续帧
#       3<->流控帧
def fun_Get_PCI(Msgdata):
    PCI = Msgdata[0] & 0xF0
    if PCI==0x00:		#单帧
        PCIFlag = 0
        return PCIFlag
    elif PCI==0x10:		#首帧
        PCIFlag = 1
        return PCIFlag
    elif PCI==0x20:     #连续帧
        PCIFlag = 2
        return PCIFlag
    elif PCI==0x30:     #流控帧
        PCIFlag = 3
        return PCIFlag

#函数名:fun_can_send_MultiFrame
#参  数:bus(object),can_id(Number),can_data(list),data_length(Number)
#返  回:None
#说  明:通过CAN报文发送多帧数据时调用本函数,函数默认一次性将多帧数据发出,忽略流控处理
def fun_can_send_MultiFrame(bus,can_id,can_data,data_length):
    FirstFrameData = []
    if (data_length//256 >= 16):
        pass
    else:
        FirstFrameData[0:0+2] = data_length.to_bytes(2,byteorder='big')
        FirstFrameData[0] += 0x10
        FirstFrameData[2:2+6] = can_data[0:0+6]
        fun_can_send_OneFrame(bus,can_id,FirstFrameData)
        FlowControlMsg = fun_can_rev_OneFrame(bus)
        
        if((data_length-6)%7==0):
            ConsecutiveFrameCounter = (data_length-6)//7
            for i in range(1,ConsecutiveFrameCounter+1,1):
                ConsecutiveFrameData=[]
                SN = i%16
                ConsecutiveFrameData[0:0+1]=(SN+0x20).to_bytes(1,byteorder='big')
                ConsecutiveFrameData[1:1+7]=can_data[6+(i-1)*7:6+7*i]
                #print(fun_Bytes_To_Hex_InStr(ConsecutiveFrameData))
                fun_can_send_OneFrame(bus,can_id,ConsecutiveFrameData)
        else:
            ConsecutiveFrameCounter = (data_length-6)//7 + 1
            remainder = (data_length-6)%7
            for i in range(1,ConsecutiveFrameCounter+1,1):
                if(i<ConsecutiveFrameCounter):
                    ConsecutiveFrameData=[]
                    SN = i%16
                    ConsecutiveFrameData[0:0+1]=(SN+0x20).to_bytes(1,byteorder='big')
                    ConsecutiveFrameData[1:1+7]=can_data[6+(i-1)*7:6+7*i]
                    #print(fun_Bytes_To_Hex_InStr(ConsecutiveFrameData))
                    fun_can_send_OneFrame(bus,can_id,ConsecutiveFrameData)
                else:
                    ConsecutiveFrameData=[]
                    SN = i%16
                    ConsecutiveFrameData[0:0+1]=(SN+0x20).to_bytes(1,byteorder='big')
                    ConsecutiveFrameData[1:1+remainder]=can_data[data_length-remainder:data_length]
                    #print(fun_Bytes_To_Hex_InStr(ConsecutiveFrameData))
                    fun_can_send_OneFrame(bus,can_id,ConsecutiveFrameData)
        #print(fun_Bytes_To_Hex_InStr(FirstFrameData))

#函数名:fun_can_recv_MultiFrame
#参  数:总线对象,接收数据长度
#返  回:列表
#说  明:调用该函数前,先调用Get_PCI函数,判断接受的第一帧数据为首帧数据,并将首帧的数据长度参数传入;
#       本函数返回的Data中不包括首帧中的数据
def fun_can_recv_MultiFrame(bus,data_length):	
    MultiFrame_Recv_Data=[]						
    RecievedData=[]
    if((data_length-6)%7==0):
        FrameRecvCnter = (data_length-6)//7
        for i in range(0,FrameRecvCnter):
            Msg_recv = fun_can_rev_OneFrame(bus)
            MultiFrame_Recv_Data.append(Msg_recv.data)
    else:
        print("Entered CF recieve function...")
        FrameRecvCnter = (data_length-6)//7 + 1
        for i in range(0,FrameRecvCnter):
            Msg_recv = fun_can_rev_OneFrame(bus)
            MultiFrame_Recv_Data.append(Msg_recv.data)

    FrameNumber = len(MultiFrame_Recv_Data)

    if((data_length-6)%7==0):							  #去除SN保留实际数据
        for i in range(0,FrameNumber,1):
            RecievedData += MultiFrame_Recv_Data[i][1:1+7]		
    else:
        DataRemainder = (data_length-6)%7
        for i in range(0,FrameNumber,1):
            if(i<FrameNumber-1):
                RecievedData += MultiFrame_Recv_Data[i][1:1+7]
            else:
                RecievedData += MultiFrame_Recv_Data[i][1:1+remainder]

    return RecievedData




if __name__ == '__main__':
    data=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38]
    data*=9
    #print('Data Len is {}:\n {}'.format(len(data),data))
    #fun_can_send_data(bus,CAN_ID_TX,data)
    #msg1 = can.Message(data=[0x02,2,3])
    #PCI = fun_Get_PCI(msg1.data)
    #print("PCI = 0x%2X" %PCI)
    #fun_can_send_MultiFrame(bus,CAN_ID_TX,data,len(data))

2 安全访问算法实现

#!/usr/bin/env python3
# File Name:SecurityAccessAlgrithm.py
# Author   :Patek 
# data     :2019-02-28
# Note     : This script is used to $27 service of UDS.
import hashlib
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes

def fun_Bytes_To_Hex_Instr(bytes):
	l = [('%2x' %i) for i in bytes]
	#print(l)
	return "|".join(l)

def fun_Generate_Secret_Seed_Inbytes():
	PUN = get_random_bytes(8)
	SecLev =bytes([0x01]) 
	SessNum = bytes([0x02])
	ECUSeriNum = get_random_bytes(20)
	return (PUN+SecLev+SessNum+ECUSeriNum)

def fun_Generate_Secret_Key_Inbytes(PUN_Get_from_Seed_Inbytes):
	Fingerprint = get_random_bytes(16)
	secretekey =PUN_Get_from_Seed_Inbytes+Fingerprint
	return secretekey

def fun_FillTo32ByteInPKCS7(data_In_bytes):
	DataLength = len(data_In_bytes)
	print("Orignal seed length is :",DataLength)
	residue = 16 - (DataLength%16)
	list_t = []
	for i in range(residue):
		list_t.append(residue)
	result = data_In_bytes+bytes(list_t)
	return result

def fun_ReverseFillTo32ByteInPKCS7(data_In_bytes):
	data = data_In_bytes
	if data[-1] == 0x01:
		return data[:-1]
	elif (data[-1] == 0x02 and data[-2]==0x02):
		return data[:-2]
	elif (data[-1] == 0x03 and data[-2]==0x03 and data[-3]==0x03):
		return data[:-3]
	elif (data[-1] == 0x04 and data[-2]==0x04 and data[-3]==0x04 and data[-4]==0x04):
		return data[:-4]
	elif (data[-1] == 0x05 and data[-2]==0x05 and data[-3]==0x05 and data[-4]==0x05 and data[-5]==0x05):
		return data[0:-5]
	elif (data[-1] == 0x06 and data[-2]==0x06 and data[-3]==0x06 and data[-4]==0x06 and data[-5]==0x06 and data[-6]==0x06):
		return data[:-6]
	elif (data[-1] == 0x07 and data[-2]==0x07 and data[-3]==0x07 and data[-4]==0x07 and data[-5]==0x07 and data[-6]==0x07 and data[-7]==0x07):
		return data[:-7]
	elif (data[-1] == 0x08 and data[-2]==0x08 and data[-3]==0x08 and data[-4]==0x08 and data[-5]==0x08 and data[-6]==0x08 and data[-7]==0x08 and data[-8]==0x08):
		return data[:-8]
	elif (data[-1] == 0x09 and data[-2]==0x09 and data[-3]==0x09 and data[-4]==0x09 and data[-5]==0x09 and data[-6]==0x09 and data[-7]==0x09 and data[-8]==0x09 and data[-9]==0x09):
		return data[:-9]
	elif (data[-1] == 0x0a and data[-2]==0x0a and data[-3]==0x0a and data[-4]==0x0a and data[-5]==0x0a and data[-6]==0x0a and data[-7]==0x0a and data[-8]==0x0a and data[-9]==0x0a and data[-10]==0x0a):
		return data[:-10]
	elif (data[-1] == 0x0b and data[-2]==0x0b and data[-3]==0x0b and data[-4]==0x0b and data[-5]==0x0b and data[-6]==0x0b and data[-7]==0x0b and data[-8]==0x0b and data[-9]==0x0b and data[-10]==0x0b and data[-11]==0x0b):
		return data[:-11]
	elif (data[-1] == 0x0c and data[-2]==0x0c and data[-3]==0x0c and data[-4]==0x0c and data[-5]==0x0c and data[-6]==0x0c and data[-7]==0x0c and data[-8]==0x0c and data[-9]==0x0c and data[-10]==0x0c and data[-11]==0x0c and data[-12]==0x0c):
		return data[:-12]
	elif (data[-1] == 0x0d and data[-2]==0x0d and data[-3]==0x0d and data[-4]==0x0d and data[-5]==0x0d and data[-6]==0x0d and data[-7]==0x0d and data[-8]==0x0d and data[-9]==0x0d and data[-10]==0x0d and data[-11]==0x0d and data[-12]==0x0d and data[-13]==0x0d):
		return data[:-13]
	elif (data[-1] == 0x0e and data[-2]==0x0e and data[-3]==0x0e and data[-4]==0x0e and data[-5]==0x0e and data[-6]==0x0e and data[-7]==0x0e and data[-8]==0x0e and data[-9]==0x0e and data[-10]==0x0e and data[-11]==0x0e and data[-12]==0x0e and data[-13]==0x0e and data[-14]==0x0e):
		return data[:-14]
	elif (data[-1] == 0x0f and data[-2]==0x0f and data[-3]==0x0f and data[-4]==0x0f and data[-5]==0x0f and data[-6]==0x0f and data[-7]==0x0f and data[-8]==0x0f and data[-9]==0x0f and data[-10]==0x0f and data[-11]==0x0f and data[-12]==0x0f and data[-13]==0x0f and data[-14]==0x0f and data[-15]==0x0f):
		return data[:-15]

def SHA1_Hash(Input_In_bytes):
	SHA1 = hashlib.sha1()
	SHA1.update(Input_In_bytes)
	digest = SHA1.digest()
	print("Digest calculated expressed in hex value is :\n",SHA1.hexdigest().upper())
	return digest

def fun_Get_Encrypt_Data_byAESCBC(key_bytes,encrypt_mode,initial_vector_bytes,iput_data_after_padding_bytes):
	cipher = AES.new(key_bytes, encrypt_mode, initial_vector_bytes)
	ct_bytes = cipher.encrypt(iput_data_after_padding_bytes)
	return ct_bytes

def fun_Get_Decrypt_Data_byAESCBC(key_bytes,encrypt_mode,initial_vector_bytes,cipher_data_bytes):
	cipher = AES.new(key_bytes, encrypt_mode, initial_vector_bytes)
	pt_bytes = cipher.decrypt(cipher_data_bytes)
	return pt_bytes

if __name__ == '__main__':
	#seed  = bytes([0xDA,0xAD,0xBC,0xDA,0x21,0x28,0xC1,0x5C,0xB1,0xA0,0xB0,0xFC,0xCC,0xBB,0xCD,0xCA,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18])
	seed = get_random_bytes(30)
	#key  = bytes([0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f])
	statickey   = bytes([0xDA,0xAD,0xBC,0xDA,0x21,0x28,0xC1,0x5C,0xB1,0xA0,0xB0,0xFC,0xCC,0xBB,0xCD,0xCA]) 
	iv          = bytes([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00])
	#print("Original seed in bytes type is :\n",seed)
	print("Expressed by hex value,the original data of seed is :\n",fun_Bytes_To_Hex_Instr(seed))
	SHA1Digest = SHA1_Hash(seed)
	#print("Digest calculated from seed expressed in bytes type is :\n",SHA1Digest)								
																												
	FilledData = fun_FillTo32ByteInPKCS7(seed)
	#print("After filled according PKCS7 standar the seed expressed in bytes type is:\n",FilledData)
	print("After filled according PKCS7 standar the seed expressed in hex type is:\n",fun_Bytes_To_Hex_Instr(FilledData))
	print("\n-----------------------Encrypt the filled seed in PKCS7 standar with AES128-CBC algrithm------------------------\n")
	cipther_text = fun_Get_Encrypt_Data_byAESCBC(statickey,AES.MODE_CBC,iv,FilledData)
	print("The cipher data is:\n",fun_Bytes_To_Hex_Instr(cipther_text))
	print("\n---------------------------Decrypt the cipher data in with AES128-CBC algrithm----------------------------------\n")
	plain_text = fun_Get_Decrypt_Data_byAESCBC(statickey,AES.MODE_CBC,iv,cipther_text)
	print("The plain data is:\n",fun_Bytes_To_Hex_Instr(plain_text))
	print("\n-----------------------------Detach plain data according to PKCS7 standard--------------------------------------\n")
	plain_text_detached = fun_ReverseFillTo32ByteInPKCS7(plain_text)
	print("The plain data detached is:\n",fun_Bytes_To_Hex_Instr(plain_text_detached))
	#print("Test for generated secrete seed data type:\n",fun_Generate_Secret_Seed_Inbytes())
	#print("Test for generated secrete seed data expressed in hex:\n",fun_Bytes_To_Hex_Instr(fun_Generate_Secret_Seed_Inbytes()))

	#attached = bytes([0x8,0xb6,0xf1,0x87,0x35,0x32,0x38,0x82,0x78,0x91,0x89,0x35,0x0e,0xb6,0x68,0x1b,0xb9,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f])
	#detached = fun_ReverseFillTo32ByteInPKCS7(attached)
	#print(fun_Bytes_To_Hex_Instr(detached))

3 应用层

#!/usr/bin/env python3
# FileName:UDS_$27.py
# Author: Patek
# Data  : 2019-03-04
# Note  : This script is used to $27 service of UDS
import can
import SecurityAccessAlgrithm as SA
import UDSTransLayer as Transmitter
from Crypto.Cipher import AES

#bus初始化配置
bus = can.interface.Bus(bustype='neovi', channel=1, bitrate=500000)
CAN_ID_TX=0x7F8
CAN_ID_RX=0x7F0
can_filters = [ 
    {"can_id": 0x7F0, "can_mask": 0x7FF, "extended": False},           
]																	#报文过滤器设置,接受CAN ID0x7F8CAN报文

bus.set_filters(can_filters)

Statickey    = bytes([0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F])
iv           = bytes([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00])
FingerPrint  = bytes([0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F])



while True:
    print('Script is on running for recieving can data.....')
    Msg = Transmitter.fun_can_rev_OneFrame(bus)
    print('Recieved a message.....')
    if(1 == Transmitter.fun_Get_PCI(Msg.data)):
        
        if(Msg.data[2]==0x27 and Msg.data[3]==0x03):					#send seed  27 03 
            print('Data recieving....\n')							    #return key 27 04
            RecvData=[]													
            RecvData += Msg.data

            print("After recieve the first frame the buffered data in RecvData is :\n", SA.fun_Bytes_To_Hex_Instr(RecvData))
            RecvDataLength = ((Msg.data[0] &0x0F) << 8) | Msg.data[1]
            print("The data need to be received during the consecutive frame is :%d \n" %RecvDataLength)

            FlowControlData = [0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00]

            Transmitter.fun_can_send_OneFrame(bus,CAN_ID_TX,FlowControlData)	#对首帧回复流控帧
            print("FlowControl frame has been sent....\n")

            RecvData += Transmitter.fun_can_recv_MultiFrame(bus,RecvDataLength)
            print("RecvData is:\n ",SA.fun_Bytes_To_Hex_Instr(bytes(RecvData)))

            SecuritySeed = RecvData[4:4+RecvDataLength-2]
            print("SecuritySeed is:\n ",SA.fun_Bytes_To_Hex_Instr(SecuritySeed))

            SecretSeed = SA.fun_Get_Decrypt_Data_byAESCBC(Statickey,AES.MODE_CBC,iv,bytes(SecuritySeed))[0:0+30]
            print("SecretSeed is:\n ",SA.fun_Bytes_To_Hex_Instr(SecretSeed))

            Sessionkey = SA.SHA1_Hash(SecretSeed)[0:0+16]
            print("Sessionkey is:\n ",SA.fun_Bytes_To_Hex_Instr(Sessionkey))

            SecretKey  = SecretSeed[0:0+8]+FingerPrint
            print("Secretkey is:\n ",SA.fun_Bytes_To_Hex_Instr(SecretKey))

            Secretkey_Filled = SA.fun_FillTo32ByteInPKCS7(SecretKey)
            print("Secretkey_Filled is:\n ",SA.fun_Bytes_To_Hex_Instr(Secretkey_Filled))

            SecurityKey=SA.fun_Get_Encrypt_Data_byAESCBC(Sessionkey,AES.MODE_CBC,iv,Secretkey_Filled)
            print("SecurityKey is:\n ",SA.fun_Bytes_To_Hex_Instr(SecurityKey))

            SecurityKey_send = bytes([0x27,0x04])
            SecurityKey_send += SecurityKey

            Transmitter.fun_can_send_MultiFrame(bus,CAN_ID_TX,SecurityKey_send,len(SecurityKey_send))
        else:
            pass
    else:
        pass


        





        




UDS安全访问算法主要用于解锁车辆中的ECU。安全访问服务通过发送种子请求和发送安全密钥来实现解锁。首先,客户端发送种子请求,该请求包含一个安全访问服务标识符(0x27)。ECU收到种子请求后,会生成一个种子响应帧,其中包含一个种子值。客户端接收到种子响应帧后,通过发送SendKey请求帧,将种子值作为参数进行加密处理,并发送给ECU。ECU收到SendKey请求帧后,会解密种子值,并验证其有效性。如果验证成功,ECU将发送一个SendKey响应帧,表示解锁成功。如果验证失败,ECU将发送一个否定响应消息,表示解锁失败。 这是UDS安全访问算法的基本流程。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [UDS诊断系列之七 安全访问(27)服务](https://blog.csdn.net/kalake/article/details/125984985)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [一文理解UDS安全访问服务(0x27)](https://blog.csdn.net/huihuige092/article/details/126465150)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汽车电子开发攻城狮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值