본 문서는 스마트 제어 충전기(K-VAS)의 배터리 상태정보 수집 기능을 CSMS와 연동하기 위한 상세 규격입니다. 전체 플로우와 모든 메시지의 요청/응답 샘플을 포함합니다. 연결·표준 메시지·DataTransfer 공통 규칙은 OCPP 1.6 연동 가이드를 먼저 참조하십시오.
0. 문서 개요
| 항목 | 내용 |
|---|---|
| 대상 독자 | 충전기 펌웨어/연동 개발자 |
| 대상 프로토콜 | OCPP 1.6 (JSON) — 모든 K-VAS 메시지는 DataTransfer로 교환 |
| vendorId | com.bionever |
| 근거 규격 | 환경부 「전기자동차 배터리 상태정보 수집 가이드 v1.3」 + CSMS 확장 규격 |
용어
| 용어 | 의미 |
|---|---|
| EVSE / CP | 충전기. 차량에서 배터리 정보를 수집·암호화해 CSMS로 전송하는 주체 |
| CSMS | 충전 관리 시스템(본 시스템). 키·인증서 중계와 환경부 전달을 담당 |
| KECO / 환경부 연계서버 | 한국환경공단 배터리 상태정보 수집 서버 |
| K-VAS | ISO 15118 부가가치 서비스(VAS) 기반 배터리 정보교환 규격 |
| SoC | 배터리 충전 상태(%) |
세션키 / keyId | 배터리 데이터 암호화용 임시 대칭키와 그 일련번호(16자리) |
1. 아키텍처와 사전 조건
다이어그램 로딩 중…
- 배터리 데이터는 충전기가 암호화(AES-CBC-128 + HMAC-SHA256, 4장)해 보내며, CSMS는 복호화하지 않고 환경부로 중계만 합니다.
- CSMS가 자동으로 수행: 환경부 암호화 키 발급/갱신 → 충전기 설치, 배터리 데이터 수신·적재 → 환경부 전송(재시도 포함).
사전 조건 (연동 시작 전 확인)
| # | 조건 | 담당 |
|---|---|---|
| 1 | 충전기가 OCPP_16_BIONEVER 프로파일로 등록되고 KVAS 사용이 켜져 있음 | CSMS 운영팀 |
| 2 | CPO에 환경부 기관 인증키(bkey)가 설정되어 있음 | CSMS 운영팀 |
| 3 | 충전기 인증서/개인키 + 환경부 서버 인증서가 충전기에 설치되어 있음 (2.1) | 운영팀 + 충전기 |
| 4 | TLS(wss) 연결 — 인증서 배포는 TLS 연결에서만 수행됨 | 충전기 |
KVAS 사용이 꺼진 충전기가 배터리 수집 메시지(
ReportBatteryData·세션키·인증서)를 보내면 Rejected(message: "KVAS is not enabled for this charger")로 응답합니다. 단 SetChargePreference·SetPrivacyConsent는 KVAS 사용 여부와 무관하게 처리됩니다.2. 전체 플로우 총괄
K-VAS 연동은 ① 사전 준비(인증서·세션키) ② 충전 세션(현장 2-A 또는 원격 2-B) ③ 충전 중 배터리 전송 ④ CSMS→환경부 전달(충전기 관여 없음)의 4단계로 구성됩니다.
다이어그램 로딩 중…
2.1 사전 준비 — 인증서·세션키 설치
최초 설치(또는 인증서 재발급) 시 1회 수행합니다. 인증서가 있어야 세션키 서명 검증과 배터리 암호화가 가능합니다.
다이어그램 로딩 중…
2.2 현장 인증 충전 (2-A) — 전체 시퀀스와 프레임 샘플
다이어그램 로딩 중…
(1)→(2) 순서 보장: 각 메시지의
Accepted를 받은 뒤 다음 단계로 진행합니다. (1)에서 한도(wh/amount)를 함께 보내면 (3) 거래 시작 시 자동 이관됩니다(idTag 일치 또는 30분 이내 — 3.1).(1) SetChargePreference — 프레임 샘플
→ [2, "a001", "DataTransfer", {
"vendorId": "com.bionever",
"messageId": "SetChargePreference",
"data": { "connectorId": 1, "targetSoC": 80, "limitWh": 30500, "limitAmount": 10000, "idTag": "CARD-0001" }
}]
← [3, "a001", { "status": "Accepted", "data": {} }](2) SetPrivacyConsent — 프레임 샘플
→ [2, "a002", "DataTransfer", {
"vendorId": "com.bionever",
"messageId": "SetPrivacyConsent",
"data": {
"connectorId": 1,
"timestamp": "2026-07-19T10:30:00.000Z",
"idTag": "CARD-0001",
"policies": [
{ "policyCode": "PRIVACY_COLLECT", "consented": true },
{ "policyCode": "THIRD_PARTY_KECO", "consented": true }
]
}
}]
← [3, "a002", { "status": "Accepted", "data": {} }](4) ReportBatteryData — 프레임 샘플 (2건 적재분)
→ [2, "a010", "DataTransfer", {
"vendorId": "com.bionever",
"messageId": "ReportBatteryData",
"data": {
"connectorId": 1,
"batteryDataSet": [
{ "timeStamp": "2026-07-19T10:35:00.000Z", "sessionDuration": "300", "counter": "1",
"batteryData": "XfdsySC7Q1g4OHKh7/6gbTI1MDQyNDA1Mzk3MTA3Nji..." },
{ "timeStamp": "2026-07-19T10:35:03.000Z", "sessionDuration": "303", "counter": "2",
"batteryData": "kQs2miCZASC7Q1g4OHKh7/6gbTI1MDQyNDA1Mzk3MTA..." }
]
}
}]
← [3, "a010", { "status": "Accepted", "data": {} }]2.3 원격 시작 충전 (2-B) — 전체 시퀀스와 프레임 샘플
다이어그램 로딩 중…
(3) CSMS 발신 SetChargePreference — 프레임 샘플 (충전기가 응답)
← [2, "c001", "DataTransfer", {
"vendorId": "com.bionever",
"messageId": "SetChargePreference",
"data": { "connectorId": 1, "targetSoC": "100" }
}]
→ [3, "c001", { "status": "Accepted" }](4) CSMS 발신 SetPrivacyConsent — 프레임 샘플 (신·구 포맷 병행)
← [2, "c002", "DataTransfer", {
"vendorId": "com.bionever",
"messageId": "SetPrivacyConsent",
"data": {
"connectorId": 1,
"timestamp": "2026-07-19T10:31:00.000Z",
"consented": true,
"policies": [ { "policyCode": "DEFAULT", "consented": true } ],
"idTag": "MEMBER-0042"
}
}]
→ [3, "c002", { "status": "Accepted" }]2.4 세션키 수명 주기
다이어그램 로딩 중…
- CSMS가 1시간 주기로 점검해 키 없음/만료 임박 충전기에 자동 발급·설치합니다.
- 충전기가 키 없음/만료를 감지하면
RequestBatteryEncKey(3.4)로 직접 요청할 수 있습니다 — 이 경우 충전 중이어도 즉시 설치가 전송됩니다. - 설치 실패(
Rejected) 시 CSMS가 재시도하며 3회 연속 실패하면 운영자에게 통보됩니다. 새 키Accepted후에는 이전 키를 폐기하십시오.
2.5 CSMS → 환경부 전달 (참고 — 충전기 관여 없음)
수신된 배터리 데이터는 CSMS가 충전기 식별자(BID+SID+CID)·충전 시작 시각·keyId를 붙여 환경부 rcvData API로 전달합니다(요청당 최대 20건). 실패 시 60초 후 재전송하며 동일 실패 코드 3회면 중단 후 운영자 통보합니다. 개인정보 미동의 세션의 데이터는 전달하지 않습니다(수신 시 Accepted는 반환).
3.1 SetChargePreference — 사용자 충전 설정 (CP ↔ CSMS)
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
connectorId | int | 필수 | 커넥터 번호 (1 이상, 0 금지) |
targetSoC | integer | 필수 | 목표 SoC 0~100. 한도만 설정 시 100 권장 |
limitWh | number | 선택 | 충전량 한도 (Wh, 양수) — 도달 시 CSMS가 원격 정지 |
limitAmount | number | 선택 | 금액 한도 (원, 양수) — 도달 시 CSMS가 원격 정지 |
idTag | string | 선택 | 설정 주체 idTag (100자 이내) |
한도 반영: 거래 시작 전 수신분은 보관 후 거래 시작 시 이관(
idTag를 보냈으면 충전 인증 idTag와 일치할 때만, 없으면 30분 이내). 충전 중 재수신은 진행 중 거래에 즉시 반영. 선결제 등 결제 기반 한도가 우선. 구 SetChargeLimit은 제거됨(UnknownMessageId).요청/응답 샘플 — 정상
→ [2, "m101", "DataTransfer", {
"vendorId": "com.bionever", "messageId": "SetChargePreference",
"data": { "connectorId": 1, "targetSoC": 80, "limitWh": 30500, "limitAmount": 10000, "idTag": "CARD-0001" }
}]
← [3, "m101", { "status": "Accepted", "data": {} }]응답 샘플 — 거부 (SoC 범위 밖)
→ data: { "connectorId": 1, "targetSoC": 101 }
← [3, "m102", { "status": "Rejected", "data": { "message": "targetSoC must be 0..100" } }]3.2 SetPrivacyConsent — 개인정보 정책 동의 (CP ↔ CSMS)
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
connectorId | int | 필수 | 커넥터 번호 (1 이상) |
timestamp | string | 필수 | 동의 확인 시각 (ISO 8601, UTC) — 목록 공통 |
idTag | string | 선택 | 동의 주체 idTag (100자 이내) |
policies | array | 필수 | 정책별 동의 목록 (1건 이상, policyCode 중복 불가) |
policies[].policyCode | string | 필수 | 정책 코드 (50자 이내, 운영팀과 합의) |
policies[].consented | boolean | 필수 | 해당 정책 동의 여부 |
전송 게이트: 세션에서 수신된 모든 정책이 동의일 때만 배터리 데이터가 환경부로 전달됩니다. 같은
policyCode 재전송 시 최신 값으로 갱신(부분 정정 가능).요청/응답 샘플 — 일부 미동의 (수신은 Accepted, 환경부 전달은 차단됨)
→ [2, "m201", "DataTransfer", {
"vendorId": "com.bionever", "messageId": "SetPrivacyConsent",
"data": {
"connectorId": 1, "timestamp": "2026-07-19T10:30:00.000Z",
"policies": [
{ "policyCode": "PRIVACY_COLLECT", "consented": true },
{ "policyCode": "THIRD_PARTY_KECO", "consented": false }
]
}
}]
← [3, "m201", { "status": "Accepted", "data": {} }]구 단건 포맷 미지원
→ data: { "connectorId": 1, "timestamp": "2026-07-19T10:30:00.000Z", "consented": true }
← [3, "m202", { "status": "Rejected", "data": { "message": "policies must be a non-empty array" } }]
※ 단일 consented Boolean 포맷은 지원하지 않는다 — policies 목록 필수응답 샘플 — 거부 (정책 코드 중복)
← [3, "m203", { "status": "Rejected", "data": { "message": "duplicated policyCode: THIRD_PARTY_KECO" } }]3.3 ReportBatteryData — 배터리 데이터 전송 (CP → CSMS)
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
connectorId | int | 필수 | 커넥터 번호 (1 이상) |
batteryDataSet | array | 필수 | 배터리 데이터 배열 (아래 표, 1건 이상) — 건수 필드 없음(배열 길이 = 건수) |
batteryDataSet 원소
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
timeStamp | string | 택1* | 배터리 정보 취득 시각 (ISO 8601, UTC) |
sessionDuration | string | 택1* | 배터리 정보교환 TCP 세션 지속시간(초) |
counter | string | 택1* | 프레임 전송 일련번호 — 세션 내 연속 증가 |
batteryData | string | 필수 | 암호화 후 base64 인코딩한 배터리 데이터 (평문 전송 금지) |
* timeStamp/sessionDuration/counter 중 최소 1개는 평문 필수 (환경부 규격)
전송 트리거: 3초마다 적재 →
BatteryReportMaxRecords 도달 또는 최초 적재 후 1분 경과 중 먼저 충족되는 시점에 즉시 전송. 전송 후 버퍼·1분 타이머 초기화. 네트워크 단절 시 버퍼 유지 → 재연결 후 counter 연속성을 유지한 채 순서대로 전송.요청/응답 샘플 — 정상 (2.2 (4)와 동일 구조)
→ [2, "m301", "DataTransfer", {
"vendorId": "com.bionever", "messageId": "ReportBatteryData",
"data": {
"connectorId": 1,
"batteryDataSet": [
{ "timeStamp": "2026-07-19T10:35:00.000Z", "counter": "1",
"batteryData": "XfdsySC7Q1g4OHKh7/6gbTI1MDQyNDA1Mzk3MTA3Nji..." }
]
}
}]
← [3, "m301", { "status": "Accepted", "data": {} }]응답 샘플 — 거부 (세션키 미설치)
← [3, "m302", { "status": "Rejected",
"data": { "message": "no active battery encryption key for this charger" } }]
→ 이 경우: RequestBatteryEncKey(3.4)로 키를 요청하고, 버퍼를 유지한 채 키 설치 후 재전송Rejected 수신 시 버퍼를 버리지 말고 백오프 재전송하십시오. (SendSmartChargeBatteryData라는 messageId도 동일하게 처리됩니다 — 신규 구현은 ReportBatteryData 사용)3.4 RequestBatteryEncKey — 세션키 발급 요청 (CP → CSMS)
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
connectorId | int | 필수 | 커넥터 번호 (1 이상) |
timestamp | string | 선택 | 요청 시각 (ISO 8601, UTC) |
요청/응답 샘플
→ [2, "m401", "DataTransfer", {
"vendorId": "com.bionever", "messageId": "RequestBatteryEncKey",
"data": { "connectorId": 1, "timestamp": "2026-07-19T10:00:00.000Z" }
}]
← [3, "m401", { "status": "Accepted", "data": {} }]
※ 응답에 키가 실리지 않음 — 곧이어 CSMS가 InstallBatteryEncKey(3.5)를 별도 전송응답 샘플 — 거부 (발급 불가)
← [3, "m402", { "status": "Rejected", "data": { "message": "Session key is not available" } }]
→ 원인: CPO 기관 인증키(bkey) 미설정, 환경부 발급 오류 등 — CSMS 운영팀 확인 필요3.5 InstallBatteryEncKey — 세션키 설치 (CSMS → CP)
| 필드 | 타입 | 설명 |
|---|---|---|
connectorId | int | 1 고정 |
keyId | string | 키 일련번호 16자리 — 배터리 암호화·환경부 전송에 사용 |
encryptPub | string | 임시 공유 공개키 A_pub (base64, X.509 SubjectPublicKeyInfo) — 환경부 규격 필드명 |
signData | string | 환경부 개인키로 keyId + A_pub를 서명한 값 (base64) |
validFrom / validTo | string | 키 유효기간 (ISO 8601, UTC) |
수신/응답 샘플 (충전기가 응답)
← [2, "k501", "DataTransfer", {
"vendorId": "com.bionever", "messageId": "InstallBatteryEncKey",
"data": {
"connectorId": 1,
"keyId": "2607190412345678",
"encryptPub": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE32JH3n6M38zsYGOAB3a3N1Reuz...",
"signData": "MEUCIQDJJE7Co/j+s1xrBDr6td3Se41ORscge85Fa+zs4dGpogIgdwwBWya1Tu8...",
"validFrom": "2026-07-19T00:00:00Z",
"validTo": "2026-08-18T00:00:00Z"
}
}]
→ [3, "k501", { "status": "Accepted" }] // 서명 검증·키 파생 성공 시
→ [3, "k501", { "status": "Rejected" }] // 검증 실패 시 — CSMS가 재시도, 3회 실패 시 운영자 통보충전기 처리 절차
- 환경부 서버 인증서의 공개키로 서명 검증:
Verify(keyId바이트(UTF-8) + Base64Decode(encryptPub), Base64Decode(signData)) - 검증 성공 시 세션키 파생(4장) 후 기존 키 교체,
Accepted응답 - 이후
ReportBatteryData의 암호문은 이 키로 생성 (CSMS는 이keyId를 환경부 전송에 사용)
CSMS는 충전 중에는 키 교체를 보류합니다(세션 중 keyId 불일치 방지). 단 충전기가 3.4로 직접 요청한 경우에는 충전 중에도 즉시 전송됩니다.
3.6 인증서 요청·설치 — RequestBatteryCertificate / InstallBatteryCertificate
RequestBatteryCertificate (CP → CSMS) — 요청/응답 샘플
→ [2, "m601", "DataTransfer", {
"vendorId": "com.bionever", "messageId": "RequestBatteryCertificate",
"data": { "connectorId": 1, "timestamp": "2026-07-19T09:00:00.000Z" }
}]
← [3, "m601", { "status": "Accepted", "data": {} }]
※ CSMS에 보관된 인증서가 있으면 곧이어 InstallBatteryCertificate가 별도 전송됨InstallBatteryCertificate (CSMS → CP) — 수신/응답 샘플
← [2, "k601", "DataTransfer", {
"vendorId": "com.bionever", "messageId": "InstallBatteryCertificate",
"data": {
"connectorId": 1,
"certificates": {
"chargerCert": "-----BEGIN CERTIFICATE-----\nMIICVTCC...\n-----END CERTIFICATE-----",
"chargerKey": "-----BEGIN PRIVATE KEY-----\nMIGTAgEA...\n-----END PRIVATE KEY-----",
"kecoCert": "-----BEGIN CERTIFICATE-----\nMIICojCC...\n-----END CERTIFICATE-----"
}
}
}]
→ [3, "k601", { "status": "Accepted" }]| certificates 필드 | 용도 |
|---|---|
chargerCert | 충전기 인증서 (환경부 발급, CN = BID+SID+CID) |
chargerKey | 충전기 개인키 — ECDHE 공유 비밀 생성(4장)에 사용 |
kecoCert | 환경부 서버 인증서 — 세션키 서명 검증(3.5)에 사용 |
개인키가 전달되므로 TLS(wss) 연결에서만 배포됩니다(평문 연결이면 CSMS가 거부). 수신한 개인키는 보안 저장소에 보관하고 외부로 노출하지 마십시오.
4. 배터리 데이터 암호화
환경부 「전기자동차 배터리 상태정보 수집 가이드 v1.3」 규격입니다. 환경부 제공 샘플 코드 (Java/C++)를 참조하십시오.
세션키 파생
1) 공유 비밀 생성
Z = ECDHE(충전기 개인키, A_pub) // P-256
2) 키 파생
Output = KDF({Z, keydatalen=384, AlgorithmID=0x01, ID_U=0x55, ID_V=0x56})
// NIST SP 800-56A Concatenation KDF, 해시 SHA-384, 출력 384bit
3) 키 분리
AES_KEY = Output 상위 128bit
HMAC_KEY = Output 하위 256bit배터리 데이터 암호화
- 암호화: AES-CBC-128 (NIST 800-38A)
- 무결성: HMAC-SHA256
- 메시지 구조: RFC 5246 기반 + keyID 추가
- 전송: 암호문을 base64 인코딩하여 batteryData 필드에 탑재 (원문 평문 전송 금지)서명 검증 (InstallBatteryEncKey 수신 시)
verifyBytes = UTF8(keyId) || Base64Decode(encryptPub) // 이어붙임
verifyOk = ECDSA_Verify(kecoPublicKey, verifyBytes, Base64Decode(signData))
// 환경부 공개키는 kecoCert(3.6)에서 추출5. 오류 처리 매트릭스
| status | 의미 | 충전기 조치 |
|---|---|---|
Accepted | 정상 처리 | 다음 단계 진행 |
Rejected | data.message에 사유 | 사유 확인. ReportBatteryData는 버퍼 유지 후 백오프 재전송 |
UnknownMessageId | 미지원 messageId | 배포 버전/규격 확인 (구 SetChargeLimit 등) |
UnknownVendorId | vendorId 불일치 | com.bionever 확인 |
메시지별 주요 Rejected 사유
| 메시지 | 사유 (data.message) |
|---|---|
SetChargePreference | connectorId must be >= 1 · targetSoC must be 0..100 · limitWh must be a positive number · limitAmount must be a positive number · idTag must be <= 100 chars |
SetPrivacyConsent | timestamp must be ISO 8601 UTC · policies must be a non-empty array · policyCode is required for each policy · duplicated policyCode: ... |
ReportBatteryData | batteryDataSet must be a non-empty array · batteryData is required · one of timeStamp/sessionDuration/counter is required · no active battery encryption key for this charger · no charging transaction for this connector |
| KVAS 전용 메시지 공통 | KVAS is not enabled for this charger — CSMS 운영팀에 KVAS 사용 설정 요청 |
재시도 정책 요약
| 상황 | 동작 |
|---|---|
ReportBatteryData 비정상 응답 | 충전기: 버퍼 유지 + 백오프 재전송 (counter 연속성 유지) |
| 네트워크 단절 | 충전기: 로컬 버퍼 유지 → 재연결 후 순서대로 전송 |
| 키/인증서 설치 실패 | CSMS: 자동 재시도, 3회 연속 실패 시 운영자 통보 |
| CSMS → 환경부 전송 실패 | CSMS: 60초 후 재전송, 동일 실패 코드 3회면 중단 + 운영자 통보 (충전기 관여 없음) |
6. 검증 시나리오 체크리스트
- ☐인증서 설치:
RequestBatteryCertificate→InstallBatteryCertificate수신·저장·Accepted응답 - ☐세션키 설치:
RequestBatteryEncKey→InstallBatteryEncKey수신, 서명 검증 후Accepted - ☐현장 인증(2-A):
SetChargePreference(80, wh/amount 포함) →SetPrivacyConsent(정책 2건) 순차Accepted - ☐한도 이관: 위 설정 후 StartTransaction → 한도 도달 시 CSMS 원격 정지 수신 확인
- ☐원격 시작(2-B): RemoteStart → StartTransaction → CSMS 발신 TargetSoC/Privacy 2건 수신·
Accepted응답 → 화면 전환 - ☐트리거-적재량:
BatteryReportMaxRecords=N에서 N건 적재 즉시 전송, 배열 길이 N 확인 - ☐트리거-시간: 적재량 미달 상태에서 1분 경과 시 전송
- ☐예외:
connectorId=0/ SoC 101 / 빈batteryDataSet/ 미지원 messageId(구 SK 계열 포함) → 각Rejected·UnknownMessageId확인 - ☐키 미보유:
ReportBatteryData→Rejected→ 키 요청·설치 후 버퍼 재전송 - ☐복원력: 전송 중 네트워크 단절 → 재연결 후
counter연속 전송 - ☐미동의: 정책 하나
consented=false로 전송 →Accepted이지만 CSMS 어드민에서 "미동의 제외" 확인
현재 환경부 연계서버 미연결 구간에서는 CSMS가 모의(Mock) 응답으로 키를 발급합니다. 실연계 테스트 일정과 정책 코드 목록은 CSMS 운영팀(axd@bionever.com)과 협의하십시오.