성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>SQL Server TLS 통신을 위해 사용되는 키 길이 확인 방법</h1> <p> 지난 글에서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - Ubuntu + Microsoft.Data.SqlClient + SQL Server 2008 R2 연결 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13364'>https://www.sysnet.pe.kr/2/0/13364</a> </pre> <br /> 해당 현상은 (SQL Server 2012, 2014 버전은 테스트가 필요하지만, 적어도) SQL Server 2016 (버전 13.x)에서는 발생하지 않는다고 했습니다. 달리 말하면, <a target='tab' href='https://www.sysnet.pe.kr/2/0/13370#keylen_2048'>해당 버전부터는 2048비트 길이의 키를 가진 인증서가 생성</a>돼 있다고 짐작할 수 있습니다.<br /> <br /> 정말 그런지 테스트해 봐야겠죠? ^^<br /> <br /> 마침 가지고 있던 테스트 PC 중에 SQL Server 2016 Express 버전이 설치된 것이 있길래 <a target='tab' href='https://gist.github.com/lnattrass/a4a91dbf439fc1719d69f7865c1b1791'>get_tds_cert.py</a>를 실행했더니,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > c:\temp> <span style='color: blue; font-weight: bold'>python get_tds_cert.py 192.168.100.50 1433</span> Traceback (most recent call last): File "get_tds_cert.py", line 86, in <module> s.connect(( hostname, port )) socket.timeout: timed out </pre> <br /> 1433 포트로 연결이 안 됩니다. 실제로 SQL Server 2016 Express 측에는 1433 포트로 LISTENING하고 있는 소켓이 없습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // SQL Server 2016 Experss 버전이 설치된 컴퓨터 C:\WINDOWS\system32> <span style='color: blue; font-weight: bold'>netstat -ano | findstr 1433</span> C:\WINDOWS\system32> </pre> <br /> 왜냐하면 Express 버전의 경우 동적 포트를 사용하게 되는데, 이 번호를 알려면 "<a target='tab' href='https://www.sysnet.pe.kr/2/0/13271'>Sql Server Configuration Manager</a>"를 실행해 다음과 같이 "SQL Server Network Configuration" 노드의 "Protocols for SQLEXPRESS" 속성 창을 통해 구해야 합니다.<br /> <br /> <img alt='sql_server_2016_cert_1.png' src='/SysWebRes/bbs/sql_server_2016_cert_1.png' /><br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\WINDOWS\system32> <span style='color: blue; font-weight: bold'>netstat -ano | findstr 47000</span> TCP <span style='color: blue; font-weight: bold'>0.0.0.0:47000</span> 0.0.0.0:0 LISTENING 11588 TCP <span style='color: blue; font-weight: bold'>[::]:47000</span> [::]:0 LISTENING 11588 </pre> <br /> 그럼, 이렇게 인증서를 조회할 수 있고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > c:\temp> <span style='color: blue; font-weight: bold'>python get_tds_cert.py 192.168.100.50 47000</span> # get_tdspacket: 0, tdspacket len: 43 # Header: {'type': 4, 'status': 1, 'length': 43, 'channel': 0, 'packet': 1, 'window': 0} # Remaining tdspbuf length: 0 # Starting TLS handshake loop.. # Shaking (0/5) # get_tdspacket: 0, tdspacket len: 1249 # Header: {'type': 18, 'status': 1, 'length': 1249, 'channel': 0, 'packet': 0, 'window': 0} # Remaining tdspbuf length: 0 # Shaking (1/5) # get_tdspacket: 0, tdspacket len: 59 # Header: {'type': 18, 'status': 1, 'length': 59, 'channel': 0, 'packet': 0, 'window': 0} # Remaining tdspbuf length: 0 # Handshake completed, dumping certificates -----BEGIN CERTIFICATE----- MIIDADCCAeigAwIBAgIQH5dNyvwxI4ZI58THgmrvnjANBgkqhkiG9w0BAQUFADA7 MTkwNwYDVQQDHjAAUwBTAEwAXwBTAGUAbABmAF8AUwBpAGcAbgBlAGQAXwBGAGEA ...[생략]... 1ZLqpBiCaT6J9AG16QbYJMO0SMLTjHIuwgguSi+tUbydA734ugFtTJw0CHoR1K1x 2pqdlQ== -----END CERTIFICATE----- </pre> <br /> "BEGIN/END CERTIFICATE" 구간을 cer 파일로 저장해 윈도우 탐색기로 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > c:\temp> <span style='color: blue; font-weight: bold'>type 2016.cer</span> -----BEGIN CERTIFICATE----- MIIDADCCAeigAwIBAgIQH5dNyvwxI4ZI58THgmrvnjANBgkqhkiG9w0BAQUFADA7 MTkwNwYDVQQDHjAAUwBTAEwAXwBTAGUAbABmAF8AUwBpAGcAbgBlAGQAXwBGAGEA ...[생략]... 1ZLqpBiCaT6J9AG16QbYJMO0SMLTjHIuwgguSi+tUbydA734ugFtTJw0CHoR1K1x 2pqdlQ== -----END CERTIFICATE----- </pre> <br /> <img alt='sql_server_2016_cert_2.png' src='/SysWebRes/bbs/sql_server_2016_cert_2.png' /><br /> <br /> 2048비트 길이의 키가 사용된 것을 확인할 수 있습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 참고로, <a target='tab' href='https://gist.github.com/lnattrass/a4a91dbf439fc1719d69f7865c1b1791'>get_tds_cert.py</a>의 소스코드는 TDS(Tabular data stream) Protocol 중 인증서를 가져오는 부분까지만 구현하고 있기 때문에 원한다면 C# 소스코드로 쉽게(?) 포팅할 수 있을 것입니다. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > import sys import pprint import struct import socket import ssl from time import sleep # Standard "HELLO" message for TDS prelogin_msg = bytearray([ 0x12, 0x01, 0x00, 0x2f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x06, 0x01, 0x00, 0x20, 0x00, 0x01, 0x02, 0x00, 0x21, 0x00, 0x01, 0x03, 0x00, 0x22, 0x00, 0x04, 0x04, 0x00, 0x26, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]) # Prep Header function def prep_header(data): data_len = len(data) prelogin_head = bytearray([ 0x12, 0x01 ]) header_len = 8 total_len = header_len + data_len data_head = prelogin_head + total_len.to_bytes(2, 'big') data_head += bytearray([ 0x00, 0x00, 0x01, 0x00]) return data_head + data def read_header(data): if len(data) != 8: raise ValueError("prelogin header is > 8-bytes", data) format = ">bbhhbb" sct = struct.Struct(format) unpacked = sct.unpack(data) return { "type": unpacked[0], "status": unpacked[1], "length": unpacked[2], "channel": unpacked[3], "packet": unpacked[4], "window": unpacked[5] } tdspbuf = bytearray() def recv_tdspacket(sock): global tdspbuf tdspacket = tdspbuf header = {} for i in range(0,5): tdspacket += sock.recv(4096) print("\n# get_tdspacket: {}, tdspacket len: {} ".format(i, len(tdspacket))) if len(tdspacket) >= 8: header = read_header(tdspacket[:8]) print("# Header: ", header) if len(tdspacket) >= header['length']: tdspbuf = tdspacket[header['length']:] print("# Remaining tdspbuf length: {}\n".format(len(tdspbuf))) return header, tdspacket[8:header['length']] sleep(0.05) # Ensure we have a commandline if len(sys.argv) != 3: print("Usage: {} <hostname> <port>".format(sys.argv[0])) sys.exit(1) hostname = sys.argv[1] port = int(sys.argv[2]) # Setup SSL if hasattr(ssl, 'PROTOCOL_TLS'): sslProto = ssl.PROTOCOL_TLS else: sslProto = ssl.PROTOCOL_SSLv23 sslctx = ssl.SSLContext(sslProto) sslctx.check_hostname = False tls_in_buf = ssl.MemoryBIO() tls_out_buf = ssl.MemoryBIO() # Create the SSLObj connected to the tls_in_buf and tls_out_buf tlssock = sslctx.wrap_bio(tls_in_buf, tls_out_buf) # create an INET, STREAMing socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setblocking(0) s.settimeout(1) # Connect to the SQL Server s.connect(( hostname, port )) # Send the first TDS PRELOGIN message s.send(prelogin_msg) # Get the response and ignore. We will try to negotiate encryption anyway. header, data = recv_tdspacket(s) while header['status']==0: header, ext_data = recv_tdspacket(s) data += ext_data print("# Starting TLS handshake loop..") # Craft the packet for i in range(0,5): try: tlssock.do_handshake() print("# Handshake completed, dumping certificates") peercert = ssl.DER_cert_to_PEM_cert(tlssock.getpeercert(True)) print(peercert) sys.exit(0) except ssl.SSLWantReadError as err: # TLS wants to keep shaking hands, but because we're controlling the R/W buffers it throws an exception print("# Shaking ({}/5)".format(i)) tls_data = tls_out_buf.read() s.sendall(prep_header(tls_data)) # TDS Packets can be split over two frames, each with their own headers. # We have to concat these for TLS to handle nego properly header, data = recv_tdspacket(s) while header['status']==0: header, ext_data = recv_tdspacket(s) data += ext_data tls_in_buf.write(data) print("# Handshake did not complete / exiting") </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1043
(왼쪽의 숫자를 입력해야 합니다.)